@arcis/node 1.4.4 → 1.5.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/LICENSE +21 -0
- package/README.md +36 -6
- package/dist/astro/index.js +6141 -0
- package/dist/astro/index.js.map +1 -0
- package/dist/astro/index.mjs +6136 -0
- package/dist/astro/index.mjs.map +1 -0
- package/dist/bun/index.js +6195 -0
- package/dist/bun/index.js.map +1 -0
- package/dist/bun/index.mjs +6189 -0
- package/dist/bun/index.mjs.map +1 -0
- package/dist/core/constants.d.ts +3 -2
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/index.js +4 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +4 -3
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/types.d.ts +32 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/fastify/index.js +6160 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/fastify/index.mjs +6155 -0
- package/dist/fastify/index.mjs.map +1 -0
- package/dist/guards.d.ts +156 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/hono/index.js +6159 -0
- package/dist/hono/index.js.map +1 -0
- package/dist/hono/index.mjs +6154 -0
- package/dist/hono/index.mjs.map +1 -0
- package/dist/index.d.ts +23 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7126 -178
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7088 -179
- package/dist/index.mjs.map +1 -1
- package/dist/koa/index.js +6158 -0
- package/dist/koa/index.js.map +1 -0
- package/dist/koa/index.mjs +6153 -0
- package/dist/koa/index.mjs.map +1 -0
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/index.mjs.map +1 -1
- package/dist/logging/redactor.d.ts.map +1 -1
- package/dist/middleware/astro.d.ts +64 -0
- package/dist/middleware/astro.d.ts.map +1 -0
- package/dist/middleware/bot-detection.d.ts.map +1 -1
- package/dist/middleware/bun.d.ts +75 -0
- package/dist/middleware/bun.d.ts.map +1 -0
- package/dist/middleware/csrf.d.ts.map +1 -1
- package/dist/middleware/error-handler.d.ts.map +1 -1
- package/dist/middleware/fastify.d.ts +89 -0
- package/dist/middleware/fastify.d.ts.map +1 -0
- package/dist/middleware/graphql.d.ts +35 -0
- package/dist/middleware/graphql.d.ts.map +1 -0
- package/dist/middleware/hono.d.ts +63 -0
- package/dist/middleware/hono.d.ts.map +1 -0
- package/dist/middleware/index.d.ts +12 -0
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +6469 -119
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +6459 -120
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/middleware/koa.d.ts +84 -0
- package/dist/middleware/koa.d.ts.map +1 -0
- package/dist/middleware/main.d.ts +0 -30
- package/dist/middleware/main.d.ts.map +1 -1
- package/dist/middleware/mass-assign.d.ts +81 -0
- package/dist/middleware/mass-assign.d.ts.map +1 -0
- package/dist/middleware/method-allowlist.d.ts +66 -0
- package/dist/middleware/method-allowlist.d.ts.map +1 -0
- package/dist/middleware/nestjs.d.ts +62 -0
- package/dist/middleware/nestjs.d.ts.map +1 -0
- package/dist/middleware/nextjs.d.ts +102 -0
- package/dist/middleware/nextjs.d.ts.map +1 -0
- package/dist/middleware/nuxt.d.ts +61 -0
- package/dist/middleware/nuxt.d.ts.map +1 -0
- package/dist/middleware/overload.d.ts +92 -0
- package/dist/middleware/overload.d.ts.map +1 -0
- package/dist/middleware/protect.d.ts +91 -0
- package/dist/middleware/protect.d.ts.map +1 -0
- package/dist/middleware/rate-limit-sliding.d.ts.map +1 -1
- package/dist/middleware/rate-limit-token.d.ts.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/response-splitting.d.ts +83 -0
- package/dist/middleware/response-splitting.d.ts.map +1 -0
- package/dist/middleware/sveltekit.d.ts +68 -0
- package/dist/middleware/sveltekit.d.ts.map +1 -0
- package/dist/middleware/token-budget.d.ts +75 -0
- package/dist/middleware/token-budget.d.ts.map +1 -0
- package/dist/nestjs/index.js +1724 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/nestjs/index.mjs +1717 -0
- package/dist/nestjs/index.mjs.map +1 -0
- package/dist/nextjs/index.js +6184 -0
- package/dist/nextjs/index.js.map +1 -0
- package/dist/nextjs/index.mjs +6178 -0
- package/dist/nextjs/index.mjs.map +1 -0
- package/dist/nuxt/index.js +6141 -0
- package/dist/nuxt/index.js.map +1 -0
- package/dist/nuxt/index.mjs +6136 -0
- package/dist/nuxt/index.mjs.map +1 -0
- package/dist/sanitizers/encode.d.ts.map +1 -1
- package/dist/sanitizers/graphql.d.ts +72 -0
- package/dist/sanitizers/graphql.d.ts.map +1 -0
- package/dist/sanitizers/headers.d.ts +18 -0
- package/dist/sanitizers/headers.d.ts.map +1 -1
- package/dist/sanitizers/index.d.ts +4 -1
- package/dist/sanitizers/index.d.ts.map +1 -1
- package/dist/sanitizers/index.js +140 -66
- package/dist/sanitizers/index.js.map +1 -1
- package/dist/sanitizers/index.mjs +135 -67
- package/dist/sanitizers/index.mjs.map +1 -1
- package/dist/sanitizers/prompt-injection.d.ts +62 -0
- package/dist/sanitizers/prompt-injection.d.ts.map +1 -0
- package/dist/sanitizers/sanitize.d.ts +1 -1
- package/dist/sanitizers/sanitize.d.ts.map +1 -1
- package/dist/sanitizers/xpath.d.ts +37 -0
- package/dist/sanitizers/xpath.d.ts.map +1 -0
- package/dist/stores/index.js +4 -4
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/index.mjs +4 -4
- package/dist/stores/index.mjs.map +1 -1
- package/dist/stores/redis.d.ts +7 -1
- package/dist/stores/redis.d.ts.map +1 -1
- package/dist/sveltekit/index.js +6142 -0
- package/dist/sveltekit/index.js.map +1 -0
- package/dist/sveltekit/index.mjs +6137 -0
- package/dist/sveltekit/index.mjs.map +1 -0
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/index.js +137 -12
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/index.mjs +116 -13
- package/dist/validation/index.mjs.map +1 -1
- package/dist/validation/redirect.d.ts.map +1 -1
- package/dist/validation/schema.d.ts.map +1 -1
- package/dist/validation/url-async.d.ts +137 -0
- package/dist/validation/url-async.d.ts.map +1 -0
- package/package.json +52 -7
- package/scripts/postinstall.cjs +26 -0
- package/dist/cli/arcis.d.ts +0 -23
- package/dist/cli/arcis.d.ts.map +0 -1
- package/dist/cli/arcis.js +0 -312
- package/dist/cli/arcis.js.map +0 -1
- package/dist/cli/arcis.mjs +0 -309
- package/dist/cli/arcis.mjs.map +0 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/middleware/protect
|
|
3
|
+
*
|
|
4
|
+
* Composite protection helpers (issue #52). Pre-configured middleware
|
|
5
|
+
* stacks for the three endpoint shapes that show up in every app:
|
|
6
|
+
* login, signup, generic API. Each helper composes EXISTING middleware
|
|
7
|
+
* with sensible defaults; no new security logic lives here.
|
|
8
|
+
*
|
|
9
|
+
* Express supports passing an array of middleware to a route — the
|
|
10
|
+
* elements get unrolled in declaration order — so each helper returns
|
|
11
|
+
* a `RequestHandler[]` that drops directly into `app.post(...)`:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { protectLogin, protectSignup, protectApi } from '@arcis/node';
|
|
15
|
+
*
|
|
16
|
+
* app.post('/login', protectLogin(), loginHandler);
|
|
17
|
+
* app.post('/signup', protectSignup(), signupHandler);
|
|
18
|
+
* app.use ('/api', protectApi());
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Defaults (issue #52 spec):
|
|
22
|
+
*
|
|
23
|
+
* | Helper | rate-limit | bot | csrf | cors | sanitize | email |
|
|
24
|
+
* |---------------|------------|-----|------|------|----------|-------|
|
|
25
|
+
* | protectLogin | 5/min | yes | yes | - | yes | - |
|
|
26
|
+
* | protectSignup | 3/min | yes | - | - | yes | yes |
|
|
27
|
+
* | protectApi | 100/min | - | - | yes | yes | - |
|
|
28
|
+
*
|
|
29
|
+
* Each option is overridable. Pass `{ rateLimit: false }` to disable a
|
|
30
|
+
* specific layer; pass an options object to forward to the underlying
|
|
31
|
+
* factory.
|
|
32
|
+
*/
|
|
33
|
+
import type { RequestHandler } from 'express';
|
|
34
|
+
import { type BotProtectionOptions } from './bot-detection';
|
|
35
|
+
import { type CsrfOptions } from './csrf';
|
|
36
|
+
import type { CorsOptions } from './cors';
|
|
37
|
+
import { type SignupProtectionOptions } from './signup-protection';
|
|
38
|
+
import type { RateLimitOptions, SanitizeOptions } from '../core/types';
|
|
39
|
+
/**
|
|
40
|
+
* Per-layer override knob: pass `false` to disable, an options object
|
|
41
|
+
* to merge into the layer's defaults, or omit to accept the helper's
|
|
42
|
+
* baked-in default.
|
|
43
|
+
*/
|
|
44
|
+
type LayerOverride<T> = false | T | undefined;
|
|
45
|
+
export interface ProtectLoginOptions {
|
|
46
|
+
rateLimit?: LayerOverride<RateLimitOptions>;
|
|
47
|
+
bot?: LayerOverride<BotProtectionOptions>;
|
|
48
|
+
csrf?: LayerOverride<CsrfOptions>;
|
|
49
|
+
sanitize?: LayerOverride<SanitizeOptions>;
|
|
50
|
+
}
|
|
51
|
+
export interface ProtectSignupOptions {
|
|
52
|
+
rateLimit?: LayerOverride<RateLimitOptions>;
|
|
53
|
+
bot?: LayerOverride<BotProtectionOptions>;
|
|
54
|
+
sanitize?: LayerOverride<SanitizeOptions>;
|
|
55
|
+
/** signupProtection options (email-style validation, disposable-mail block, etc.). */
|
|
56
|
+
signup?: LayerOverride<SignupProtectionOptions>;
|
|
57
|
+
}
|
|
58
|
+
export interface ProtectApiOptions {
|
|
59
|
+
rateLimit?: LayerOverride<RateLimitOptions>;
|
|
60
|
+
/** CORS is required to take an Origin/Methods config — no default origin. */
|
|
61
|
+
cors?: LayerOverride<CorsOptions>;
|
|
62
|
+
sanitize?: LayerOverride<SanitizeOptions>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Login endpoints get the strictest defaults: 5 req/min/IP, deny
|
|
66
|
+
* AUTOMATED bots, CSRF token check, and input sanitization. Designed
|
|
67
|
+
* for `app.post('/login', protectLogin(), handler)`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function protectLogin(options?: ProtectLoginOptions): RequestHandler[];
|
|
70
|
+
/**
|
|
71
|
+
* Signup endpoints: 3 req/min/IP, deny AUTOMATED bots, sanitize input,
|
|
72
|
+
* and run signup-specific validation (email shape + disposable-domain
|
|
73
|
+
* check via `signupProtection`). No CSRF here because most signup
|
|
74
|
+
* forms are first-touch, no prior session to anchor a token to.
|
|
75
|
+
*/
|
|
76
|
+
export declare function protectSignup(options?: ProtectSignupOptions): RequestHandler[];
|
|
77
|
+
/**
|
|
78
|
+
* Generic API endpoints: 100 req/min/IP, CORS, input sanitization. No
|
|
79
|
+
* bot detection by default because legitimate API consumers (curl,
|
|
80
|
+
* fetch, server-to-server) are often classified AUTOMATED — opt-in
|
|
81
|
+
* by passing a `bot` override... wait, protectApi doesn't expose bot.
|
|
82
|
+
* That's deliberate per the issue spec table. Users who want bot
|
|
83
|
+
* detection on API endpoints compose `botProtection()` directly.
|
|
84
|
+
*
|
|
85
|
+
* CORS is the one layer with no usable default — every app's allow
|
|
86
|
+
* list is different. Pass `cors: { origin: '...' }` or `cors: false`
|
|
87
|
+
* to skip it explicitly.
|
|
88
|
+
*/
|
|
89
|
+
export declare function protectApi(options?: ProtectApiOptions): RequestHandler[];
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=protect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protect.d.ts","sourceRoot":"","sources":["../../src/middleware/protect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAoB,KAAK,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAErF,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEvE;;;;GAIG;AACH,KAAK,aAAa,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC5C,GAAG,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC1C,IAAI,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC5C,GAAG,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC1C,QAAQ,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC1C,sFAAsF;IACtF,MAAM,CAAC,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC5C,6EAA6E;IAC7E,IAAI,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CAC3C;AAaD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,cAAc,EAAE,CAoBhF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,cAAc,EAAE,CAoBlF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,EAAE,CAgB5E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit-sliding.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit-sliding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAA0B,cAAc,EAAE,MAAM,SAAS,CAAC;AAI/E,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;CAClC;AAOD,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC7D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,oBAAyB,GAAG,uBAAuB,
|
|
1
|
+
{"version":3,"file":"rate-limit-sliding.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit-sliding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAA0B,cAAc,EAAE,MAAM,SAAS,CAAC;AAI/E,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;CAClC;AAOD,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC7D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,oBAAyB,GAAG,uBAAuB,CAwGtG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit-token.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAA0B,cAAc,EAAE,MAAM,SAAS,CAAC;AAG/E,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;CAClC;AAOD,MAAM,WAAW,qBAAsB,SAAQ,cAAc;IAC3D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,kBAAuB,GAAG,qBAAqB,
|
|
1
|
+
{"version":3,"file":"rate-limit-token.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAA0B,cAAc,EAAE,MAAM,SAAS,CAAC;AAG/E,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;CAClC;AAOD,MAAM,WAAW,qBAAsB,SAAQ,cAAc;IAC3D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,kBAAuB,GAAG,qBAAqB,CA4FhG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAkB,MAAM,eAAe,CAAC;AAO7F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,gBAAqB,GAAG,qBAAqB,
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAkB,MAAM,eAAe,CAAC;AAO7F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,gBAAqB,GAAG,qBAAqB,CAwIvF;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,0BAAoB,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/middleware/response-splitting
|
|
3
|
+
*
|
|
4
|
+
* HTTP response splitting prevention (sdk-vectors.md tier 1 #27).
|
|
5
|
+
*
|
|
6
|
+
* Response splitting is the *output* counterpart to header injection: app
|
|
7
|
+
* code passes user input into `res.setHeader`, `res.writeHead`, or
|
|
8
|
+
* `res.appendHeader` (Node 17+) without stripping CR/LF, and an attacker
|
|
9
|
+
* uses the embedded newline to break out of the header block and forge a
|
|
10
|
+
* second response. Most often weaponised against `Location:` after a
|
|
11
|
+
* redirect that reflects user input (`/redirect?to=...`).
|
|
12
|
+
*
|
|
13
|
+
* `sanitizeHeaderValue` already covers the byte-level fix on the way in;
|
|
14
|
+
* this middleware wraps the response object so every header that leaves
|
|
15
|
+
* the app gets sanitised on the way out — even when the app forgets.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { responseSplittingGuard } from '@arcis/node/middleware/response-splitting';
|
|
19
|
+
*
|
|
20
|
+
* app.use(responseSplittingGuard());
|
|
21
|
+
*
|
|
22
|
+
* // Later — even this passes through clean:
|
|
23
|
+
* app.get('/r', (req, res) => res.redirect(req.query.to as string));
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Pair with `validateRedirect` for full coverage: this middleware blocks
|
|
27
|
+
* the response-splitting payload, `validateRedirect` blocks the
|
|
28
|
+
* open-redirect payload.
|
|
29
|
+
*/
|
|
30
|
+
import type { RequestHandler } from 'express';
|
|
31
|
+
import { detectHeaderInjection, sanitizeHeaderValue } from '../sanitizers/headers';
|
|
32
|
+
export interface ResponseSplittingGuardOptions {
|
|
33
|
+
/**
|
|
34
|
+
* What to do when an outgoing header value contains CR / LF / NUL.
|
|
35
|
+
*
|
|
36
|
+
* - `'strip'` (default) — silently sanitise the value before it reaches
|
|
37
|
+
* the wire. Preserves availability; existing routes don't break.
|
|
38
|
+
* - `'reject'` — throw a `ResponseSplittingError`. Use in apps that
|
|
39
|
+
* would rather fail-closed than emit a partial response.
|
|
40
|
+
*
|
|
41
|
+
* Both modes invoke `onDetect` if provided.
|
|
42
|
+
*/
|
|
43
|
+
mode?: 'strip' | 'reject';
|
|
44
|
+
/**
|
|
45
|
+
* Per-detection callback. Fires before strip/reject. Useful for
|
|
46
|
+
* logging or alerting when an attempted split slips through into the
|
|
47
|
+
* response builder.
|
|
48
|
+
*/
|
|
49
|
+
onDetect?: (header: string, originalValue: string) => void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Thrown by `responseSplittingGuard({ mode: 'reject' })` when an
|
|
53
|
+
* outgoing header value contains CR / LF / NUL. The header name is in
|
|
54
|
+
* `header`; the originally attempted value is in `value` so it can be
|
|
55
|
+
* logged or surfaced in an error handler.
|
|
56
|
+
*/
|
|
57
|
+
export declare class ResponseSplittingError extends Error {
|
|
58
|
+
readonly header: string;
|
|
59
|
+
readonly value: string;
|
|
60
|
+
constructor(header: string, value: string);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Re-export under the response-splitting name. Same byte pattern as
|
|
64
|
+
* header injection (CR / LF / NUL) — different threat model: input
|
|
65
|
+
* boundary vs output boundary.
|
|
66
|
+
*/
|
|
67
|
+
export declare const detectResponseSplitting: typeof detectHeaderInjection;
|
|
68
|
+
/**
|
|
69
|
+
* Re-export under the response-splitting name. Strips CR / LF / NUL.
|
|
70
|
+
*/
|
|
71
|
+
export declare const sanitizeResponseHeader: typeof sanitizeHeaderValue;
|
|
72
|
+
/**
|
|
73
|
+
* Build the response-splitting guard middleware. Wraps `res.setHeader`,
|
|
74
|
+
* `res.writeHead`, and `res.appendHeader` (when present) on each
|
|
75
|
+
* incoming request so every header that leaves the app gets the same
|
|
76
|
+
* CRLF / NUL treatment regardless of which code path emitted it.
|
|
77
|
+
*
|
|
78
|
+
* Wrapping happens per-request (not on the prototype) so multiple
|
|
79
|
+
* mounts with different options don't trample each other.
|
|
80
|
+
*/
|
|
81
|
+
export declare function responseSplittingGuard(options?: ResponseSplittingGuardOptions): RequestHandler;
|
|
82
|
+
export default responseSplittingGuard;
|
|
83
|
+
//# sourceMappingURL=response-splitting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-splitting.d.ts","sourceRoot":"","sources":["../../src/middleware/response-splitting.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAY,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAE1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5D;AAED;;;;;GAKG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAM1C;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,8BAAwB,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,sBAAsB,4BAAsB,CAAC;AAiB1D;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC,GAC1C,cAAc,CAoHhB;AAED,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/sveltekit
|
|
3
|
+
*
|
|
4
|
+
* SvelteKit adapter for Arcis. Returns a `Handle` factory you can drop into
|
|
5
|
+
* `src/hooks.server.ts`:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { arcisHandle } from '@arcis/node/sveltekit';
|
|
9
|
+
* export const handle = arcisHandle({ rateLimit: { max: 100 }, bot: true });
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Or compose with other handles via SvelteKit's own `sequence` helper:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { sequence } from '@sveltejs/kit/hooks';
|
|
16
|
+
* import { arcisHandle } from '@arcis/node/sveltekit';
|
|
17
|
+
* export const handle = sequence(arcisHandle(), authHandle, loggingHandle);
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* SvelteKit uses Web Fetch `Request`/`Response` objects, not Express
|
|
21
|
+
* `req`/`res`, so this adapter implements the Arcis pipeline natively against
|
|
22
|
+
* the Fetch API rather than wrapping `arcis()`. There is no runtime dependency
|
|
23
|
+
* on `@sveltejs/kit` — its types are imported only for compile-time checks.
|
|
24
|
+
*/
|
|
25
|
+
import type { HeaderOptions, RateLimitOptions } from '../core/types';
|
|
26
|
+
import { type BotProtectionOptions } from './bot-detection';
|
|
27
|
+
interface SvelteKitCookies {
|
|
28
|
+
get(name: string): string | undefined;
|
|
29
|
+
set(name: string, value: string, opts: {
|
|
30
|
+
path: string;
|
|
31
|
+
[k: string]: unknown;
|
|
32
|
+
}): void;
|
|
33
|
+
delete(name: string, opts: {
|
|
34
|
+
path: string;
|
|
35
|
+
}): void;
|
|
36
|
+
}
|
|
37
|
+
export interface SvelteKitRequestEvent {
|
|
38
|
+
request: Request;
|
|
39
|
+
url: URL;
|
|
40
|
+
cookies: SvelteKitCookies;
|
|
41
|
+
getClientAddress(): string;
|
|
42
|
+
}
|
|
43
|
+
export type SvelteKitResolve = (event: SvelteKitRequestEvent, opts?: unknown) => Promise<Response> | Response;
|
|
44
|
+
export type SvelteKitHandle = (input: {
|
|
45
|
+
event: SvelteKitRequestEvent;
|
|
46
|
+
resolve: SvelteKitResolve;
|
|
47
|
+
}) => Promise<Response>;
|
|
48
|
+
export interface ArcisHandleOptions {
|
|
49
|
+
/** Security headers configuration. Default: enabled. Pass `false` to disable. */
|
|
50
|
+
headers?: boolean | HeaderOptions;
|
|
51
|
+
/** Rate limiter configuration. Default: 100 req/60s in-memory. Pass `false` to disable. */
|
|
52
|
+
rateLimit?: boolean | RateLimitOptions;
|
|
53
|
+
/**
|
|
54
|
+
* Bot protection. Default: disabled (opt-in to avoid surprising behavior on
|
|
55
|
+
* legitimate crawlers). Pass `true` for sensible defaults or an options
|
|
56
|
+
* object for full control.
|
|
57
|
+
*/
|
|
58
|
+
bot?: boolean | BotProtectionOptions;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build a SvelteKit `Handle` that applies Arcis protections in this order on
|
|
62
|
+
* each request: rate limit (returns 429 if exceeded), bot detection (returns
|
|
63
|
+
* 403 if the bot is in the deny list), then runs downstream `resolve(event)`,
|
|
64
|
+
* then mutates the resulting `Response`'s headers with security defaults.
|
|
65
|
+
*/
|
|
66
|
+
export declare function arcisHandle(options?: ArcisHandleOptions): SvelteKitHandle;
|
|
67
|
+
export default arcisHandle;
|
|
68
|
+
//# sourceMappingURL=sveltekit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sveltekit.d.ts","sourceRoot":"","sources":["../../src/middleware/sveltekit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EACV,aAAa,EAEb,gBAAgB,EACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,iBAAiB,CAAC;AAMzB,UAAU,gBAAgB;IACxB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACrF,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,gBAAgB,CAAC;IAC1B,gBAAgB,IAAI,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,gBAAgB,GAAG,CAC7B,KAAK,EAAE,qBAAqB,EAC5B,IAAI,CAAC,EAAE,OAAO,KACX,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;AAElC,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE;IACpC,KAAK,EAAE,qBAAqB,CAAC;IAC7B,OAAO,EAAE,gBAAgB,CAAC;CAC3B,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAIxB,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;IAClC,2FAA2F;IAC3F,SAAS,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvC;;;;OAIG;IACH,GAAG,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAC;CACtC;AAgID;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,eAAe,CAqE7E;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/middleware/token-budget
|
|
3
|
+
*
|
|
4
|
+
* Token-budget protection middleware. Caps per-key token spend over a
|
|
5
|
+
* sliding window — meant for routes that proxy LLM calls, where a tight
|
|
6
|
+
* 100-req/min rate limit isn't enough because a single 50KB prompt costs
|
|
7
|
+
* the same as 1000 small requests.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import express from 'express';
|
|
11
|
+
* import { tokenBudget } from '@arcis/node';
|
|
12
|
+
*
|
|
13
|
+
* const guard = tokenBudget({
|
|
14
|
+
* maxTokens: 100_000, // 100K tokens per window
|
|
15
|
+
* windowMs: 60 * 60 * 1000, // 1-hour window
|
|
16
|
+
* maxRequestTokens: 5_000, // reject any single request over this
|
|
17
|
+
* keyGenerator: (req) => req.user?.id ?? req.ip,
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* app.post('/chat', guard, chatHandler);
|
|
21
|
+
*
|
|
22
|
+
* The default token estimator is `Math.ceil(bytesOf(body+query) / 4)` —
|
|
23
|
+
* close to OpenAI's "1 token ≈ 4 English characters" rule. Override via
|
|
24
|
+
* `estimateTokens` for accurate counting (tiktoken, etc.).
|
|
25
|
+
*/
|
|
26
|
+
import type { Request, RequestHandler } from 'express';
|
|
27
|
+
export interface TokenBudgetOptions {
|
|
28
|
+
/** Max tokens a single key can spend in one window. Default: 100,000. */
|
|
29
|
+
maxTokens?: number;
|
|
30
|
+
/** Window length in milliseconds. Default: 60 * 60 * 1000 (1 hour). */
|
|
31
|
+
windowMs?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Max tokens a single request may consume. Requests over this size are
|
|
34
|
+
* rejected with 413 BEFORE counting against the window budget. Default:
|
|
35
|
+
* unset (no per-request cap).
|
|
36
|
+
*/
|
|
37
|
+
maxRequestTokens?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Function that returns the budget key for a request. Default: client IP
|
|
40
|
+
* (req.ip), falling back to `unknown` when unresolvable.
|
|
41
|
+
*/
|
|
42
|
+
keyGenerator?: (req: Request) => string;
|
|
43
|
+
/**
|
|
44
|
+
* Function that estimates the number of tokens a request will consume.
|
|
45
|
+
* Default: `Math.ceil((req.body + req.query stringified bytes) / 4)`,
|
|
46
|
+
* which approximates OpenAI's "1 token ≈ 4 characters" rule. Override
|
|
47
|
+
* with tiktoken/accurate counting when you need precision.
|
|
48
|
+
*/
|
|
49
|
+
estimateTokens?: (req: Request) => number;
|
|
50
|
+
/** Status code for budget-exceeded responses. Default: 429. */
|
|
51
|
+
statusCode?: number;
|
|
52
|
+
/** Status code for oversize-request rejections. Default: 413. */
|
|
53
|
+
statusCodeOversize?: number;
|
|
54
|
+
/** Error message when budget is exceeded. */
|
|
55
|
+
message?: string;
|
|
56
|
+
/** Error message when a single request exceeds maxRequestTokens. */
|
|
57
|
+
messageOversize?: string;
|
|
58
|
+
/** Skip budget enforcement for certain requests. */
|
|
59
|
+
skip?: (req: Request) => boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface TokenBudgetMiddleware extends RequestHandler {
|
|
62
|
+
/** Release internal cleanup resources. */
|
|
63
|
+
close: () => void;
|
|
64
|
+
/** Inspect current usage for a key (read-only; for tests/telemetry). */
|
|
65
|
+
inspect: (key: string) => {
|
|
66
|
+
used: number;
|
|
67
|
+
resetTime: number;
|
|
68
|
+
} | null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Build a token-budget middleware. See module-level JSDoc for usage.
|
|
72
|
+
*/
|
|
73
|
+
export declare function tokenBudget(options?: TokenBudgetOptions): TokenBudgetMiddleware;
|
|
74
|
+
export default tokenBudget;
|
|
75
|
+
//# sourceMappingURL=token-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-budget.d.ts","sourceRoot":"","sources":["../../src/middleware/token-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IAC1C,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,qBAAsB,SAAQ,cAAc;IAC3D,0CAA0C;IAC1C,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,wEAAwE;IACxE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACtE;AAwCD;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,qBAAqB,CAkGnF;AAED,eAAe,WAAW,CAAC"}
|