@daloyjs/core 0.34.3 → 0.35.1

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.
Files changed (186) hide show
  1. package/README.md +151 -32
  2. package/dist/adapters/bun.d.ts +0 -1
  3. package/dist/adapters/bun.js +0 -1
  4. package/dist/adapters/cloudflare.d.ts +0 -1
  5. package/dist/adapters/cloudflare.js +0 -1
  6. package/dist/adapters/deno.d.ts +0 -1
  7. package/dist/adapters/deno.js +0 -1
  8. package/dist/adapters/fastly.d.ts +0 -1
  9. package/dist/adapters/fastly.js +0 -1
  10. package/dist/adapters/lambda.d.ts +0 -1
  11. package/dist/adapters/lambda.js +0 -1
  12. package/dist/adapters/node.d.ts +0 -1
  13. package/dist/adapters/node.js +175 -35
  14. package/dist/adapters/vercel.d.ts +0 -1
  15. package/dist/adapters/vercel.js +0 -1
  16. package/dist/app.d.ts +18 -1
  17. package/dist/app.js +396 -118
  18. package/dist/banner.d.ts +0 -1
  19. package/dist/banner.js +0 -1
  20. package/dist/cli.d.ts +32 -1
  21. package/dist/cli.js +62 -5
  22. package/dist/client.d.ts +0 -1
  23. package/dist/client.js +0 -1
  24. package/dist/combine.d.ts +0 -1
  25. package/dist/combine.js +0 -1
  26. package/dist/compression.d.ts +0 -1
  27. package/dist/compression.js +0 -1
  28. package/dist/config.d.ts +0 -1
  29. package/dist/config.js +0 -1
  30. package/dist/conn-info.d.ts +0 -1
  31. package/dist/conn-info.js +0 -1
  32. package/dist/contract.d.ts +0 -1
  33. package/dist/contract.js +0 -1
  34. package/dist/cookie.d.ts +0 -1
  35. package/dist/cookie.js +0 -1
  36. package/dist/dependency.d.ts +0 -1
  37. package/dist/dependency.js +0 -1
  38. package/dist/discriminator.d.ts +0 -1
  39. package/dist/discriminator.js +0 -1
  40. package/dist/docs.d.ts +0 -1
  41. package/dist/docs.js +0 -1
  42. package/dist/errors.d.ts +0 -1
  43. package/dist/errors.js +0 -1
  44. package/dist/etag.d.ts +0 -1
  45. package/dist/etag.js +0 -1
  46. package/dist/fetch-guard.d.ts +50 -6
  47. package/dist/fetch-guard.js +83 -18
  48. package/dist/hashing.d.ts +0 -1
  49. package/dist/hashing.js +0 -1
  50. package/dist/index.d.ts +5 -4
  51. package/dist/index.js +3 -3
  52. package/dist/ip-restriction.d.ts +0 -1
  53. package/dist/ip-restriction.js +0 -1
  54. package/dist/jwk.d.ts +0 -1
  55. package/dist/jwk.js +0 -1
  56. package/dist/jwt.d.ts +15 -1
  57. package/dist/jwt.js +56 -3
  58. package/dist/load-shedding.d.ts +0 -1
  59. package/dist/load-shedding.js +0 -1
  60. package/dist/logger.d.ts +0 -1
  61. package/dist/logger.js +0 -1
  62. package/dist/middleware.d.ts +78 -1
  63. package/dist/middleware.js +150 -18
  64. package/dist/multipart.d.ts +0 -1
  65. package/dist/multipart.js +0 -1
  66. package/dist/openapi.d.ts +0 -1
  67. package/dist/openapi.js +0 -1
  68. package/dist/rate-limit-redis.d.ts +0 -1
  69. package/dist/rate-limit-redis.js +0 -1
  70. package/dist/router.d.ts +0 -1
  71. package/dist/router.js +0 -1
  72. package/dist/safe-redirect.d.ts +91 -0
  73. package/dist/safe-redirect.js +168 -0
  74. package/dist/sbom.cdx.json +13 -9
  75. package/dist/sbom.spdx.json +5 -5
  76. package/dist/schema.d.ts +0 -1
  77. package/dist/schema.js +0 -1
  78. package/dist/security-schemes.d.ts +0 -1
  79. package/dist/security-schemes.js +0 -1
  80. package/dist/security.d.ts +134 -3
  81. package/dist/security.js +265 -6
  82. package/dist/session.d.ts +0 -1
  83. package/dist/session.js +0 -1
  84. package/dist/streaming.d.ts +0 -1
  85. package/dist/streaming.js +0 -1
  86. package/dist/subdomains.d.ts +0 -1
  87. package/dist/subdomains.js +0 -1
  88. package/dist/time-claims.d.ts +0 -1
  89. package/dist/time-claims.js +0 -1
  90. package/dist/tracing.d.ts +0 -1
  91. package/dist/tracing.js +0 -1
  92. package/dist/types.d.ts +0 -1
  93. package/dist/types.js +0 -1
  94. package/dist/websocket.d.ts +0 -1
  95. package/dist/websocket.js +0 -1
  96. package/package.json +35 -15
  97. package/dist/adapters/bun.d.ts.map +0 -1
  98. package/dist/adapters/bun.js.map +0 -1
  99. package/dist/adapters/cloudflare.d.ts.map +0 -1
  100. package/dist/adapters/cloudflare.js.map +0 -1
  101. package/dist/adapters/deno.d.ts.map +0 -1
  102. package/dist/adapters/deno.js.map +0 -1
  103. package/dist/adapters/fastly.d.ts.map +0 -1
  104. package/dist/adapters/fastly.js.map +0 -1
  105. package/dist/adapters/lambda.d.ts.map +0 -1
  106. package/dist/adapters/lambda.js.map +0 -1
  107. package/dist/adapters/node.d.ts.map +0 -1
  108. package/dist/adapters/node.js.map +0 -1
  109. package/dist/adapters/vercel.d.ts.map +0 -1
  110. package/dist/adapters/vercel.js.map +0 -1
  111. package/dist/app.d.ts.map +0 -1
  112. package/dist/app.js.map +0 -1
  113. package/dist/banner.d.ts.map +0 -1
  114. package/dist/banner.js.map +0 -1
  115. package/dist/cli.d.ts.map +0 -1
  116. package/dist/cli.js.map +0 -1
  117. package/dist/client.d.ts.map +0 -1
  118. package/dist/client.js.map +0 -1
  119. package/dist/combine.d.ts.map +0 -1
  120. package/dist/combine.js.map +0 -1
  121. package/dist/compression.d.ts.map +0 -1
  122. package/dist/compression.js.map +0 -1
  123. package/dist/config.d.ts.map +0 -1
  124. package/dist/config.js.map +0 -1
  125. package/dist/conn-info.d.ts.map +0 -1
  126. package/dist/conn-info.js.map +0 -1
  127. package/dist/contract.d.ts.map +0 -1
  128. package/dist/contract.js.map +0 -1
  129. package/dist/cookie.d.ts.map +0 -1
  130. package/dist/cookie.js.map +0 -1
  131. package/dist/dependency.d.ts.map +0 -1
  132. package/dist/dependency.js.map +0 -1
  133. package/dist/discriminator.d.ts.map +0 -1
  134. package/dist/discriminator.js.map +0 -1
  135. package/dist/docs.d.ts.map +0 -1
  136. package/dist/docs.js.map +0 -1
  137. package/dist/errors.d.ts.map +0 -1
  138. package/dist/errors.js.map +0 -1
  139. package/dist/etag.d.ts.map +0 -1
  140. package/dist/etag.js.map +0 -1
  141. package/dist/fetch-guard.d.ts.map +0 -1
  142. package/dist/fetch-guard.js.map +0 -1
  143. package/dist/hashing.d.ts.map +0 -1
  144. package/dist/hashing.js.map +0 -1
  145. package/dist/index.d.ts.map +0 -1
  146. package/dist/index.js.map +0 -1
  147. package/dist/ip-restriction.d.ts.map +0 -1
  148. package/dist/ip-restriction.js.map +0 -1
  149. package/dist/jwk.d.ts.map +0 -1
  150. package/dist/jwk.js.map +0 -1
  151. package/dist/jwt.d.ts.map +0 -1
  152. package/dist/jwt.js.map +0 -1
  153. package/dist/load-shedding.d.ts.map +0 -1
  154. package/dist/load-shedding.js.map +0 -1
  155. package/dist/logger.d.ts.map +0 -1
  156. package/dist/logger.js.map +0 -1
  157. package/dist/middleware.d.ts.map +0 -1
  158. package/dist/middleware.js.map +0 -1
  159. package/dist/multipart.d.ts.map +0 -1
  160. package/dist/multipart.js.map +0 -1
  161. package/dist/openapi.d.ts.map +0 -1
  162. package/dist/openapi.js.map +0 -1
  163. package/dist/rate-limit-redis.d.ts.map +0 -1
  164. package/dist/rate-limit-redis.js.map +0 -1
  165. package/dist/router.d.ts.map +0 -1
  166. package/dist/router.js.map +0 -1
  167. package/dist/schema.d.ts.map +0 -1
  168. package/dist/schema.js.map +0 -1
  169. package/dist/security-schemes.d.ts.map +0 -1
  170. package/dist/security-schemes.js.map +0 -1
  171. package/dist/security.d.ts.map +0 -1
  172. package/dist/security.js.map +0 -1
  173. package/dist/session.d.ts.map +0 -1
  174. package/dist/session.js.map +0 -1
  175. package/dist/streaming.d.ts.map +0 -1
  176. package/dist/streaming.js.map +0 -1
  177. package/dist/subdomains.d.ts.map +0 -1
  178. package/dist/subdomains.js.map +0 -1
  179. package/dist/time-claims.d.ts.map +0 -1
  180. package/dist/time-claims.js.map +0 -1
  181. package/dist/tracing.d.ts.map +0 -1
  182. package/dist/tracing.js.map +0 -1
  183. package/dist/types.d.ts.map +0 -1
  184. package/dist/types.js.map +0 -1
  185. package/dist/websocket.d.ts.map +0 -1
  186. package/dist/websocket.js.map +0 -1
package/dist/banner.d.ts CHANGED
@@ -37,4 +37,3 @@ export declare function formatStartupBanner(options: StartupBannerOptions): stri
37
37
  * replace ad-hoc `console.log("listening on …")` calls in starter templates.
38
38
  */
39
39
  export declare function printStartupBanner(options: StartupBannerOptions, write?: (s: string) => void): void;
40
- //# sourceMappingURL=banner.d.ts.map
package/dist/banner.js CHANGED
@@ -110,4 +110,3 @@ export function formatStartupBanner(options) {
110
110
  export function printStartupBanner(options, write = (s) => process.stdout.write(s)) {
111
111
  write(`\n${formatStartupBanner(options)}\n\n`);
112
112
  }
113
- //# sourceMappingURL=banner.js.map
package/dist/cli.d.ts CHANGED
@@ -71,6 +71,32 @@ export type DevRuntime = "node" | "bun" | "deno";
71
71
  * @since 0.3.0
72
72
  */
73
73
  export declare function detectRuntime(): DevRuntime;
74
+ /**
75
+ * Validate a CLI-supplied entry path before it is handed to `import()` or
76
+ * forwarded as a child-process argv element. Rejects NUL bytes, CR/LF, and
77
+ * leading `-` characters so a hostile caller (or a misparsed positional)
78
+ * cannot smuggle a `--eval=...`, `--inspect-brk=...`, or similar
79
+ * argv-injection payload past `spawn({ shell: false })` into Node, Bun, or
80
+ * Deno's own argv parser.
81
+ *
82
+ * This is defense-in-depth alongside {@link parseArgs}, which already
83
+ * rejects `-`-prefixed positionals. The check also runs in programmatic
84
+ * `runCli()` callers and tests so the invariant holds regardless of how the
85
+ * entry value was produced.
86
+ *
87
+ * @internal
88
+ */
89
+ export declare function assertSafeEntryPath(entry: string, context: string): void;
90
+ /**
91
+ * Normalize a relative entry path so the host runtime always sees an
92
+ * unambiguous file path, never a possible flag. Absolute paths and paths
93
+ * already anchored with `./` or `../` are returned unchanged; bare relative
94
+ * paths like `src/server.ts` are rewritten to `./src/server.ts`. This is
95
+ * the second half of the {@link assertSafeEntryPath} defense.
96
+ *
97
+ * @internal
98
+ */
99
+ export declare function normalizeEntryArg(entry: string): string;
74
100
  /**
75
101
  * Build the `(command, args)` pair `daloy dev` should spawn for the given
76
102
  * runtime and entry file. Pure function so tests can assert exact argv
@@ -84,6 +110,12 @@ export declare function detectRuntime(): DevRuntime;
84
110
  * reading the source tree; users who need more should run `deno run`
85
111
  * directly).
86
112
  *
113
+ * The entry is validated via {@link assertSafeEntryPath} and anchored with
114
+ * {@link normalizeEntryArg} so a future caller cannot smuggle a flag past
115
+ * `spawn({ shell: false })` into the runtime's argv parser. See
116
+ * `SECURITY.md` § "CLI threat model" for the full rationale (and why we
117
+ * are not vulnerable to the class of bug Snyk reported as CVE-2022-22984).
118
+ *
87
119
  * @since 0.3.0
88
120
  */
89
121
  export declare function buildDevCommand(runtime: DevRuntime, entry: string): {
@@ -114,4 +146,3 @@ export declare function runCli(argv: readonly string[], io: CliIO): Promise<CliR
114
146
  * @since 0.14.0
115
147
  */
116
148
  export declare function buildAiDump(app: App, opts: CliOptions): Record<string, unknown>;
117
- //# sourceMappingURL=cli.d.ts.map
package/dist/cli.js CHANGED
@@ -115,6 +115,52 @@ export function detectRuntime() {
115
115
  return "deno";
116
116
  return "node";
117
117
  }
118
+ /**
119
+ * Validate a CLI-supplied entry path before it is handed to `import()` or
120
+ * forwarded as a child-process argv element. Rejects NUL bytes, CR/LF, and
121
+ * leading `-` characters so a hostile caller (or a misparsed positional)
122
+ * cannot smuggle a `--eval=...`, `--inspect-brk=...`, or similar
123
+ * argv-injection payload past `spawn({ shell: false })` into Node, Bun, or
124
+ * Deno's own argv parser.
125
+ *
126
+ * This is defense-in-depth alongside {@link parseArgs}, which already
127
+ * rejects `-`-prefixed positionals. The check also runs in programmatic
128
+ * `runCli()` callers and tests so the invariant holds regardless of how the
129
+ * entry value was produced.
130
+ *
131
+ * @internal
132
+ */
133
+ export function assertSafeEntryPath(entry, context) {
134
+ if (typeof entry !== "string" || entry.length === 0) {
135
+ throw new Error(`${context}: entry path must be a non-empty string.`);
136
+ }
137
+ if (entry.includes("\0") || /[\r\n]/.test(entry)) {
138
+ throw new Error(`${context}: entry path must not contain NUL bytes or newlines (got: ${JSON.stringify(entry)}).`);
139
+ }
140
+ if (entry.startsWith("-")) {
141
+ throw new Error(`${context}: entry path must not start with "-" — runtimes parse leading dashes as flags ` +
142
+ `(got: ${JSON.stringify(entry)}). Prefix the path with "./" to disambiguate.`);
143
+ }
144
+ }
145
+ /**
146
+ * Normalize a relative entry path so the host runtime always sees an
147
+ * unambiguous file path, never a possible flag. Absolute paths and paths
148
+ * already anchored with `./` or `../` are returned unchanged; bare relative
149
+ * paths like `src/server.ts` are rewritten to `./src/server.ts`. This is
150
+ * the second half of the {@link assertSafeEntryPath} defense.
151
+ *
152
+ * @internal
153
+ */
154
+ export function normalizeEntryArg(entry) {
155
+ if (entry.startsWith("./") || entry.startsWith("../"))
156
+ return entry;
157
+ if (entry.startsWith("/"))
158
+ return entry;
159
+ // Windows drive letter (C:\, D:/, ...).
160
+ if (/^[A-Za-z]:[\\/]/.test(entry))
161
+ return entry;
162
+ return `./${entry}`;
163
+ }
118
164
  /**
119
165
  * Build the `(command, args)` pair `daloy dev` should spawn for the given
120
166
  * runtime and entry file. Pure function so tests can assert exact argv
@@ -128,20 +174,28 @@ export function detectRuntime() {
128
174
  * reading the source tree; users who need more should run `deno run`
129
175
  * directly).
130
176
  *
177
+ * The entry is validated via {@link assertSafeEntryPath} and anchored with
178
+ * {@link normalizeEntryArg} so a future caller cannot smuggle a flag past
179
+ * `spawn({ shell: false })` into the runtime's argv parser. See
180
+ * `SECURITY.md` § "CLI threat model" for the full rationale (and why we
181
+ * are not vulnerable to the class of bug Snyk reported as CVE-2022-22984).
182
+ *
131
183
  * @since 0.3.0
132
184
  */
133
185
  export function buildDevCommand(runtime, entry) {
186
+ assertSafeEntryPath(entry, "daloy dev");
187
+ const safe = normalizeEntryArg(entry);
134
188
  switch (runtime) {
135
189
  case "bun":
136
- return { command: "bun", args: ["--hot", entry] };
190
+ return { command: "bun", args: ["--hot", safe] };
137
191
  case "deno":
138
192
  return {
139
193
  command: "deno",
140
- args: ["run", "--watch", "--allow-net", "--allow-env", "--allow-read", entry],
194
+ args: ["run", "--watch", "--allow-net", "--allow-env", "--allow-read", safe],
141
195
  };
142
196
  case "node":
143
197
  default:
144
- return { command: "node", args: ["--import", "tsx", "--watch", entry] };
198
+ return { command: "node", args: ["--import", "tsx", "--watch", safe] };
145
199
  }
146
200
  }
147
201
  /**
@@ -151,8 +205,10 @@ export function buildDevCommand(runtime, entry) {
151
205
  * first candidate.
152
206
  */
153
207
  async function resolveDevEntry(entry) {
154
- if (entry)
208
+ if (entry) {
209
+ assertSafeEntryPath(entry, "daloy dev");
155
210
  return entry;
211
+ }
156
212
  try {
157
213
  const fs = await import("node:fs");
158
214
  const path = await import("node:path");
@@ -725,6 +781,8 @@ function aiSchema(schema) {
725
781
  return {};
726
782
  }
727
783
  async function loadApp(entry, io) {
784
+ if (entry !== undefined)
785
+ assertSafeEntryPath(entry, "daloy inspect");
728
786
  const candidates = entry ? [entry] : DEFAULT_ENTRIES.slice();
729
787
  let lastErr;
730
788
  for (const candidate of candidates) {
@@ -790,4 +848,3 @@ function isApp(value) {
790
848
  typeof value.introspect === "function" &&
791
849
  typeof value.fetch === "function");
792
850
  }
793
- //# sourceMappingURL=cli.js.map
package/dist/client.d.ts CHANGED
@@ -74,4 +74,3 @@ export interface ClientOptions {
74
74
  */
75
75
  export declare function createClient<A extends App>(app: A, opts: ClientOptions): ClientFor<A>;
76
76
  export {};
77
- //# sourceMappingURL=client.d.ts.map
package/dist/client.js CHANGED
@@ -85,4 +85,3 @@ function safeJson(text) {
85
85
  return text;
86
86
  }
87
87
  }
88
- //# sourceMappingURL=client.js.map
package/dist/combine.d.ts CHANGED
@@ -94,4 +94,3 @@ export type ExceptPredicate = string | string[] | ((ctx: BaseContext<any, any>)
94
94
  * @since 0.19.0
95
95
  */
96
96
  export declare function except(when: ExceptPredicate, hooks: Hooks): Hooks;
97
- //# sourceMappingURL=combine.d.ts.map
package/dist/combine.js CHANGED
@@ -244,4 +244,3 @@ function mergeCombineHooks(layers) {
244
244
  }
245
245
  return merged;
246
246
  }
247
- //# sourceMappingURL=combine.js.map
@@ -124,4 +124,3 @@ export declare function _resetCompressionRuntimeProbeForTests(): void;
124
124
  * @since 0.25.0
125
125
  */
126
126
  export declare function compression(opts?: CompressionOptions): Hooks;
127
- //# sourceMappingURL=compression.d.ts.map
@@ -365,4 +365,3 @@ export function compression(opts = {}) {
365
365
  hooks[COMPRESSION_HOOK_MARKER] = true;
366
366
  return hooks;
367
367
  }
368
- //# sourceMappingURL=compression.js.map
package/dist/config.d.ts CHANGED
@@ -95,4 +95,3 @@ export interface DefineConfigOptions<S extends StandardSchemaV1> {
95
95
  * @since 0.20.0
96
96
  */
97
97
  export declare function defineConfig<S extends StandardSchemaV1>(opts: DefineConfigOptions<S>): Promise<StandardSchemaV1.InferOutput<S>>;
98
- //# sourceMappingURL=config.d.ts.map
package/dist/config.js CHANGED
@@ -130,4 +130,3 @@ export async function defineConfig(opts) {
130
130
  }
131
131
  return result.value;
132
132
  }
133
- //# sourceMappingURL=config.js.map
@@ -118,4 +118,3 @@ export declare function readRemotePort(ctx: BaseContext<any, any>): number | und
118
118
  /** @internal Test-only helper. */
119
119
  export declare function _makeConnInfoForTests(info: ConnInfo): MutableConnInfo;
120
120
  export {};
121
- //# sourceMappingURL=conn-info.d.ts.map
package/dist/conn-info.js CHANGED
@@ -142,4 +142,3 @@ export function readRemotePort(ctx) {
142
142
  export function _makeConnInfoForTests(info) {
143
143
  return { ...info };
144
144
  }
145
- //# sourceMappingURL=conn-info.js.map
@@ -47,4 +47,3 @@ export interface ContractTestOptions {
47
47
  * @returns A {@link ContractReport}. In CI, exit non-zero when `report.ok` is `false`.
48
48
  */
49
49
  export declare function runContractTests(app: App, opts?: ContractTestOptions): Promise<ContractReport>;
50
- //# sourceMappingURL=contract.d.ts.map
package/dist/contract.js CHANGED
@@ -133,4 +133,3 @@ export async function runContractTests(app, opts = {}) {
133
133
  issues,
134
134
  };
135
135
  }
136
- //# sourceMappingURL=contract.js.map
package/dist/cookie.d.ts CHANGED
@@ -123,4 +123,3 @@ export declare function serializeClearCookie(name: string, attributes?: CookieAt
123
123
  export declare function readRequestCookie(header: string | null | undefined, name: string): string | null;
124
124
  /** @internal Re-exported for tests. */
125
125
  export declare const __COOKIE_FRAMEWORK_PREFIX__ = "daloy.cookie";
126
- //# sourceMappingURL=cookie.d.ts.map
package/dist/cookie.js CHANGED
@@ -202,4 +202,3 @@ export function readRequestCookie(header, name) {
202
202
  }
203
203
  /** @internal Re-exported for tests. */
204
204
  export const __COOKIE_FRAMEWORK_PREFIX__ = FRAMEWORK_PREFIX;
205
- //# sourceMappingURL=cookie.js.map
@@ -45,4 +45,3 @@ export interface DependencyHooks extends Hooks {
45
45
  * @since 0.24.0
46
46
  */
47
47
  export declare function defineDependency<TName extends string, TValue, TStateKey extends string = TName>(opts: DependencyOptions<TName, TValue, TStateKey>): DependencyHooks;
48
- //# sourceMappingURL=dependency.d.ts.map
@@ -65,4 +65,3 @@ export function defineDependency(opts) {
65
65
  });
66
66
  return hooks;
67
67
  }
68
- //# sourceMappingURL=dependency.js.map
@@ -71,4 +71,3 @@ export type DiscriminatedUnion<V extends Record<string, StandardSchemaV1>> = Sta
71
71
  */
72
72
  export declare function discriminatedUnion<P extends string, V extends Record<string, StandardSchemaV1>>(propertyName: P, variants: V, opts?: DiscriminatedUnionOptions): DiscriminatedUnion<V>;
73
73
  export {};
74
- //# sourceMappingURL=discriminator.d.ts.map
@@ -111,4 +111,3 @@ function variantToJsonSchema(schema) {
111
111
  }
112
112
  return {};
113
113
  }
114
- //# sourceMappingURL=discriminator.js.map
package/dist/docs.d.ts CHANGED
@@ -153,4 +153,3 @@ export declare function docsContentSecurityPolicy(opts?: DocsContentSecurityPoli
153
153
  * {@link docsContentSecurityPolicy} (or a caller-supplied override).
154
154
  */
155
155
  export declare function htmlResponse(html: string, opts?: HtmlResponseOptions): Response;
156
- //# sourceMappingURL=docs.d.ts.map
package/dist/docs.js CHANGED
@@ -113,4 +113,3 @@ function scalarConfigurationAttr(specUrl, configuration) {
113
113
  const { content: _content, fetch: _fetch, plugins: _plugins, sources: _sources, spec: _spec, url: _url, ...uiConfiguration } = configuration;
114
114
  return ` data-configuration='${escapeHtml(JSON.stringify({ ...uiConfiguration, url: specUrl }))}'`;
115
115
  }
116
- //# sourceMappingURL=docs.js.map
package/dist/errors.d.ts CHANGED
@@ -327,4 +327,3 @@ export declare class RequestTimeoutError extends HttpError {
327
327
  export declare class InternalError extends HttpError {
328
328
  constructor(detail?: string);
329
329
  }
330
- //# sourceMappingURL=errors.d.ts.map
package/dist/errors.js CHANGED
@@ -444,4 +444,3 @@ export class InternalError extends HttpError {
444
444
  this.name = "InternalError";
445
445
  }
446
446
  }
447
- //# sourceMappingURL=errors.js.map
package/dist/etag.d.ts CHANGED
@@ -45,4 +45,3 @@ export interface ETagOptions {
45
45
  * @since 0.21.0
46
46
  */
47
47
  export declare function etag(opts?: ETagOptions): Hooks;
48
- //# sourceMappingURL=etag.d.ts.map
package/dist/etag.js CHANGED
@@ -114,4 +114,3 @@ export function etag(opts = {}) {
114
114
  },
115
115
  };
116
116
  }
117
- //# sourceMappingURL=etag.js.map
@@ -24,18 +24,63 @@
24
24
  * Plus an always-deny floor that no flag can lift:
25
25
  *
26
26
  * - `0.0.0.0/8` (this-network), `100.64.0.0/10` (carrier-grade NAT — covers
27
- * Alibaba `100.100.100.200`), `192.0.0.0/24` (covers Oracle Cloud
28
- * `192.0.0.192`), `192.0.2.0/24`, `198.18.0.0/15`, `198.51.100.0/24`,
29
- * `203.0.113.0/24` (IANA-reserved), `224.0.0.0/4` (multicast),
30
- * `240.0.0.0/4` (reserved), `255.255.255.255` (broadcast).
31
- * - IPv6: `::/128` (unspecified), `ff00::/8` (multicast), IPv4-mapped
27
+ * Alibaba `100.100.100.200`), `169.254.169.254/32` (AWS / Azure /
28
+ * DigitalOcean / GCP IMDS), `169.254.170.2/32` and `169.254.170.23/32`
29
+ * (AWS ECS task metadata / EKS Pod Identity), `192.0.0.0/24` (covers
30
+ * Oracle Cloud `192.0.0.192`), `192.0.2.0/24`, `198.18.0.0/15`,
31
+ * `198.51.100.0/24`, `203.0.113.0/24` (IANA-reserved), `224.0.0.0/4`
32
+ * (multicast), `240.0.0.0/4` (reserved), `255.255.255.255` (broadcast).
33
+ * - IPv6: `::/128` (unspecified), `ff00::/8` (multicast),
34
+ * `fd00:ec2::254/128` (AWS IMDSv2 IPv6), IPv4-mapped
32
35
  * `::ffff:0:0/96` is re-checked against the embedded IPv4 address.
33
36
  *
37
+ * The floor also picks up any user-supplied `denyAddresses` — these win
38
+ * over `allowAddresses` and over the soft-deny class flags, so an
39
+ * operator-pinned internal range is never accidentally re-exposed by a
40
+ * later `allowAddresses` carveout.
41
+ *
34
42
  * Redirects are followed **manually** with re-validation at each hop so
35
43
  * an attacker cannot bypass the check via a `302 → http://169.254...`.
36
44
  * `non-http(s)` protocols (`file:`, `ftp:`, `gopher:`, `data:`) are
37
45
  * rejected before any network call.
38
46
  *
47
+ * ## Residual DNS-rebinding (TOCTOU) caveat
48
+ *
49
+ * `fetchGuard()` resolves the hostname, validates every returned address
50
+ * against the deny / allow set, and only then hands the original
51
+ * `Request` to the underlying `fetch`. The underlying `fetch` performs
52
+ * its **own** DNS lookup when it opens the socket. An attacker that
53
+ * controls a TTL=0 record can return a public IP at validation time and
54
+ * a `127.0.0.1` / `169.254.169.254` at connect time, slipping past the
55
+ * library-level check. This residual TOCTOU window cannot be closed at
56
+ * the `fetch()` boundary without injecting a custom HTTP dispatcher
57
+ * (e.g. `undici.Agent({ connect: { lookup } })`) — and Daloy
58
+ * deliberately ships **zero** runtime dependencies, so we do not bundle
59
+ * `undici`. To fully close the window:
60
+ *
61
+ * 1. **Operator-side** (recommended): block egress to RFC1918 /
62
+ * loopback / link-local at the VPC / firewall layer. This neutralises
63
+ * the rebinding even if the application is naïve.
64
+ * 2. **Caller-side, Node only**: install `undici` and pass a custom
65
+ * `fetch` that wires a dispatcher with a pinned-IP `connect.lookup`,
66
+ * e.g.
67
+ * ```ts
68
+ * import { Agent, fetch as undiciFetch } from "undici";
69
+ * import * as dns from "node:dns/promises";
70
+ * const safeFetch = fetchGuard({
71
+ * fetch: async (input, init) => {
72
+ * const url = new URL(typeof input === "string" ? input : input.url);
73
+ * const { address, family } = await dns.lookup(url.hostname, { verbatim: true });
74
+ * const dispatcher = new Agent({
75
+ * connect: { lookup: (_h, _o, cb) => cb(null, address, family) },
76
+ * });
77
+ * return undiciFetch(input, { ...init, dispatcher });
78
+ * },
79
+ * });
80
+ * ```
81
+ * The pre-resolved IP is what the socket connects to; TLS SNI /
82
+ * cert validation still uses the original hostname.
83
+ *
39
84
  * @since 0.34.0
40
85
  * @module
41
86
  */
@@ -160,4 +205,3 @@ export interface FetchGuardOptions {
160
205
  * @since 0.34.0
161
206
  */
162
207
  export declare function fetchGuard(options?: FetchGuardOptions): typeof fetch;
163
- //# sourceMappingURL=fetch-guard.d.ts.map
@@ -24,18 +24,63 @@
24
24
  * Plus an always-deny floor that no flag can lift:
25
25
  *
26
26
  * - `0.0.0.0/8` (this-network), `100.64.0.0/10` (carrier-grade NAT — covers
27
- * Alibaba `100.100.100.200`), `192.0.0.0/24` (covers Oracle Cloud
28
- * `192.0.0.192`), `192.0.2.0/24`, `198.18.0.0/15`, `198.51.100.0/24`,
29
- * `203.0.113.0/24` (IANA-reserved), `224.0.0.0/4` (multicast),
30
- * `240.0.0.0/4` (reserved), `255.255.255.255` (broadcast).
31
- * - IPv6: `::/128` (unspecified), `ff00::/8` (multicast), IPv4-mapped
27
+ * Alibaba `100.100.100.200`), `169.254.169.254/32` (AWS / Azure /
28
+ * DigitalOcean / GCP IMDS), `169.254.170.2/32` and `169.254.170.23/32`
29
+ * (AWS ECS task metadata / EKS Pod Identity), `192.0.0.0/24` (covers
30
+ * Oracle Cloud `192.0.0.192`), `192.0.2.0/24`, `198.18.0.0/15`,
31
+ * `198.51.100.0/24`, `203.0.113.0/24` (IANA-reserved), `224.0.0.0/4`
32
+ * (multicast), `240.0.0.0/4` (reserved), `255.255.255.255` (broadcast).
33
+ * - IPv6: `::/128` (unspecified), `ff00::/8` (multicast),
34
+ * `fd00:ec2::254/128` (AWS IMDSv2 IPv6), IPv4-mapped
32
35
  * `::ffff:0:0/96` is re-checked against the embedded IPv4 address.
33
36
  *
37
+ * The floor also picks up any user-supplied `denyAddresses` — these win
38
+ * over `allowAddresses` and over the soft-deny class flags, so an
39
+ * operator-pinned internal range is never accidentally re-exposed by a
40
+ * later `allowAddresses` carveout.
41
+ *
34
42
  * Redirects are followed **manually** with re-validation at each hop so
35
43
  * an attacker cannot bypass the check via a `302 → http://169.254...`.
36
44
  * `non-http(s)` protocols (`file:`, `ftp:`, `gopher:`, `data:`) are
37
45
  * rejected before any network call.
38
46
  *
47
+ * ## Residual DNS-rebinding (TOCTOU) caveat
48
+ *
49
+ * `fetchGuard()` resolves the hostname, validates every returned address
50
+ * against the deny / allow set, and only then hands the original
51
+ * `Request` to the underlying `fetch`. The underlying `fetch` performs
52
+ * its **own** DNS lookup when it opens the socket. An attacker that
53
+ * controls a TTL=0 record can return a public IP at validation time and
54
+ * a `127.0.0.1` / `169.254.169.254` at connect time, slipping past the
55
+ * library-level check. This residual TOCTOU window cannot be closed at
56
+ * the `fetch()` boundary without injecting a custom HTTP dispatcher
57
+ * (e.g. `undici.Agent({ connect: { lookup } })`) — and Daloy
58
+ * deliberately ships **zero** runtime dependencies, so we do not bundle
59
+ * `undici`. To fully close the window:
60
+ *
61
+ * 1. **Operator-side** (recommended): block egress to RFC1918 /
62
+ * loopback / link-local at the VPC / firewall layer. This neutralises
63
+ * the rebinding even if the application is naïve.
64
+ * 2. **Caller-side, Node only**: install `undici` and pass a custom
65
+ * `fetch` that wires a dispatcher with a pinned-IP `connect.lookup`,
66
+ * e.g.
67
+ * ```ts
68
+ * import { Agent, fetch as undiciFetch } from "undici";
69
+ * import * as dns from "node:dns/promises";
70
+ * const safeFetch = fetchGuard({
71
+ * fetch: async (input, init) => {
72
+ * const url = new URL(typeof input === "string" ? input : input.url);
73
+ * const { address, family } = await dns.lookup(url.hostname, { verbatim: true });
74
+ * const dispatcher = new Agent({
75
+ * connect: { lookup: (_h, _o, cb) => cb(null, address, family) },
76
+ * });
77
+ * return undiciFetch(input, { ...init, dispatcher });
78
+ * },
79
+ * });
80
+ * ```
81
+ * The pre-resolved IP is what the socket connects to; TLS SNI /
82
+ * cert validation still uses the original hostname.
83
+ *
39
84
  * @since 0.34.0
40
85
  * @module
41
86
  */
@@ -65,6 +110,9 @@ export class SsrfBlockedError extends Error {
65
110
  const ALWAYS_DENY = [
66
111
  "0.0.0.0/8", // "this network"
67
112
  "100.64.0.0/10", // CGNAT (Alibaba metadata 100.100.100.200)
113
+ "169.254.169.254/32", // AWS / Azure / DigitalOcean / GCP IMDS — hard floor
114
+ "169.254.170.2/32", // AWS ECS task metadata v2 / EKS Pod Identity
115
+ "169.254.170.23/32", // AWS EKS Pod Identity (IPv4)
68
116
  "192.0.0.0/24", // IANA reserved (Oracle metadata 192.0.0.192)
69
117
  "192.0.2.0/24", // TEST-NET-1
70
118
  "198.18.0.0/15", // benchmarking
@@ -74,6 +122,7 @@ const ALWAYS_DENY = [
74
122
  "240.0.0.0/4", // reserved (includes 255.255.255.255)
75
123
  "::/128", // unspecified
76
124
  "ff00::/8", // IPv6 multicast
125
+ "fd00:ec2::254/128", // AWS IMDSv2 IPv6
77
126
  ];
78
127
  const LOOPBACK = ["127.0.0.0/8", "::1/128"];
79
128
  const LINK_LOCAL = ["169.254.0.0/16", "fe80::/10"];
@@ -107,7 +156,17 @@ const UNIQUE_LOCAL = ["fc00::/7"];
107
156
  */
108
157
  export function fetchGuard(options = {}) {
109
158
  const allowProtocols = new Set((options.allowProtocols ?? ["http:", "https:"]).map((s) => s.toLowerCase()));
110
- const denyMatchers = [];
159
+ // Hard-deny floor: ALWAYS_DENY (cloud metadata + reserved) plus any
160
+ // user-supplied `denyAddresses`. No allow flag, including
161
+ // `allowAddresses`, can lift these — this is what keeps a misconfigured
162
+ // egress allow-list from accidentally re-exposing 169.254.169.254 or
163
+ // an operator-pinned internal range.
164
+ const hardDenyMatchers = [];
165
+ // Soft-deny class defaults: loopback / private / link-local /
166
+ // unique-local. These reflect "off by default" classes that the
167
+ // matching `allow*` flag — or an explicit `allowAddresses` range —
168
+ // is allowed to opt into.
169
+ const softDenyMatchers = [];
111
170
  const allowMatchers = [];
112
171
  const allowHosts = new Set((options.allowHosts ?? []).map((h) => h.toLowerCase()));
113
172
  const maxRedirects = options.maxRedirects ?? 5;
@@ -117,28 +176,35 @@ export function fetchGuard(options = {}) {
117
176
  }
118
177
  const resolveFn = options.resolve ?? createDefaultResolver();
119
178
  for (const c of ALWAYS_DENY)
120
- denyMatchers.push(compileCidrMatcher(c));
121
- if (!options.allowLoopback)
179
+ hardDenyMatchers.push(compileCidrMatcher(c));
180
+ for (const c of options.denyAddresses ?? [])
181
+ hardDenyMatchers.push(compileCidrMatcher(c));
182
+ if (!options.allowLoopback) {
122
183
  for (const c of LOOPBACK)
123
- denyMatchers.push(compileCidrMatcher(c));
124
- if (!options.allowPrivate)
184
+ softDenyMatchers.push(compileCidrMatcher(c));
185
+ }
186
+ if (!options.allowPrivate) {
125
187
  for (const c of PRIVATE)
126
- denyMatchers.push(compileCidrMatcher(c));
127
- if (!options.allowLinkLocal)
188
+ softDenyMatchers.push(compileCidrMatcher(c));
189
+ }
190
+ if (!options.allowLinkLocal) {
128
191
  for (const c of LINK_LOCAL)
129
- denyMatchers.push(compileCidrMatcher(c));
192
+ softDenyMatchers.push(compileCidrMatcher(c));
193
+ }
130
194
  if (!options.allowUniqueLocal) {
131
195
  for (const c of UNIQUE_LOCAL)
132
- denyMatchers.push(compileCidrMatcher(c));
196
+ softDenyMatchers.push(compileCidrMatcher(c));
133
197
  }
134
- for (const c of options.denyAddresses ?? [])
135
- denyMatchers.push(compileCidrMatcher(c));
136
198
  for (const c of options.allowAddresses ?? [])
137
199
  allowMatchers.push(compileCidrMatcher(c));
138
200
  function isAddressAllowed(parsed) {
201
+ // Hard-deny wins over every allow knob — cloud metadata IPs and
202
+ // operator-pinned `denyAddresses` are non-negotiable.
203
+ if (hardDenyMatchers.some((m) => matchesMatcher(parsed, m)))
204
+ return false;
139
205
  if (allowMatchers.some((m) => matchesMatcher(parsed, m)))
140
206
  return true;
141
- return !denyMatchers.some((m) => matchesMatcher(parsed, m));
207
+ return !softDenyMatchers.some((m) => matchesMatcher(parsed, m));
142
208
  }
143
209
  async function validateUrl(url) {
144
210
  const proto = url.protocol.toLowerCase();
@@ -262,4 +328,3 @@ function createDefaultResolver() {
262
328
  return results.map((r) => r.address);
263
329
  };
264
330
  }
265
- //# sourceMappingURL=fetch-guard.js.map
package/dist/hashing.d.ts CHANGED
@@ -56,4 +56,3 @@ export declare function passwordHash(password: string): Promise<string>;
56
56
  * @since 0.15.0
57
57
  */
58
58
  export declare function passwordVerify(password: string, storedHash: string): Promise<boolean>;
59
- //# sourceMappingURL=hashing.d.ts.map
package/dist/hashing.js CHANGED
@@ -151,4 +151,3 @@ export async function passwordVerify(password, storedHash) {
151
151
  return false;
152
152
  return nodeTimingSafeEqual(derived, parsed.hash);
153
153
  }
154
- //# sourceMappingURL=hashing.js.map
package/dist/index.d.ts CHANGED
@@ -15,9 +15,9 @@ export { HttpError, BadRequestError, ValidationError, NotFoundError, Unauthorize
15
15
  export type { ProblemDetails, ProblemRenderOptions, HttpErrorOptions } from "./errors.js";
16
16
  export type { StandardSchemaV1 } from "./schema.js";
17
17
  export { validate, isStandardSchema } from "./schema.js";
18
- export { readBodyLimited, safeJsonParse, isForbiddenObjectKey, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, assertNoDuplicateSingletonHeaders, assertNoReservedInternalHeaders, RESERVED_INBOUND_HEADER_PREFIXES, SMUGGLING_SINGLETON_HEADERS, verifyWebhookSignature, signWebhookPayload, assertStrongSecret, MIN_PROD_SECRET_BYTES, WEAK_SECRET_STRINGS, } from "./security.js";
18
+ export { readBodyLimited, safeJsonParse, isForbiddenObjectKey, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, assertNoDuplicateSingletonHeaders, assertNoReservedInternalHeaders, RESERVED_INBOUND_HEADER_PREFIXES, SMUGGLING_SINGLETON_HEADERS, verifyWebhookSignature, signWebhookPayload, WEBHOOK_DEFAULT_TOLERANCE_SECONDS, assertStrongSecret, MIN_PROD_SECRET_BYTES, WEAK_SECRET_STRINGS, sanitizeFilename, assertSafeRelativePath, hasMongoOperatorKeys, assertNoMongoOperators, } from "./security.js";
19
19
  export type { WebhookHmacAlgorithm } from "./security.js";
20
- export { requestId, secureHeaders, SECURE_HEADERS_MARKER, cors, CORS_HOOK_MARKER, CORS_ORIGIN_ALLOW_MARKER, CORS_WILDCARD_ORIGIN_MARKER, rateLimit, loginThrottle, timing, bearerAuth, basicAuth, csrf, CSRF_HOOK_MARKER, requireScopes, REQUIRE_SCOPES_AGGREGATE_KEY, REQUIRE_SCOPES_HOOK_MARKER, _resetSharedRateLimitStoresForTests, } from "./middleware.js";
20
+ export { requestId, secureHeaders, SECURE_HEADERS_MARKER, cors, CORS_HOOK_MARKER, CORS_ORIGIN_ALLOW_MARKER, CORS_WILDCARD_ORIGIN_MARKER, rateLimit, loginThrottle, timing, bearerAuth, basicAuth, csrf, CSRF_HOOK_MARKER, fetchMetadata, requireScopes, REQUIRE_SCOPES_AGGREGATE_KEY, REQUIRE_SCOPES_HOOK_MARKER, _resetSharedRateLimitStoresForTests, } from "./middleware.js";
21
21
  export { etag } from "./etag.js";
22
22
  export type { ETagOptions } from "./etag.js";
23
23
  export { compression, COMPRESSION_HOOK_MARKER, _resetCompressionRuntimeProbeForTests, } from "./compression.js";
@@ -36,11 +36,13 @@ export { ipRestriction } from "./ip-restriction.js";
36
36
  export type { IpRestrictionOptions } from "./ip-restriction.js";
37
37
  export { fetchGuard, SsrfBlockedError } from "./fetch-guard.js";
38
38
  export type { FetchGuardOptions, SsrfBlockReason } from "./fetch-guard.js";
39
+ export { safeRedirect, OpenRedirectBlockedError } from "./safe-redirect.js";
40
+ export type { SafeRedirectOptions, SafeRedirectStatus, SafeRedirectBlockReason, } from "./safe-redirect.js";
39
41
  export { loadShedding, LOAD_SHEDDING_MARKER } from "./load-shedding.js";
40
42
  export type { LoadSheddingOptions, LoadSheddingSnapshot, } from "./load-shedding.js";
41
43
  export { defineConfig, ConfigValidationError } from "./config.js";
42
44
  export type { ConfigSource, DefineConfigOptions, } from "./config.js";
43
- export type { RequestIdOptions, SecureHeadersOptions, CspDirectivesOptions, CorsOptions, CorsOriginAllow, RateLimitOptions, RateLimitStore, LoginThrottleOptions, CsrfOptions, CsrfCookieOptions, BasicAuthOptions, } from "./middleware.js";
45
+ export type { RequestIdOptions, SecureHeadersOptions, CspDirectivesOptions, CorsOptions, CorsOriginAllow, RateLimitOptions, RateLimitStore, LoginThrottleOptions, CsrfOptions, CsrfCookieOptions, FetchMetadataOptions, BasicAuthOptions, } from "./middleware.js";
44
46
  export type { BearerAuthOptions, BearerAuthVerifyHook } from "./middleware.js";
45
47
  export { createLogger, noopLogger, DEFAULT_REDACT_KEYS } from "./logger.js";
46
48
  export type { Logger, LogLevel, ConsoleLoggerOptions, LoggerRedactionOptions, } from "./logger.js";
@@ -61,4 +63,3 @@ export { otelTracing, TRACING_SPAN_KIND_SERVER, TRACING_SPAN_STATUS_UNSET, TRACI
61
63
  export type { OtelTracingOptions, TracingAttributes, TracingAttributeValue, TracingSpan, TracingStartSpanOptions, TracingTracer, } from "./tracing.js";
62
64
  export { defineWebSocket, WebSocketRegistry, WebSocketProtocolError, WebSocketPayloadTooLargeError, WS_GUID, WS_READY_STATE, WS_OPCODE, WS_CLOSE_CODE, WS_MAX_CONTROL_PAYLOAD, DEFAULT_WS_BACKPRESSURE_LIMIT, DEFAULT_WS_MAX_PAYLOAD_LENGTH, DEFAULT_WS_IDLE_TIMEOUT_SECONDS, computeAcceptKey, parseSubprotocols, validateSelectedSubprotocol, validateUpgrade, checkWebSocketOrigin, parseFrame, encodeFrame, encodeClosePayload, decodeClosePayload, encodeSendPayload, normalizeWebSocketOptions, wsRateLimit, FrameSink, FRAME_INCOMPLETE, } from "./websocket.js";
63
65
  export type { WebSocketConnection, WebSocketContext, WebSocketHandler, WebSocketRouteEntry, NormalizedWebSocketOptions, WebSocketBeforeUpgrade, HandshakeResult, ParsedFrame, MessageEvent as WebSocketMessageEvent, FrameSinkEvents, } from "./websocket.js";
64
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ export { subdomains, PSL_SNAPSHOT_DATE, PSL_PUBLIC_SUFFIXES, MAX_SNAPSHOT_AGE_DA
8
8
  export { defineDependency, DEPENDENCY_MARKER } from "./dependency.js";
9
9
  export { HttpError, BadRequestError, ValidationError, NotFoundError, UnauthorizedError, ForbiddenError, MethodNotAllowedError, PayloadTooLargeError, UnsupportedMediaTypeError, TooManyRequestsError, RequestTimeoutError, InternalError, MessageLeakError, httpError, SAFE_CUSTOM_ERROR_RESPONSE_HEADERS, checkCustomErrorResponseHeaders, } from "./errors.js";
10
10
  export { validate, isStandardSchema } from "./schema.js";
11
- export { readBodyLimited, safeJsonParse, isForbiddenObjectKey, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, assertNoDuplicateSingletonHeaders, assertNoReservedInternalHeaders, RESERVED_INBOUND_HEADER_PREFIXES, SMUGGLING_SINGLETON_HEADERS, verifyWebhookSignature, signWebhookPayload, assertStrongSecret, MIN_PROD_SECRET_BYTES, WEAK_SECRET_STRINGS, } from "./security.js";
12
- export { requestId, secureHeaders, SECURE_HEADERS_MARKER, cors, CORS_HOOK_MARKER, CORS_ORIGIN_ALLOW_MARKER, CORS_WILDCARD_ORIGIN_MARKER, rateLimit, loginThrottle, timing, bearerAuth, basicAuth, csrf, CSRF_HOOK_MARKER, requireScopes, REQUIRE_SCOPES_AGGREGATE_KEY, REQUIRE_SCOPES_HOOK_MARKER, _resetSharedRateLimitStoresForTests, } from "./middleware.js";
11
+ export { readBodyLimited, safeJsonParse, isForbiddenObjectKey, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, assertNoDuplicateSingletonHeaders, assertNoReservedInternalHeaders, RESERVED_INBOUND_HEADER_PREFIXES, SMUGGLING_SINGLETON_HEADERS, verifyWebhookSignature, signWebhookPayload, WEBHOOK_DEFAULT_TOLERANCE_SECONDS, assertStrongSecret, MIN_PROD_SECRET_BYTES, WEAK_SECRET_STRINGS, sanitizeFilename, assertSafeRelativePath, hasMongoOperatorKeys, assertNoMongoOperators, } from "./security.js";
12
+ export { requestId, secureHeaders, SECURE_HEADERS_MARKER, cors, CORS_HOOK_MARKER, CORS_ORIGIN_ALLOW_MARKER, CORS_WILDCARD_ORIGIN_MARKER, rateLimit, loginThrottle, timing, bearerAuth, basicAuth, csrf, CSRF_HOOK_MARKER, fetchMetadata, requireScopes, REQUIRE_SCOPES_AGGREGATE_KEY, REQUIRE_SCOPES_HOOK_MARKER, _resetSharedRateLimitStoresForTests, } from "./middleware.js";
13
13
  export { etag } from "./etag.js";
14
14
  export { compression, COMPRESSION_HOOK_MARKER, _resetCompressionRuntimeProbeForTests, } from "./compression.js";
15
15
  export { createJwtSigner, createJwtVerifier, JwtError, DEFAULT_JWT_MAX_LIFETIME_SECONDS, } from "./jwt.js";
@@ -19,6 +19,7 @@ export { assertTemporalClaims, TemporalClaimError, } from "./time-claims.js";
19
19
  export { every, some, except } from "./combine.js";
20
20
  export { ipRestriction } from "./ip-restriction.js";
21
21
  export { fetchGuard, SsrfBlockedError } from "./fetch-guard.js";
22
+ export { safeRedirect, OpenRedirectBlockedError } from "./safe-redirect.js";
22
23
  export { loadShedding, LOAD_SHEDDING_MARKER } from "./load-shedding.js";
23
24
  export { defineConfig, ConfigValidationError } from "./config.js";
24
25
  export { createLogger, noopLogger, DEFAULT_REDACT_KEYS } from "./logger.js";
@@ -30,4 +31,3 @@ export { session, rotateSession, signValue, verifySignedValue, MemorySessionStor
30
31
  export { fileField, multipartObject, isFileFieldSchema, isMultipartObjectSchema, } from "./multipart.js";
31
32
  export { otelTracing, TRACING_SPAN_KIND_SERVER, TRACING_SPAN_STATUS_UNSET, TRACING_SPAN_STATUS_OK, TRACING_SPAN_STATUS_ERROR, } from "./tracing.js";
32
33
  export { defineWebSocket, WebSocketRegistry, WebSocketProtocolError, WebSocketPayloadTooLargeError, WS_GUID, WS_READY_STATE, WS_OPCODE, WS_CLOSE_CODE, WS_MAX_CONTROL_PAYLOAD, DEFAULT_WS_BACKPRESSURE_LIMIT, DEFAULT_WS_MAX_PAYLOAD_LENGTH, DEFAULT_WS_IDLE_TIMEOUT_SECONDS, computeAcceptKey, parseSubprotocols, validateSelectedSubprotocol, validateUpgrade, checkWebSocketOrigin, parseFrame, encodeFrame, encodeClosePayload, decodeClosePayload, encodeSendPayload, normalizeWebSocketOptions, wsRateLimit, FrameSink, FRAME_INCOMPLETE, } from "./websocket.js";
33
- //# sourceMappingURL=index.js.map
@@ -89,4 +89,3 @@ export declare function matchesMatcher(ip: ParsedIp, m: IpMatcher): boolean;
89
89
  export declare function compileCidrMatcher(input: string): IpMatcher;
90
90
  /** @internal */
91
91
  export declare function parseIp(input: string): ParsedIp | undefined;
92
- //# sourceMappingURL=ip-restriction.d.ts.map