@daloyjs/core 0.29.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -380,9 +380,11 @@ What works today, at a glance:
380
380
 
381
381
  - Contract-first routing, Standard Schema validation (Zod 4 / Valibot / ArkType / TypeBox), and OpenAPI 3.1 from a single source of truth.
382
382
  - Adapters for Node (Heroku/Railway/Render/Fly.io), Bun, Deno, Cloudflare Workers, Vercel Node / Edge / Next.js / Netlify Edge, Fastly Compute, and AWS Lambda / Netlify Functions / Lambda Function URLs.
383
- - Built-in security primitives (body limits, prototype-pollution-safe JSON, path-traversal guard, request timeouts, header injection guards, **duplicate `Host` / `Content-Length` rejection**, **stripped `Server` / `X-Powered-By` headers by default**, **structured-log redaction defaults** for authorization / cookie / password / token / JWT-shaped values, **`secureHeaders()` auto-applied since `0.16.0`** with user-installed overrides automatically replacing the auto instance, **cross-origin state-changing requests rejected with `403` since `0.16.0`** unless a route's `cors()` policy allows the request origin, **refuse-to-boot on weak session secrets, `cors({ origin: "*" })`, `session()` + state-changing route without `csrf()`, and unconfigured `X-Forwarded-*` in production since `0.17.0`**, **connection-draining shutdown with `Connection: close` on `503` and in-flight responses, `crashOnUnhandledRejection` default-on in production, and `app.healthcheck()` / `app.readinesscheck()` primitives with bearer-token auth + per-IP rate limit since `0.18.0`**, **`rateLimit({ groupId })` shared buckets, `combine` primitives `every` / `some` / `except`, `ipRestriction()` with CIDR-aware IPv4/IPv6 allow/deny lists, and `internal: true` route flag + `app.inject()` since `0.19.0`**, **`loadShedding()` event-loop-pressure middleware (auto-`503` + `Retry-After`), `app.cspReportRoute()` rate-limited CSP violation receiver + `secureHeaders({ reportingEndpoints, reportTo })` wiring, `disconnectStatusCode: 499` default for client-aborted requests, and `defineConfig({ schema, source })` boot-time typed configuration validation since `0.20.0`**, **`createJwtSigner()` / `createJwtVerifier()` with `alg`-discipline (no `alg: "none"`, explicit allowlist, HS+JWK refused at construction) + `exp`-required sign refusal, `requireScopes()` with RFC-6750 `WWW-Authenticate: Bearer` challenge + per-request scope aggregation, and `etag()` helper with `Set-Cookie` / `Cache-Control: private | no-store | no-cache` auto-skip (cross-tenant fingerprinting defense) since `0.21.0`**, **`jwk()` asymmetric-only JWKS middleware (refuses `HS*` at construction, `kid` + JWT-vs-JWK `alg` cross-check, `https://` JWKS URL with TTL caching + in-flight-promise dedup, normalizes `scope` / `scp` / `scopes` claims), per-scheme `verify(credentials, ctx)` revalidation hook on `bearerAuth()` / `jwk()`, `basicAuth({ onAuthSuccess })` typed-context callback, and `Cache-Control: no-store` on every first-party auth helper 401 challenge since `0.22.0`**, **`wsRateLimit()` for WebSocket upgrades, `loginThrottle()` credential-entry preset, `rotateSession()` privilege-change session rotation, `fileField({ magicBytes })` upload signature checks, `requirePayloadAuth` security-scheme guard, and WebSocket safe defaults since `0.23.0`**, **`app({ behindProxy })` declarative model (replaces `trustProxy`), adapter-independent `ConnInfo` abstraction (`getConnInfo()` / lazy `ctx.remoteAddress` / `ctx.remotePort`), `daloy doctor` production-posture validator (with `--audit-secrets` + `--no-audit-defaults`), container-first `create-daloy` templates (`HEALTHCHECK` to `/readyz`, `STOPSIGNAL SIGTERM`, non-root user, `tini` PID 1), PSL-aware `subdomains()` helper with `≤ 90 days` snapshot guard, plugin `dependencies: string[]` refuse-to-boot, namespace-protected `decorate({ override })`, plugin extension ordering with `before` / `after` + cycle detection, `behindProxy.hops` collapses to the `(N+1)`-from-rightmost slot, `defineDependency()` typed-DI helper with per-request deduplication, scheme-aware `ctx.state.auth` typed contract, plugin lifecycle encapsulation default of `local`, and required `name` + optional `seed` for stateful plugins since `0.24.0`**, **`compression()` middleware (built on the web-standard `CompressionStream`, prefers `br` > `gzip` > `deflate` and probes runtime support once) with BREACH-aware always-on guards (skip `Set-Cookie` / `Authorization` / session-or-CSRF cookie / already-compressed content types), `minimumSize: 1024` + negative-compression-ratio post-check, no configurable `compressLevel` knob (CPU-DoS defense — `level: 9` is refused at construction), always-on `Vary: Accept-Encoding`, and strong → weak ETag downgrade per RFC 9110 §8.8.3 since `0.25.0`**, **`secureDefaults: false` refuse-to-construct in production (unless `acknowledgeInsecureDefaults: true`) + once-per-process `error` log naming every disabled default, `createJwtSigner()` / `createJwtVerifier()` refuse HS-shaped secrets `< 32` bytes (RFC 7518 §3.2), `secureHeaders()` refuses to construct with both `frameOptions: false` AND no CSP `frame-ancestors` directive (no clickjacking defense), and mandatory hardware-backed 2FA for every contributor with publish access (documented in `SECURITY.md`) since `0.26.0`**, and **single-source-of-truth cookie and temporal-claim helpers (`@daloyjs/core/cookie`, `@daloyjs/core/time-claims`), `session()` / `csrf()` `__Secure-` cookie refuse-to-boot, zero-runtime-dependency CI governance, and secret-comparison CI grep gate since `0.27.0`**, and **pattern-agnostic-framework parity audit suite since `0.28.0`** (`scripts/verify-wave9-audits.ts` static gates wired into CI as `pnpm verify:wave9-audits` — refuses public setters on `request.url` / `request.path` / `request.method`, refuses `ctx.respond = false`-style response-bypass switches, refuses `Referer`-based redirect targets, refuses `AES-CBC` / `SHA-1` / third-party crypto reach inside the cookie helper module, refuses adapter dispatch via `allowInternal: true` / `app.inject()`, and reaffirms the zero-runtime-dependency posture — plus `daloy doctor --audit-defaults` live-config checks that flag wildcard-credentials CORS, > 24 h CORS `maxAge`, > 25 MiB blanket body limits, zero `idleTimeoutMs` in production, `allowUnsafeValidationDetails` / `exposeFrameworkIdentity` / `enableServerTimingInProduction` opt-ins that the public type does not expose), and **zero-runtime-dependency batteries-included parity & governance audit suite since `0.29.0`** (`SECURITY-CONTACTS.md` rotation file with machine-readable ACTIVE block + `<!-- last-exercise: -->` marker, `scripts/verify-wave10-audits.ts` static gates wired into CI as `pnpm verify:wave10-audits` — refuses a missing/stale rotation file, refuses runtime deps on `@daloyjs/core/package.json`, refuses removal of the plugin-prerequisite refuse-to-boot path or the `topoSortExtensions` cycle-detection throw from `src/app.ts`, and reaffirms the governance floor: top-level `permissions:` on every workflow, `persist-credentials: false` on every `actions/checkout`, 40-hex SHA pinning on every third-party `uses:`, `step-security/harden-runner` on every workflow that uses third-party actions, and `.github/CODEOWNERS` on privileged files — plus a release-workflow contributor-rotation refusal step that exits non-zero when `github.actor` is not on the `SECURITY-CONTACTS` ACTIVE rotation) — all guards opt-out via `app({ secureDefaults: false })`) plus first-party middleware (`secureHeaders` with CSP nonce + Trusted Types, `cors`, `rateLimit`, `requestId`, `bearerAuth`, `basicAuth`, `csrf` with **double-submit cookie** + **Fetch-Metadata** strategies, `session`, `timing` / `timingSafeEqual`) and **zero-knob crypto helpers** (`passwordHash` / `passwordVerify` at `@daloyjs/core/hashing`, `verifyWebhookSignature` / `signWebhookPayload`).
383
+ - Built-in security primitives (body limits, prototype-pollution-safe JSON, path-traversal guard, request timeouts, header injection guards, **duplicate `Host` / `Content-Length` rejection**, **stripped `Server` / `X-Powered-By` headers by default**, **structured-log redaction defaults** for authorization / cookie / password / token / JWT-shaped values, **`secureHeaders()` auto-applied since `0.16.0`** with user-installed overrides automatically replacing the auto instance, **cross-origin state-changing requests rejected with `403` since `0.16.0`** unless a route's `cors()` policy allows the request origin, **refuse-to-boot on weak session secrets, `cors({ origin: "*" })`, `session()` + state-changing route without `csrf()`, and unconfigured `X-Forwarded-*` in production since `0.17.0`**, **connection-draining shutdown with `Connection: close` on `503` and in-flight responses, `crashOnUnhandledRejection` default-on in production, and `app.healthcheck()` / `app.readinesscheck()` primitives with bearer-token auth + per-IP rate limit since `0.18.0`**, **`rateLimit({ groupId })` shared buckets, `combine` primitives `every` / `some` / `except`, `ipRestriction()` with CIDR-aware IPv4/IPv6 allow/deny lists, and `internal: true` route flag + `app.inject()` since `0.19.0`**, **`loadShedding()` event-loop-pressure middleware (auto-`503` + `Retry-After`), `app.cspReportRoute()` rate-limited CSP violation receiver + `secureHeaders({ reportingEndpoints, reportTo })` wiring, `disconnectStatusCode: 499` default for client-aborted requests, and `defineConfig({ schema, source })` boot-time typed configuration validation since `0.20.0`**, **`createJwtSigner()` / `createJwtVerifier()` with `alg`-discipline (no `alg: "none"`, explicit allowlist, HS+JWK refused at construction) + `exp`-required sign refusal, `requireScopes()` with RFC-6750 `WWW-Authenticate: Bearer` challenge + per-request scope aggregation, and `etag()` helper with `Set-Cookie` / `Cache-Control: private | no-store | no-cache` auto-skip (cross-tenant fingerprinting defense) since `0.21.0`**, **`jwk()` asymmetric-only JWKS middleware (refuses `HS*` at construction, `kid` + JWT-vs-JWK `alg` cross-check, `https://` JWKS URL with TTL caching + in-flight-promise dedup, normalizes `scope` / `scp` / `scopes` claims), per-scheme `verify(credentials, ctx)` revalidation hook on `bearerAuth()` / `jwk()`, `basicAuth({ onAuthSuccess })` typed-context callback, and `Cache-Control: no-store` on every first-party auth helper 401 challenge since `0.22.0`**, **`wsRateLimit()` for WebSocket upgrades, `loginThrottle()` credential-entry preset, `rotateSession()` privilege-change session rotation, `fileField({ magicBytes })` upload signature checks, `requirePayloadAuth` security-scheme guard, and WebSocket safe defaults since `0.23.0`**, **`app({ behindProxy })` declarative model (replaces `trustProxy`), adapter-independent `ConnInfo` abstraction (`getConnInfo()` / lazy `ctx.remoteAddress` / `ctx.remotePort`), `daloy doctor` production-posture validator (with `--audit-secrets` + `--no-audit-defaults`), container-first `create-daloy` templates (`HEALTHCHECK` to `/readyz`, `STOPSIGNAL SIGTERM`, non-root user, `tini` PID 1), PSL-aware `subdomains()` helper with `≤ 90 days` snapshot guard, plugin `dependencies: string[]` refuse-to-boot, namespace-protected `decorate({ override })`, plugin extension ordering with `before` / `after` + cycle detection, `behindProxy.hops` collapses to the `(N+1)`-from-rightmost slot, `defineDependency()` typed-DI helper with per-request deduplication, scheme-aware `ctx.state.auth` typed contract, plugin lifecycle encapsulation default of `local`, and required `name` + optional `seed` for stateful plugins since `0.24.0`**, **`compression()` middleware (built on the web-standard `CompressionStream`, prefers `br` > `gzip` > `deflate` and probes runtime support once) with BREACH-aware always-on guards (skip `Set-Cookie` / `Authorization` / session-or-CSRF cookie / already-compressed content types), `minimumSize: 1024` + negative-compression-ratio post-check, no configurable `compressLevel` knob (CPU-DoS defense — `level: 9` is refused at construction), always-on `Vary: Accept-Encoding`, and strong → weak ETag downgrade per RFC 9110 §8.8.3 since `0.25.0`**, **`secureDefaults: false` refuse-to-construct in production (unless `acknowledgeInsecureDefaults: true`) + once-per-process `error` log naming every disabled default, `createJwtSigner()` / `createJwtVerifier()` refuse HS-shaped secrets `< 32` bytes (RFC 7518 §3.2), `secureHeaders()` refuses to construct with both `frameOptions: false` AND no CSP `frame-ancestors` directive (no clickjacking defense), and mandatory hardware-backed 2FA for every contributor with publish access (documented in `SECURITY.md`) since `0.26.0`**, and **single-source-of-truth cookie and temporal-claim helpers (`@daloyjs/core/cookie`, `@daloyjs/core/time-claims`), `session()` / `csrf()` `__Secure-` cookie refuse-to-boot, zero-runtime-dependency CI governance, and secret-comparison CI grep gate since `0.27.0`**, and **pattern-agnostic-framework parity audit suite since `0.28.0`** (`scripts/verify-wave9-audits.ts` static gates wired into CI as `pnpm verify:wave9-audits` — refuses public setters on `request.url` / `request.path` / `request.method`, refuses `ctx.respond = false`-style response-bypass switches, refuses `Referer`-based redirect targets, refuses `AES-CBC` / `SHA-1` / third-party crypto reach inside the cookie helper module, refuses adapter dispatch via `allowInternal: true` / `app.inject()`, and reaffirms the zero-runtime-dependency posture — plus `daloy doctor --audit-defaults` live-config checks that flag wildcard-credentials CORS, > 24 h CORS `maxAge`, > 25 MiB blanket body limits, zero `idleTimeoutMs` in production, `allowUnsafeValidationDetails` / `exposeFrameworkIdentity` / `enableServerTimingInProduction` opt-ins that the public type does not expose), and **zero-runtime-dependency batteries-included parity & governance audit suite since `0.29.0`** (`SECURITY-CONTACTS.md` rotation file with machine-readable ACTIVE block + `<!-- last-exercise: -->` marker, `scripts/verify-wave10-audits.ts` static gates wired into CI as `pnpm verify:wave10-audits` — refuses a missing/stale rotation file, refuses runtime deps on `@daloyjs/core/package.json`, refuses removal of the plugin-prerequisite refuse-to-boot path or the `topoSortExtensions` cycle-detection throw from `src/app.ts`, and reaffirms the governance floor: top-level `permissions:` on every workflow, `persist-credentials: false` on every `actions/checkout`, 40-hex SHA pinning on every third-party `uses:`, `step-security/harden-runner` on every workflow that uses third-party actions, and `.github/CODEOWNERS` on privileged files — plus a release-workflow contributor-rotation refusal step that exits non-zero when `github.actor` is not on the `SECURITY-CONTACTS` ACTIVE rotation), and **multi-runtime web-standard ergonomic-framework parity bake-ins since `0.30.0`** (`Cache-Control: no-store` baked into `UnauthorizedError` / `ForbiddenError` / `TooManyRequestsError` so every first-party auth helper 401 / 403 / 429 response is uncacheable, `cspReportRoute()` refuses `application/json` with `415` and refuses `maxBodyBytes > 64 KiB` at construction with the default production logger sink omitting the parsed report body unless `logCspReportBodies: true` is set explicitly (PII defense — CSP reports include the violated source URL), `cors()` `allowMethods` default narrowed to `[GET, HEAD, POST]` with `methods: ['*']` refused at construction (`PUT` / `PATCH` / `DELETE` are now explicit opt-ins), and `scripts/verify-wave11-audits.ts` wired into CI as `pnpm verify:wave11-audits` covering all of the above plus the reverse-proxy-helper absence audit and Wave 7 compression skip-already-encoded reaffirm), and **mature-Node ergonomic-framework second-pass bake-ins since `0.31.0`** (`useSemicolonDelimiter: false` router-level audit so `/users/42;admin=true` cannot smuggle attacker-controlled query data past auth / CSRF / rate-limit middleware via a reverse-proxy / origin disagreement on RFC 3986 path-segment delimiters, `allowErrorHandlerOverride: false` audit so the framework never ships a standalone `setErrorHandler()` / `onError()` class method that could silently overwrite previously-registered error handlers, `requestId()` `trustIncoming: false` default audit so client-supplied `X-Request-ID` headers cannot poison framework logs by default, RFC 7231 + RFC 5789 HTTP-method allowlist now runtime-enforced inside `app.route()` (WebDAV / `TRACE` / `CONNECT` rejected at the framework boundary), `Connection: close` on every response produced during graceful shutdown reaffirmed by audit, and `scripts/verify-wave12-audits.ts` wired into CI as `pnpm verify:wave12-audits` covering all of the above), and **Wave 11 leftover focused slice since `0.32.0`** (`app.ws()` now scans the effective hook stack for header-mutating middleware — `secureHeaders()` / `cors()` / `csrf()` / `compression()` — and refuses-at-registration with a structured error naming both the WebSocket route AND every conflicting middleware unless the handler opts in via `acknowledgeHeaderMutatingMiddleware: true`, new `httpError({ status, problem, headers?, res? })` factory in `@daloyjs/core` extracts headers from a custom `Response` and refuses-at-construction with `MessageLeakError` when the response would leak request-scoped state — `Set-Cookie`, `Server-Timing`, `X-*-Token`, or `Cache-Control` other than `no-store` / `no-cache` all trip the gate, leaving only the `WWW-Authenticate` / `Proxy-Authenticate` / `Retry-After` / `Content-Type` / `Content-Language` allowlist (with `Content-Length` accepted for safety validation but not forwarded), plus a new `ProblemRenderOptions.contextHeaders` extension so direct callers of `HttpError.toResponse()` get the same Context-merge as the framework boundary, and the Wave 6 `PluginExtension` contract now accepts `responseHeaders?: readonly string[]` — `topoSortExtensions()` refuses-at-call when two extensions declare overlapping `responseHeaders` without declaring a `before` / `after` relationship between them so plugin-registration order can no longer non-deterministically pick a winner on a shared response header) — WebSocket public-route exposure can be acknowledged explicitly, while middleware/header-order checks remain registration invariants) plus first-party middleware (`secureHeaders` with CSP nonce + Trusted Types, `cors`, `rateLimit`, `requestId`, `bearerAuth`, `basicAuth`, `csrf` with **double-submit cookie** + **Fetch-Metadata** strategies, `session`, `timing` / `timingSafeEqual`) and **zero-knob crypto helpers** (`passwordHash` / `passwordVerify` at `@daloyjs/core/hashing`, `verifyWebhookSignature` / `signWebhookPayload`).
384
384
  - Streaming helpers (SSE + NDJSON), multipart ergonomics, OpenTelemetry-compatible tracing, signed-cookie sessions with pluggable stores, and a Redis-backed rate-limit store at `@daloyjs/core/rate-limit-redis`.
385
385
  - WebSocket primitives with the same Bun-style handler shape (`open`/`message`/`close`/`drain`/`error`) running on both Node and Bun adapters, plus typed `app.ws(path, handler)` registration and route-table awareness so the upgrade listener is only installed when WS routes exist.
386
+ - Production WebSocket routes under `secureDefaults` require a pre-upgrade `beforeUpgrade` decision hook or an explicit `acknowledgeUnauthenticated: true` marker; `httpError({ res })` also uses case-insensitive header merging and never forwards a custom response's `Content-Length` onto the rendered `problem+json` body.
387
+ - Registration-conflict guards are deliberate startup invariants: use `acknowledgeHeaderMutatingMiddleware` for reviewed WebSocket/middleware scopes and explicit plugin `before` / `after` ordering for shared `responseHeaders`, rather than expecting `secureDefaults: false` to suppress those checks.
386
388
  - Pretty `printStartupBanner()` / `formatStartupBanner()` startup helpers at `@daloyjs/core/banner`, used by every starter template so `pnpm dev` greets you with a colorized boxed panel (TTY + `NO_COLOR` / `FORCE_COLOR` aware, with an ASCII fallback for dumb terminals).
387
389
  - In-process test client (`app.request()`), contract-test runner, in-process typed client, and Hey API codegen via `pnpm gen`.
388
390
  - One-command watch loop: `daloy dev` delegates to the host runtime's native watcher (`node --import tsx --watch`, `bun --hot`, or `deno run --watch`) with a `--runtime` override for cross-runtime `package.json` scripts.
package/dist/app.d.ts CHANGED
@@ -341,6 +341,17 @@ export interface PluginExtension {
341
341
  before?: readonly string[];
342
342
  /** Extension names this one must run after. */
343
343
  after?: readonly string[];
344
+ /**
345
+ * Wave 11 — names of response headers this extension mutates. When two
346
+ * extensions declare an overlapping `responseHeaders` entry without
347
+ * declaring an explicit `before` / `after` relationship between them,
348
+ * {@link topoSortExtensions} refuses-at-registration with a structured
349
+ * error — the resulting header value would otherwise depend on the
350
+ * non-deterministic registration order of the plugins.
351
+ *
352
+ * @since 0.32.0
353
+ */
354
+ responseHeaders?: readonly string[];
344
355
  }
345
356
  /** Information passed to {@link App.onShutdown} listeners. */
346
357
  export interface ShutdownEvent {
@@ -399,7 +410,9 @@ export interface CspReportRouteOptions {
399
410
  } | false;
400
411
  /**
401
412
  * Maximum accepted request body size, in bytes. Default `8192`. Larger
402
- * bodies are rejected with `413`.
413
+ * bodies are rejected with `413`. Hard upper bound is 65536 bytes
414
+ * (Wave 11) — CSP / Reporting API reports are small; a cap above 64 KiB
415
+ * is rejected at construction time as a DoS-via-report-flood defense.
403
416
  */
404
417
  maxBodyBytes?: number;
405
418
  /**
@@ -412,6 +425,18 @@ export interface CspReportRouteOptions {
412
425
  ip: string | null;
413
426
  userAgent: string | null;
414
427
  }) => void | Promise<void>;
428
+ /**
429
+ * Wave 11 — when `false` (the default in production), the default logger
430
+ * sink omits the parsed report body and logs only `{ ip, userAgent }`.
431
+ * CSP reports include the URL that violated CSP, which may contain PII
432
+ * (e.g. a session-id query parameter on a navigation that triggered the
433
+ * report). Opt in only when the operator has reviewed their logging
434
+ * pipeline. Ignored when `onReport` is supplied — custom sinks own their
435
+ * own redaction policy.
436
+ *
437
+ * @since 0.30.0
438
+ */
439
+ logCspReportBodies?: boolean;
415
440
  }
416
441
  /**
417
442
  * Lightweight introspection record produced by {@link App.introspect}. Useful
@@ -742,8 +767,8 @@ export declare class App {
742
767
  * when omitted). Returns `204 No Content` so browsers stop retrying.
743
768
  *
744
769
  * Combine with `secureHeaders({ reportingEndpoints, reportTo })` to wire
745
- * the browser to this endpoint. The route is registered as `internal:
746
- * false` (i.e. publicly reachable); that is required for the browser
770
+ * the browser to this endpoint. The route is registered as publicly
771
+ * reachable; that is required for the browser
747
772
  * Reporting API to send to it.
748
773
  *
749
774
  */
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
package/dist/app.js CHANGED
@@ -7,6 +7,7 @@ import { createLogger, noopLogger } from "./logger.js";
7
7
  import { generateOpenAPI, openapiToYAML, } from "./openapi.js";
8
8
  import { docsContentSecurityPolicy, scalarHtml, swaggerUiHtml, } from "./docs.js";
9
9
  import { secureHeaders as secureHeadersMiddleware, CORS_HOOK_MARKER, CORS_ORIGIN_ALLOW_MARKER, CORS_WILDCARD_ORIGIN_MARKER, CSRF_HOOK_MARKER, REQUIRE_SCOPES_AGGREGATE_KEY, REQUIRE_SCOPES_HOOK_MARKER, SECURE_HEADERS_MARKER, } from "./middleware.js";
10
+ import { COMPRESSION_HOOK_MARKER } from "./compression.js";
10
11
  import { SESSION_HOOK_MARKER, SESSION_SECRETS_MARKER, } from "./session.js";
11
12
  import { loadShedding as loadSheddingMiddleware } from "./load-shedding.js";
12
13
  import { securitySchemeRequiresPayloadAuth } from "./security-schemes.js";
@@ -54,6 +55,25 @@ const DISABLED_BY_INSECURE_DEFAULTS = Object.freeze([
54
55
  "cors({ origin: '*' }) refuse-to-boot",
55
56
  "anonymous stateful plugin refuse-to-boot",
56
57
  ]);
58
+ /**
59
+ * Wave 12 item 11: the exact RFC 7231 + RFC 5789 HTTP-method allowlist.
60
+ * The framework refuses any other method at route-registration time so
61
+ * `TRACE`, `CONNECT`, and WebDAV verbs (`MKCOL`, `COPY`, `PROPFIND`,
62
+ * `MOVE`, etc.) cannot bypass the strict Content-Type / body-on-GET /
63
+ * TRACE-and-CONNECT-refusal defaults. The set mirrors the `HttpMethod`
64
+ * union exported from `./types.ts`.
65
+ *
66
+ * @internal
67
+ */
68
+ const CANONICAL_HTTP_METHODS = new Set([
69
+ "GET",
70
+ "POST",
71
+ "PUT",
72
+ "PATCH",
73
+ "DELETE",
74
+ "HEAD",
75
+ "OPTIONS",
76
+ ]);
57
77
  const DEFAULTS = {
58
78
  bodyLimitBytes: 1024 * 1024,
59
79
  requestTimeoutMs: 30_000,
@@ -681,6 +701,20 @@ export class App {
681
701
  * @returns This `App` instance for chaining.
682
702
  */
683
703
  route(def) {
704
+ // Wave 12 item 11: refuse non-canonical HTTP methods at runtime.
705
+ // The TypeScript `HttpMethod` union already constrains the public
706
+ // surface, but an unsafe cast (or a runtime caller in plain JS)
707
+ // could bypass it. Closes the "framework silently routes WebDAV /
708
+ // TRACE / CONNECT" class of bypass-via-extended-method bugs at the
709
+ // framework boundary.
710
+ if (!CANONICAL_HTTP_METHODS.has(def.method)) {
711
+ throw new Error(`app.route(): HTTP method "${String(def.method)}" is not on ` +
712
+ `the RFC 7231 + RFC 5789 allowlist. Allowed: ` +
713
+ `${[...CANONICAL_HTTP_METHODS].join(", ")}. Custom methods ` +
714
+ `(TRACE, CONNECT, WebDAV verbs, etc.) bypass the strict ` +
715
+ `Content-Type, body-on-GET, and TRACE/CONNECT refusal ` +
716
+ `defaults and are not supported.`);
717
+ }
684
718
  if (def.hooks)
685
719
  this.assertSecureHookConfig(def.hooks);
686
720
  const fullPath = joinPath(this.prefix, def.path);
@@ -717,10 +751,47 @@ export class App {
717
751
  */
718
752
  ws(path, handler) {
719
753
  const fullPath = joinPath(this.prefix, path);
754
+ const production = this.isProduction();
755
+ const secureDefaults = this.options.secureDefaults !== false;
720
756
  const options = normalizeWebSocketOptions(handler, {
721
- production: this.isProduction(),
722
- secureDefaults: this.options.secureDefaults !== false,
757
+ production,
758
+ secureDefaults,
723
759
  });
760
+ // Wave 11 — pre-upgrade authentication boundary. In production under
761
+ // secureDefaults, a WebSocket route must either make an explicit
762
+ // pre-upgrade decision (`beforeUpgrade`) or acknowledge that the route is
763
+ // intentionally public. This prevents accidental auth in `open()` after
764
+ // the 101 response has already committed the connection.
765
+ if (production &&
766
+ secureDefaults &&
767
+ handler.beforeUpgrade === undefined &&
768
+ handler.acknowledgeUnauthenticated !== true) {
769
+ throw new Error(`app.ws(${JSON.stringify(fullPath)}): production WebSocket routes must ` +
770
+ "authenticate or reject clients before the RFC 6455 upgrade. Add a " +
771
+ "beforeUpgrade hook for authenticated routes, or pass " +
772
+ "{ acknowledgeUnauthenticated: true } for an intentionally public route.");
773
+ }
774
+ // Wave 11 — WebSocket post-upgrade header immutability. Once the RFC
775
+ // 6455 101 handshake has been sent, no further response headers can be
776
+ // added by middleware; mounting header-mutating middleware on a path
777
+ // that also matches a WS route is a documented misconfiguration in
778
+ // upstream frameworks. Refuse-at-registration with a structured error
779
+ // naming both the WS route and the conflicting middleware unless the
780
+ // developer explicitly acknowledges the configuration.
781
+ if (handler.acknowledgeHeaderMutatingMiddleware !== true) {
782
+ const conflicts = detectHeaderMutatingMiddleware([
783
+ this.options.hooks ?? {},
784
+ ...this.groupHooks,
785
+ ]);
786
+ if (conflicts.length > 0) {
787
+ throw new Error(`app.ws(${JSON.stringify(fullPath)}): ${conflicts.join(", ")} ` +
788
+ "middleware is mounted on a path that matches this WebSocket " +
789
+ "route, but no response headers can be added after the RFC 6455 " +
790
+ "upgrade. Either move the middleware below the WebSocket scope, " +
791
+ "or pass { acknowledgeHeaderMutatingMiddleware: true } after " +
792
+ "confirming the middleware does not run on Upgrade requests.");
793
+ }
794
+ }
724
795
  this.webSocketRoutes.add(fullPath, handler, () => ({ ...this.decorations }), options);
725
796
  return this;
726
797
  }
@@ -860,14 +931,20 @@ export class App {
860
931
  * when omitted). Returns `204 No Content` so browsers stop retrying.
861
932
  *
862
933
  * Combine with `secureHeaders({ reportingEndpoints, reportTo })` to wire
863
- * the browser to this endpoint. The route is registered as `internal:
864
- * false` (i.e. publicly reachable); that is required for the browser
934
+ * the browser to this endpoint. The route is registered as publicly
935
+ * reachable; that is required for the browser
865
936
  * Reporting API to send to it.
866
937
  *
867
938
  */
868
939
  cspReportRoute(opts = {}) {
869
940
  const path = (opts.path ?? "/__csp-report");
870
941
  const maxBytes = opts.maxBodyBytes ?? 8192;
942
+ // Wave 11 — refuse a configured cap above 64 KiB so a misconfigured
943
+ // policy cannot turn the receiver into a DoS-via-report-flood amplifier.
944
+ const HARD_MAX = 65536;
945
+ if (!Number.isInteger(maxBytes) || maxBytes <= 0 || maxBytes > HARD_MAX) {
946
+ throw new Error(`cspReportRoute(): maxBodyBytes must be a positive integer <= ${HARD_MAX}.`);
947
+ }
871
948
  const rateLimitConfig = opts.rateLimit === false
872
949
  ? null
873
950
  : { limit: 60, windowMs: 60_000, ...(opts.rateLimit ?? {}) };
@@ -875,6 +952,10 @@ export class App {
875
952
  ? new Map()
876
953
  : null;
877
954
  const log = this.log;
955
+ // Wave 11 — only log report bodies when explicitly enabled. In
956
+ // production this is opt-in; in development the body is included by
957
+ // default so violations are debuggable.
958
+ const includeReportBody = opts.logCspReportBodies ?? !this.isProduction();
878
959
  this.route({
879
960
  method: "POST",
880
961
  path,
@@ -901,12 +982,10 @@ export class App {
901
982
  .trim()
902
983
  .toLowerCase();
903
984
  if (contentType !== "application/csp-report" &&
904
- contentType !== "application/reports+json" &&
905
- contentType !== "application/json") {
985
+ contentType !== "application/reports+json") {
906
986
  throw new UnsupportedMediaTypeError(contentType || "<none>", [
907
987
  "application/csp-report",
908
988
  "application/reports+json",
909
- "application/json",
910
989
  ]);
911
990
  }
912
991
  const rawBytes = await readBodyLimited(request, maxBytes);
@@ -931,7 +1010,9 @@ export class App {
931
1010
  });
932
1011
  }
933
1012
  else {
934
- log.warn({ event: "csp.report", ip, userAgent, report: parsed }, "CSP violation report received");
1013
+ log.warn(includeReportBody
1014
+ ? { event: "csp.report", ip, userAgent, report: parsed }
1015
+ : { event: "csp.report", ip, userAgent }, "CSP violation report received");
935
1016
  }
936
1017
  }
937
1018
  catch (err) {
@@ -1670,6 +1751,32 @@ function corsOriginAllowsFromHooks(layers) {
1670
1751
  }
1671
1752
  return allows;
1672
1753
  }
1754
+ /**
1755
+ * Wave 11 — detect header-mutating middleware on a WebSocket route's
1756
+ * effective hook stack. Returns a list of human-readable names for any
1757
+ * middleware that would otherwise lose its headers to the post-upgrade
1758
+ * RFC 6455 frame stream.
1759
+ * @internal
1760
+ */
1761
+ function detectHeaderMutatingMiddleware(layers) {
1762
+ const found = [];
1763
+ for (const hooks of layers) {
1764
+ const record = hooks;
1765
+ // Skip the framework-default auto-secureHeaders bundle — it is
1766
+ // installed by every App and only sets headers that are missing, so it
1767
+ // never conflicts with a WebSocket upgrade in practice.
1768
+ const isAuto = record[AUTO_SECURE_HEADERS_MARKER] === true;
1769
+ if (!isAuto && record[SECURE_HEADERS_MARKER] === true)
1770
+ found.push("secureHeaders()");
1771
+ if (record[CORS_HOOK_MARKER] === true)
1772
+ found.push("cors()");
1773
+ if (record[CSRF_HOOK_MARKER] === true)
1774
+ found.push("csrf()");
1775
+ if (record[COMPRESSION_HOOK_MARKER] === true)
1776
+ found.push("compression()");
1777
+ }
1778
+ return Array.from(new Set(found));
1779
+ }
1673
1780
  /**
1674
1781
  * Topological sort of plugin extensions (Wave 6 item 10). Refuses-at-call
1675
1782
  * on cyclic ordering with a structured error naming the cycle.
@@ -1724,6 +1831,39 @@ export function topoSortExtensions(exts) {
1724
1831
  const remaining = Array.from(byName.keys()).filter((n) => !out.some((e) => e.name === n));
1725
1832
  throw new Error(`Plugin extension cycle detected among: ${remaining.map((n) => JSON.stringify(n)).join(", ")}.`);
1726
1833
  }
1834
+ // Wave 11 — refuse pairs of extensions that mutate the same response
1835
+ // header without declaring a before/after ordering relative to each
1836
+ // other. The resulting header value would otherwise depend on plugin
1837
+ // registration order.
1838
+ for (let i = 0; i < exts.length; i++) {
1839
+ const a = exts[i];
1840
+ const aHeaders = a.responseHeaders;
1841
+ if (!aHeaders || aHeaders.length === 0)
1842
+ continue;
1843
+ const aSet = new Set(aHeaders.map((h) => h.toLowerCase()));
1844
+ for (let j = i + 1; j < exts.length; j++) {
1845
+ const b = exts[j];
1846
+ const bHeaders = b.responseHeaders;
1847
+ if (!bHeaders || bHeaders.length === 0)
1848
+ continue;
1849
+ const overlap = bHeaders
1850
+ .map((h) => h.toLowerCase())
1851
+ .filter((h) => aSet.has(h));
1852
+ if (overlap.length === 0)
1853
+ continue;
1854
+ const declared = (a.before ?? []).includes(b.name) ||
1855
+ (a.after ?? []).includes(b.name) ||
1856
+ (b.before ?? []).includes(a.name) ||
1857
+ (b.after ?? []).includes(a.name);
1858
+ if (!declared) {
1859
+ throw new Error(`Plugin extension header conflict: ${JSON.stringify(a.name)} and ` +
1860
+ `${JSON.stringify(b.name)} both mutate response header(s) ` +
1861
+ `${overlap.map((h) => JSON.stringify(h)).join(", ")} but neither ` +
1862
+ "declares a `before` or `after` relationship to the other. Add the " +
1863
+ "missing ordering to make the merged header value deterministic.");
1864
+ }
1865
+ }
1866
+ }
1727
1867
  return out;
1728
1868
  }
1729
1869
  function securityMarkersFromHooks(layers) {