Skip to content

Nuxt Adapter improvements#40

Merged
muneebs merged 8 commits intomainfrom
nuxt_improvements
Mar 12, 2026
Merged

Nuxt Adapter improvements#40
muneebs merged 8 commits intomainfrom
nuxt_improvements

Conversation

@muneebs
Copy link
Owner

@muneebs muneebs commented Mar 11, 2026

feat(nuxt): polish @csrf-armor/nuxt — zero dependencies, docs, and security fixes

Summary

This PR makes some improvements to the @csrf-armor/nuxt module (merged via #39, Thank you @Jorgagu). It covers three areas: transitive dependency vulnerability fixes, a full refactor to eliminate runtime dependencies, and documentation.

1. Fix transitive dependency vulnerabilities (package.json, pnpm-lock.yaml)

All direct dependencies were already at their latest versions, so vulnerabilities were fixed by adding pnpm.overrides to force patched versions of affected transitive packages:

Severity Package Issue
Critical simple-git RCE via protocol bypass
High rollup Arbitrary file write
High serialize-javascript RCE
High svgo DoS
High tar Path traversal
High minimatch ReDoS (all active major ranges)
Low devalue CPU/memory amplification
Low qs DoS

2. Remove h3 and defu runtime dependencies (packages/nuxt/**)

The library's core principle is zero runtime dependencies beyond @csrf-armor/core. The initial Nuxt module violated this by depending on h3 helper functions and defu at runtime.

Changes:

  • adapter.ts — Replaced all h3 utility calls (getCookie, setCookie, getHeader, readBody, etc.) with native H3Event Web API properties (event.method, event.headers, event.path, event.node.req/res) and inline helpers for cookie parsing/serialization and body reading. import type { H3Event } from 'h3' is type-only and erased at compile time.
  • middleware.tsdefineEventHandler and useRuntimeConfig are now resolved via Nuxt's #imports virtual module (build-time, zero runtime cost). createError replaced with an inline Object.assign(new Error(...), { statusCode }). This also fixes a deprecation warning from h3 about implicit event handler conversion.
  • module.tsdefu replaced with a 20-line inline mergeDefaults deep-merge utility. h3 and defu removed from peerDependencies.
  • Tests — Rewritten to remove all vi.mock('h3', ...) mocking. Mock events now use real node:stream Readable instances and the Web Headers API, matching the adapter's actual expectations.

3. Add README (packages/nuxt/README.md)

New README consistent with the @csrf-armor/nextjs package, covering installation, module registration, composable usage, all five security strategies, configuration reference, and security best practices.

4. Add changeset (.changeset/add-nuxt-module.md)

minor bump for @csrf-armor/nuxt covering the initial module addition and the zero-dependency refactor.

Test plan

  • pnpm --filter @csrf-armor/nuxt test — all adapter and middleware integration tests pass
  • pnpm --filter @csrf-armor/nuxt type-check — no TypeScript errors
  • pnpm build — full monorepo build succeeds
  • pnpm audit — no unresolved vulnerabilities at or above moderate severity
  • Verify @csrf-armor/nuxt package dependencies contains only @csrf-armor/core

Summary by CodeRabbit

  • New Features

    • Added @csrf-armor/nuxt module with SSR-safe composables (token management and fetch wrapper) and server middleware for CSRF protection supporting all core strategies.
  • Documentation

    • Added comprehensive Nuxt module README with installation, configuration, examples, and best practices.
  • Refactor

    • Internal adapter and middleware reworked to use native request/response handling and simplified error signaling (no public API changes).
  • Tests

    • Substantial test rewrite to use in-memory/mock event infrastructure for more realistic coverage.
  • Chores

    • Updated package overrides and removed two dev/peer metadata entries to streamline dependencies.

muneebs added 5 commits March 11, 2026 13:51
…rrides

Add pnpm overrides to pin vulnerable transitive dependencies pulled in
by [email protected] and [email protected] (both already at their latest versions):

- simple-git >=3.32.3 (critical: RCE via protocol bypass)
- rollup >=4.59.0 (high: arbitrary file write)
- serialize-javascript >=7.0.3 (high: RCE)
- svgo >=4.0.1 (high: DoS)
- tar >=7.5.11 (high: path traversal)
- minimatch@5/9/10 (high: ReDoS, multiple ranges)
- devalue >=5.6.3 (low: CPU/memory amplification)
- qs >=6.14.2 (low: DoS, tighten existing override)
Upholds the library's zero-dependencies principle by eliminating all
runtime imports from h3 and defu in the nuxt package.

defu:
- Replace two defu merge calls with an inline mergeDefaults<T> utility

h3 (adapter.ts):
- Use event.method, event.headers, event.path natively (H3Event Web API)
- Inline cookie parsing (parseCookieHeader) and serialization (serializeCookie)
- Read body via event.node.req stream instead of readBody()
- Set response headers/cookies via event.node.res instead of h3 helpers
- Extract parseBody() as a standalone function for clarity
- Pass event.headers directly to CsrfRequest (no Map conversion needed)
- Read request.cookies from the pre-parsed Map instead of re-parsing
  the cookie header inside getTokenFromRequest

h3 (middleware.ts):
- Import defineEventHandler from #imports (Nuxt auto-import) instead of h3
- Replace createError with an inline Error + statusCode assignment

All imports from h3 are now type-only (import type { H3Event }),
erased at compile time with zero runtime cost. h3 removed from
peerDependencies since it is always provided transitively by nuxt.

Tests rewritten to mock H3Event properties directly using real
node:stream Readable instances instead of mocking h3 module functions.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

Walkthrough

A new Nuxt module for CSRF protection is added. It introduces a NuxtAdapter that uses native H3Event/Node APIs for request/response handling, server middleware enforcing CSRF, client composables (useCsrfToken, useCsrfFetch) and a client plugin, plus docs and extensive test rewrites using in-memory mocks.

Changes

Cohort / File(s) Summary
Module manifest & overrides
package.json, .changeset/add-nuxt-module.md, packages/nuxt/package.json
pnpm overrides added and qs bumped; defu removed from devDependencies and h3 removed from peerDependencies for the Nuxt package.
Nuxt module runtime
packages/nuxt/src/module.ts
Replaced defu merge with an internal mergeDefaults deep-merge helper to combine module options and runtimeConfig (including public CSRF config).
Server adapter (native APIs)
packages/nuxt/src/runtime/server/adapter.ts
Reworked to use native H3Event/Node properties: added cookie parsing/serializing, appendSetCookie, parseBody, rebuild URL/headers, and updated extractRequest/applyResponse/getTokenFromRequest to use these helpers.
Server middleware
packages/nuxt/src/runtime/server/middleware.ts
Replaced h3 createError usage with thrown Error objects augmented with statusCode, statusMessage, and data.
Client composables & runtime
packages/nuxt/src/runtime/composables/useCsrfToken.ts
Minor refactor: defaulting uses nullish coalescing assignment (resolvedConfig ??= { ... }) and import reordering; no API changes.
Documentation
packages/nuxt/README.md
New comprehensive README detailing installation, config, composables, strategies, examples, and best practices.
Tests (adapter & middleware)
packages/nuxt/test/adapter.test.ts, packages/nuxt/test/middleware.test.ts
Large test rewrite: replaced h3 mocks with in-test in-memory mock server/event/response structures; expanded coverage for body parsing, cookie serialization, token sources, and concurrency/isolation.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Browser as Browser (Client)
participant NuxtPlugin as Nuxt Client Plugin
participant Server as Nuxt Server Middleware
participant NuxtAdapter as NuxtAdapter
participant Core as @csrf-armor/core
Browser->>NuxtPlugin: load/init -> fetch or read token cookie
NuxtPlugin->>Browser: set runtime token state
Browser->>Server: mutating request (includes token header/body)
Server->>NuxtAdapter: extractRequest(event)
NuxtAdapter->>NuxtAdapter: parse cookies, headers, body
NuxtAdapter->>Core: validate token according to configured strategy
Core-->>NuxtAdapter: validation result
NuxtAdapter->>Server: applyResponse (set cookie/header if needed)
Server->>Browser: response (with Set-Cookie or header)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

"Hearken, traveler of code and night,
A Nuxt shield forged by careful light;
Tokens guarded, cookies sung,
Native paths anew are strung.
Walk safe through forms and mutating tide,
For CSRF's blight is cast aside."

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Nuxt Adapter improvements' is vague and generic, using non-descriptive language that fails to convey meaningful information about the substantial changes in this PR. Consider a more specific title that highlights the main accomplishment, such as 'Remove h3 and defu runtime dependencies from Nuxt adapter' or 'Zero runtime dependencies for @csrf-armor/nuxt module'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 88.24% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nuxt_improvements

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 and usage tips.

@muneebs muneebs changed the title Nuxt improvements Nuxt Adapter improvements Mar 11, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/nuxt/src/runtime/server/middleware.ts (1)

34-38: ⚠️ Potential issue | 🔴 Critical

The statusCode property shall not be honored upon this plain Error!

Alas, I must bring grave tidings. The H3 framework recognizes the statusCode property only when thrown as an H3Error created via createError. Thy approach of adorning a plain Error with this property shall avail thee naught—H3 shall treat it as an unhandled error and return a 500 status to the client, not the intended 403 Forbidden.

Use createError({ statusCode: 403, statusMessage: 'CSRF validation failed', data: { reason: result.reason } }) instead, for that is the magic that H3 understands. Without it, thy CSRF protection shall fail silently to the user's eye.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nuxt/src/runtime/server/middleware.ts` around lines 34 - 38, Replace
the plain Error thrown in middleware.ts (the throw using Object.assign(new
Error('CSRF validation failed'), { statusCode: 403, statusMessage: 'CSRF
validation failed', data: { reason: result.reason } })) with an H3 error created
via createError so H3 will honor the status; call createError({ statusCode: 403,
statusMessage: 'CSRF validation failed', data: { reason: result.reason } }) in
place of the current throw, ensuring you import createError from h3 if not
already present and keep the same message and data payload.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/nuxt/README.md`:
- Around line 15-20: The TOC links for the security sections are invalid because
they include a leading hyphen from the emoji; update the links referenced as
'#-security-strategies' and '#-security-best-practices' to match the actual
GitHub anchors (e.g., '#security-strategies' and '#security-best-practices') or
add explicit HTML anchors next to the headings (e.g., an <a
name="security-strategies"> or <a id="security-strategies">) so the links from
the README TOC resolve correctly for the headings "🛡️ Security Strategies" and
"🔒 Security Best Practices".

In `@packages/nuxt/src/runtime/server/adapter.ts`:
- Around line 63-93: The code currently treats 'multipart/form-data' as
supported in the supportedTypes array but then returns the rawBody string for
non-JSON content, which is unsafe for multipart bodies; either remove
'multipart/form-data' from the supportedTypes array so multipart requests won't
be parsed by the function, or implement proper multipart parsing (using a
multipart parser library) before returning body data; update references in the
supportedTypes constant and the contentType checks (the supportedTypes array and
the code paths that use contentType.startsWith(...)) so multipart is no longer
accepted unless a full multipart parser is added.

In `@packages/nuxt/test/adapter.test.ts`:
- Around line 550-559: The non-null assertion on csrfResponses[i]! in the
Promise.all mapping for events can break static analysis; update the
adapter.applyResponse call to safely handle missing csrfResponses by removing
the '!' and explicitly guarding the index (e.g., compute const csrf =
csrfResponses[i]; if (!csrf) throw or pass a safe fallback) before calling
adapter.applyResponse, or better yet zip events and csrfResponses into pairs and
map over those pairs so adapter.applyResponse is always invoked with a defined
csrfResponse; reference adapter.applyResponse, events, csrfResponses and the
Promise.all mapping when making the change.
- Around line 500-509: The test uses a non-null assertion on cookies[0] which
triggers linter warnings; replace the assertion with a guarded access when
constructing cookieStr (e.g., const cookieStr = cookies[0] ?? ''; or const
cookieStr = cookies[0] ?? throw new Error('expected cookie')); and/or add an
explicit expect(cookies[0]).toBeDefined() before using it; update the reference
in the assertions that follow to use this guarded cookieStr
(functions/variables: getSetCookies, mockEvent.node.res, cookies, cookieStr) so
the linter no longer complains.

---

Outside diff comments:
In `@packages/nuxt/src/runtime/server/middleware.ts`:
- Around line 34-38: Replace the plain Error thrown in middleware.ts (the throw
using Object.assign(new Error('CSRF validation failed'), { statusCode: 403,
statusMessage: 'CSRF validation failed', data: { reason: result.reason } }))
with an H3 error created via createError so H3 will honor the status; call
createError({ statusCode: 403, statusMessage: 'CSRF validation failed', data: {
reason: result.reason } }) in place of the current throw, ensuring you import
createError from h3 if not already present and keep the same message and data
payload.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 874c8e08-caad-4566-98fa-2f782367e55c

📥 Commits

Reviewing files that changed from the base of the PR and between 097c573 and 9cc7fe5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • .changeset/add-nuxt-module.md
  • package.json
  • packages/nuxt/README.md
  • packages/nuxt/package.json
  • packages/nuxt/src/module.ts
  • packages/nuxt/src/runtime/composables/useCsrfToken.ts
  • packages/nuxt/src/runtime/server/adapter.ts
  • packages/nuxt/src/runtime/server/middleware.ts
  • packages/nuxt/test/adapter.test.ts
  • packages/nuxt/test/middleware.test.ts
💤 Files with no reviewable changes (1)
  • packages/nuxt/package.json

muneebs added 3 commits March 11, 2026 16:07
Multipart bodies cannot be correctly parsed by the URLSearchParams fallback,
so the CSRF token could never be extracted from them anyway. Multipart requests
should include the token via header or cookie instead.
- Remove multipart/form-data from body parser (cannot be correctly parsed)
- Replace non-null assertions with ?? '' and biome-ignore comments
- Use literal key access for headerInit.cookie
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/nuxt/test/adapter.test.ts`:
- Around line 134-136: The test falsely casts result.headers to
Map<string,string>; change the type assertion to the Web Headers type used by
the adapter (e.g., const headers = result.headers as Headers) and update the
variable name (headers or headersObj) and assertions to use that Headers
instance (headers.get('content-type'), headers.get('x-csrf-token')) so the test
type accurately reflects the adapter's return value (refer to result.headers and
headersMap in the test).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c43a14f1-0001-436a-af6e-c6b60fd4fd2c

📥 Commits

Reviewing files that changed from the base of the PR and between 9cc7fe5 and 2011aba.

📒 Files selected for processing (2)
  • packages/nuxt/src/runtime/server/adapter.ts
  • packages/nuxt/test/adapter.test.ts

@muneebs muneebs merged commit 4fdec35 into main Mar 12, 2026
5 checks passed
@muneebs muneebs deleted the nuxt_improvements branch March 12, 2026 14:05
@muneebs muneebs restored the nuxt_improvements branch March 12, 2026 19:08
@muneebs muneebs deleted the nuxt_improvements branch March 12, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants