Summary
npm audit reports 6 vulnerabilities in the production dependency tree (4 high, 1 moderate, 1 low) and 18 overall. Every one has a fix available. CI runs npm ci, typecheck and test with no dependency scanning, secret scanning, or SAST, so none of this is visible on a pull request.
Severity: Medium — two of the four high findings bear directly on NAP's own security properties.
Production dependencies
$ npm audit --omit=dev
fastify high Request/Response spoofing + schema validation bypass
fast-uri high Host confusion (7 CVEs), SSRF via IPv6 normalization
find-my-way high HTTP/2 DDoS
path-to-regexp high ReDoS via multiple route parameters
qs moderate DoS via attacker-controlled isBuffer
body-parser low Size enforcement silently disabled on invalid limit
Two of these are not generic transitive noise — they undercut controls this repo implements deliberately:
fastify: "request.protocol and request.host spoofable via X-Forwarded-Proto/Host from untrusted connections." createRequestDerivedBaseUrlResolver() in nap-adapter-fastify resolves the NIP-98 audience with allow(req.headers.host, req.protocol). The audience allowlist is the control that makes a spoofable Host safe, and audience.ts is explicit that a scheme-pinned entry is "the only way to stop an X-Forwarded-Proto a misconfigured trust proxy believes from downgrading the audience to http." A framework-level spoofing bug is the layer underneath that defence.
body-parser: "size enforcement silently disabled when the limit value is invalid." createNapExpressJsonParser() passes bodyLimit straight to express.json({ limit }), and its docstring explains the 1 kB cap exists because "100 kB on an unauthenticated endpoint is 100 kB of parsing an anonymous caller can buy per request." A malformed limit string silently removing that cap defeats the control without any visible symptom.
fast-uri: SSRF via malformed IPv6 normalization + host confusion. Worth calling out given nap-voucher makes outbound calls to mint URLs and treats SSRF as the top-severity concern in allowlist.ts and mintClient.ts. The mint allowlist uses WHATWG URL, not fast-uri, so this is not currently exploitable through that path — but it is the same vulnerability class the extension is built to prevent.
Dev dependencies
vitest is critical (arbitrary file read and execution when the UI server is listening; path traversal via @vitest/mocker), and vite/esbuild are high. These do not ship to consumers, but they run on every CI job and every contributor's machine, which is the SolarWinds-shaped risk: a build-system compromise does not need to reach production to matter.
The vitest and testcontainers fixes are semver-major (vitest@2 → 4, testcontainers@10 → 12), so those need a deliberate upgrade rather than npm audit fix.
Fix
Two parts, both small:
1. Update the dependencies.
npm audit fix # clears the 6 production findings
npm i -D vitest@^4 testcontainers@^12 # major, needs a test run
2. Add scanning to CI, so this does not silently regrow. Append to .github/workflows/ci.yml:
audit:
name: Dependency audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22.x', cache: npm }
- run: npm ci
# Production tree fails the build; the dev tree is reported but does not
# block, so a vitest advisory cannot wedge every PR on an unrelated change.
- name: Audit production dependencies
run: npm audit --omit=dev --audit-level=high
- name: Audit all dependencies (advisory)
run: npm audit --audit-level=moderate || true
Splitting production from dev is the part that keeps this tuned rather than noisy — the AppSec rule of thumb is that a scanner developers learn to ignore has negative value. The production gate is small, actionable, and should always be green.
Worth adding alongside:
Note
Nothing here is a vulnerability in NAP's own code. The protocol implementation is careful and well-reasoned. This is about the 80% of an application that is third-party code and the pipeline that keeps it current.
Summary
npm auditreports 6 vulnerabilities in the production dependency tree (4 high, 1 moderate, 1 low) and 18 overall. Every one has a fix available. CI runsnpm ci,typecheckandtestwith no dependency scanning, secret scanning, or SAST, so none of this is visible on a pull request.Severity: Medium — two of the four high findings bear directly on NAP's own security properties.
Production dependencies
Two of these are not generic transitive noise — they undercut controls this repo implements deliberately:
fastify: "request.protocol and request.host spoofable via X-Forwarded-Proto/Host from untrusted connections."createRequestDerivedBaseUrlResolver()innap-adapter-fastifyresolves the NIP-98 audience withallow(req.headers.host, req.protocol). The audience allowlist is the control that makes a spoofableHostsafe, andaudience.tsis explicit that a scheme-pinned entry is "the only way to stop anX-Forwarded-Protoa misconfiguredtrust proxybelieves from downgrading the audience tohttp." A framework-level spoofing bug is the layer underneath that defence.body-parser: "size enforcement silently disabled when the limit value is invalid."createNapExpressJsonParser()passesbodyLimitstraight toexpress.json({ limit }), and its docstring explains the 1 kB cap exists because "100 kB on an unauthenticated endpoint is 100 kB of parsing an anonymous caller can buy per request." A malformed limit string silently removing that cap defeats the control without any visible symptom.fast-uri: SSRF via malformed IPv6 normalization + host confusion. Worth calling out givennap-vouchermakes outbound calls to mint URLs and treats SSRF as the top-severity concern inallowlist.tsandmintClient.ts. The mint allowlist uses WHATWGURL, notfast-uri, so this is not currently exploitable through that path — but it is the same vulnerability class the extension is built to prevent.Dev dependencies
vitestis critical (arbitrary file read and execution when the UI server is listening; path traversal via@vitest/mocker), andvite/esbuildare high. These do not ship to consumers, but they run on every CI job and every contributor's machine, which is the SolarWinds-shaped risk: a build-system compromise does not need to reach production to matter.The
vitestandtestcontainersfixes are semver-major (vitest@2 → 4,testcontainers@10 → 12), so those need a deliberate upgrade rather thannpm audit fix.Fix
Two parts, both small:
1. Update the dependencies.
2. Add scanning to CI, so this does not silently regrow. Append to
.github/workflows/ci.yml:Splitting production from dev is the part that keeps this tuned rather than noisy — the AppSec rule of thumb is that a scanner developers learn to ignore has negative value. The production gate is small, actionable, and should always be green.
Worth adding alongside:
.github/dependabot.yml, weekly, npm) so upgrades arrive as reviewable PRs rather than as audit debt.utag turns /auth/complete into a 500 (unauthenticated) #33 and writeNapCookieSuccess() defaults to a session cookie with no HttpOnly, Secure or SameSite #34, but it is close to free and covers the classes nobody is reading for.Note
Nothing here is a vulnerability in NAP's own code. The protocol implementation is careful and well-reasoned. This is about the 80% of an application that is third-party code and the pipeline that keeps it current.