Skip to content

Accepted: 10 high npm advisories with no upstream patch (build-time only) — do NOT run npm audit fix --force #123

Description

@twistedmelonman

Status: accepted risk, enforced by CI. No action is available or required.
Last verified 2026-08-29. The authoritative record is
docs/SECURITY-AUDIT.md; this issue is
the discussion thread for it.

Summary

npm audit reports 10 high advisories on main, from two root packages
carrying three CVEs between them. Both roots are build-time-only
dependencies of the Netlify toolchain, and neither has a patched release in
existence
— so there is nothing to upgrade to and nothing to fix.

The remaining eight audit entries are not distinct problems. They are chain
packages flagged solely for depending on a root.

Root Advisory Installed Vulnerable range First patched
image-size GHSA-w3rx-r6r6-pgpr — ICNS parser DoS 2.0.2 <=2.0.2 none exists
image-size GHSA-5p2g-fcmc-qvqq — JXL/HEIF parser DoS 2.0.2 <=2.0.2 none exists
extract-zip GHSA-jmr9-qjv8-65gv — symlink path traversal 2.0.1 <=2.0.1 none exists

first_patched_version: null on all three, confirmed against the GitHub
Advisory API. npm view confirms 2.0.2 and 2.0.1 are the newest published
versions of each package. Upstream fix tracked at
netlify/framework-adapters#47.

Chain-only entries: @astrojs/netlify, @netlify/blobs, @netlify/dev,
@netlify/dev-utils, @netlify/edge-functions-dev, @netlify/functions-dev,
@netlify/redirects, @netlify/vite-plugin.

⚠️ Do NOT run npm audit fix --force

npm proposes a downgrade of @astrojs/netlify from the installed 8.1.2 to
6.4.1, flagged isSemVerMajor: true, for 9 of the 10 findings:

@astrojs/netlify -> {"name":"@astrojs/netlify","version":"6.4.1","isSemVerMajor":true}

That would roll back #106's major bumps and reintroduce the Astro 5
content-collection API — re-breaking the site in exactly the way #120 fixed. It
also would not clear the advisories, because the roots are unpatched at every
version.

This trap is guarded in two places: scripts/check-audit-baseline.sh keys on
GHSA IDs rather than npm's fixAvailable (which recommends the downgrade), and
.github/dependabot.yml ignores @astrojs/netlify majors.

Why this is accepted

Both roots run at build time only. Neither is bundled into any deployed
function.

@astrojs/netlify@8.1.2 → @netlify/vite-plugin@2.12.9 → @netlify/dev-utils@4.4.7 → image-size@2.0.2
@astrojs/netlify@8.1.2 → @netlify/vite-plugin@2.12.9 → @netlify/dev@4.18.12 → @netlify/functions-dev@1.3.4 → extract-zip@2.0.1

image-size processes only images committed to this repo. extract-zip is used
by local dev/functions tooling, not by anything in production. A DoS advisory
against a build-time parser fed exclusively repo-controlled input is not a
meaningful risk here; the realistic worst case is a local build hanging on a
malformed image the author added themselves.

Two caveats that are not part of the rationale, recorded because earlier
versions of this issue wrongly relied on them:

  • The site is not free of user input. netlify/functions/instagram-webhook.mts:88
    fetches an attacker-supplied post.imageUrl and stores the bytes. It is
    bearer-token gated (:44) and never parses the image, so it reaches no
    parser — but "no user uploads" is false.
  • The site is not fully static. src/pages/blog/index.astro:7 sets
    export const prerender = false, and the built manifest carries six
    "prerender":false routes deployed at path: /*. SSR runs at request time.

Known gap: Astro vendors its own image-size

node_modules/astro/dist/assets/utils/vendor/image-size/ is a private copy that
ships in the deployed SSR bundle. It is invisible to npm audit, not
covered by the overrides block, and not seen by check-audit-baseline.sh. Its
ICNS parser is logically identical to vulnerable upstream 2.0.2 — same missing
zero-length guard.

Not exploitable, failing closed twice:

  1. astro.config.mjs declares no image: block, so domains and
    remotePatterns are empty and isRemoteAllowed returns false for every
    remote URL — 403 before any fetch.
  2. The configured service is @astrojs/netlify/image-service.js, whose exported
    service object has no transform method. The /_image endpoint's
    first check is if (!("transform" in imageService)) throw — 500 before a
    parser runs.

Both guards are configuration, not code. Adding a single image.domains
entry, or switching to a local/sharp image service, removes them and makes the
vendored parser reachable. Re-read this section before changing either.

What would change this

  • Either root publishes a patched release. check-audit-baseline.sh reports
    this without failing, which is the cue to retire its baseline line.
  • @netlify/dev moves off them. @netlify/dev-utils@6.0.1 already dropped
    image-size, but forcing that chain via overrides pushes
    @netlify/vite-plugin onto an untested @netlify/dev major — the same shape
    as the failure behind fix: restore content collections under Astro 7 glob loader #120, so it was deliberately not shipped.
  • A new advisory appears outside the accepted set. CI fails on that
    automatically; triage it rather than assuming this acceptance covers it.

@astrojs/netlify 8.1.2 → 8.2.4 is available and compatible (peer
astro: ^7.0.0 against the installed 7.1.6) but clears zero advisories — it
declares the same @netlify/vite-plugin: ^2.12.3, already resolving to 2.12.9.
Hygiene only, not a fix.

History

Related: #106, #120, #127, #144

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    securitySecurity-related concern

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions