@arcis/node 1.4.4 → 1.5.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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -6
  3. package/dist/astro/index.js +6141 -0
  4. package/dist/astro/index.js.map +1 -0
  5. package/dist/astro/index.mjs +6136 -0
  6. package/dist/astro/index.mjs.map +1 -0
  7. package/dist/bun/index.js +6195 -0
  8. package/dist/bun/index.js.map +1 -0
  9. package/dist/bun/index.mjs +6189 -0
  10. package/dist/bun/index.mjs.map +1 -0
  11. package/dist/core/constants.d.ts +3 -2
  12. package/dist/core/constants.d.ts.map +1 -1
  13. package/dist/core/index.js +4 -3
  14. package/dist/core/index.js.map +1 -1
  15. package/dist/core/index.mjs +4 -3
  16. package/dist/core/index.mjs.map +1 -1
  17. package/dist/core/types.d.ts +32 -0
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/fastify/index.js +6160 -0
  20. package/dist/fastify/index.js.map +1 -0
  21. package/dist/fastify/index.mjs +6155 -0
  22. package/dist/fastify/index.mjs.map +1 -0
  23. package/dist/guards.d.ts +156 -0
  24. package/dist/guards.d.ts.map +1 -0
  25. package/dist/hono/index.js +6159 -0
  26. package/dist/hono/index.js.map +1 -0
  27. package/dist/hono/index.mjs +6154 -0
  28. package/dist/hono/index.mjs.map +1 -0
  29. package/dist/index.d.ts +23 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +7126 -178
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +7088 -179
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/koa/index.js +6158 -0
  36. package/dist/koa/index.js.map +1 -0
  37. package/dist/koa/index.mjs +6153 -0
  38. package/dist/koa/index.mjs.map +1 -0
  39. package/dist/logging/index.js.map +1 -1
  40. package/dist/logging/index.mjs.map +1 -1
  41. package/dist/logging/redactor.d.ts.map +1 -1
  42. package/dist/middleware/astro.d.ts +64 -0
  43. package/dist/middleware/astro.d.ts.map +1 -0
  44. package/dist/middleware/bot-detection.d.ts.map +1 -1
  45. package/dist/middleware/bun.d.ts +75 -0
  46. package/dist/middleware/bun.d.ts.map +1 -0
  47. package/dist/middleware/csrf.d.ts.map +1 -1
  48. package/dist/middleware/error-handler.d.ts.map +1 -1
  49. package/dist/middleware/fastify.d.ts +89 -0
  50. package/dist/middleware/fastify.d.ts.map +1 -0
  51. package/dist/middleware/graphql.d.ts +35 -0
  52. package/dist/middleware/graphql.d.ts.map +1 -0
  53. package/dist/middleware/hono.d.ts +63 -0
  54. package/dist/middleware/hono.d.ts.map +1 -0
  55. package/dist/middleware/index.d.ts +12 -0
  56. package/dist/middleware/index.d.ts.map +1 -1
  57. package/dist/middleware/index.js +6469 -119
  58. package/dist/middleware/index.js.map +1 -1
  59. package/dist/middleware/index.mjs +6459 -120
  60. package/dist/middleware/index.mjs.map +1 -1
  61. package/dist/middleware/koa.d.ts +84 -0
  62. package/dist/middleware/koa.d.ts.map +1 -0
  63. package/dist/middleware/main.d.ts +0 -30
  64. package/dist/middleware/main.d.ts.map +1 -1
  65. package/dist/middleware/mass-assign.d.ts +81 -0
  66. package/dist/middleware/mass-assign.d.ts.map +1 -0
  67. package/dist/middleware/method-allowlist.d.ts +66 -0
  68. package/dist/middleware/method-allowlist.d.ts.map +1 -0
  69. package/dist/middleware/nestjs.d.ts +62 -0
  70. package/dist/middleware/nestjs.d.ts.map +1 -0
  71. package/dist/middleware/nextjs.d.ts +102 -0
  72. package/dist/middleware/nextjs.d.ts.map +1 -0
  73. package/dist/middleware/nuxt.d.ts +61 -0
  74. package/dist/middleware/nuxt.d.ts.map +1 -0
  75. package/dist/middleware/overload.d.ts +92 -0
  76. package/dist/middleware/overload.d.ts.map +1 -0
  77. package/dist/middleware/protect.d.ts +91 -0
  78. package/dist/middleware/protect.d.ts.map +1 -0
  79. package/dist/middleware/rate-limit-sliding.d.ts.map +1 -1
  80. package/dist/middleware/rate-limit-token.d.ts.map +1 -1
  81. package/dist/middleware/rate-limit.d.ts.map +1 -1
  82. package/dist/middleware/response-splitting.d.ts +83 -0
  83. package/dist/middleware/response-splitting.d.ts.map +1 -0
  84. package/dist/middleware/sveltekit.d.ts +68 -0
  85. package/dist/middleware/sveltekit.d.ts.map +1 -0
  86. package/dist/middleware/token-budget.d.ts +75 -0
  87. package/dist/middleware/token-budget.d.ts.map +1 -0
  88. package/dist/nestjs/index.js +1724 -0
  89. package/dist/nestjs/index.js.map +1 -0
  90. package/dist/nestjs/index.mjs +1717 -0
  91. package/dist/nestjs/index.mjs.map +1 -0
  92. package/dist/nextjs/index.js +6184 -0
  93. package/dist/nextjs/index.js.map +1 -0
  94. package/dist/nextjs/index.mjs +6178 -0
  95. package/dist/nextjs/index.mjs.map +1 -0
  96. package/dist/nuxt/index.js +6141 -0
  97. package/dist/nuxt/index.js.map +1 -0
  98. package/dist/nuxt/index.mjs +6136 -0
  99. package/dist/nuxt/index.mjs.map +1 -0
  100. package/dist/sanitizers/encode.d.ts.map +1 -1
  101. package/dist/sanitizers/graphql.d.ts +72 -0
  102. package/dist/sanitizers/graphql.d.ts.map +1 -0
  103. package/dist/sanitizers/headers.d.ts +18 -0
  104. package/dist/sanitizers/headers.d.ts.map +1 -1
  105. package/dist/sanitizers/index.d.ts +4 -1
  106. package/dist/sanitizers/index.d.ts.map +1 -1
  107. package/dist/sanitizers/index.js +140 -66
  108. package/dist/sanitizers/index.js.map +1 -1
  109. package/dist/sanitizers/index.mjs +135 -67
  110. package/dist/sanitizers/index.mjs.map +1 -1
  111. package/dist/sanitizers/prompt-injection.d.ts +62 -0
  112. package/dist/sanitizers/prompt-injection.d.ts.map +1 -0
  113. package/dist/sanitizers/sanitize.d.ts +1 -1
  114. package/dist/sanitizers/sanitize.d.ts.map +1 -1
  115. package/dist/sanitizers/xpath.d.ts +37 -0
  116. package/dist/sanitizers/xpath.d.ts.map +1 -0
  117. package/dist/stores/index.js +4 -4
  118. package/dist/stores/index.js.map +1 -1
  119. package/dist/stores/index.mjs +4 -4
  120. package/dist/stores/index.mjs.map +1 -1
  121. package/dist/stores/redis.d.ts +7 -1
  122. package/dist/stores/redis.d.ts.map +1 -1
  123. package/dist/sveltekit/index.js +6142 -0
  124. package/dist/sveltekit/index.js.map +1 -0
  125. package/dist/sveltekit/index.mjs +6137 -0
  126. package/dist/sveltekit/index.mjs.map +1 -0
  127. package/dist/validation/index.d.ts +2 -0
  128. package/dist/validation/index.d.ts.map +1 -1
  129. package/dist/validation/index.js +137 -12
  130. package/dist/validation/index.js.map +1 -1
  131. package/dist/validation/index.mjs +116 -13
  132. package/dist/validation/index.mjs.map +1 -1
  133. package/dist/validation/redirect.d.ts.map +1 -1
  134. package/dist/validation/schema.d.ts.map +1 -1
  135. package/dist/validation/url-async.d.ts +137 -0
  136. package/dist/validation/url-async.d.ts.map +1 -0
  137. package/package.json +57 -12
  138. package/scripts/postinstall.cjs +26 -0
  139. package/dist/cli/arcis.d.ts +0 -23
  140. package/dist/cli/arcis.d.ts.map +0 -1
  141. package/dist/cli/arcis.js +0 -312
  142. package/dist/cli/arcis.js.map +0 -1
  143. package/dist/cli/arcis.mjs +0 -309
  144. package/dist/cli/arcis.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/sanitizers/encode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBxD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAwBjD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAkBlD"}
1
+ {"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/sanitizers/encode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBxD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAyBjD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAkBlD"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @module @arcis/node/sanitizers/graphql
3
+ * GraphQL injection prevention (sdk-vectors.md tier 1 #21).
4
+ *
5
+ * Two threats covered:
6
+ *
7
+ * 1. **Depth-bomb DoS** — nested-query payloads like
8
+ * `query { x { x { x { ... } } } }` to ridiculous depth that explode
9
+ * resolver work (each `{` typically maps to a database round-trip).
10
+ * Even a 50-deep query against a real schema can hammer the
11
+ * backend; 1000-deep crashes the resolver entirely.
12
+ *
13
+ * 2. **Introspection abuse** — `__schema` / `__type` / `__typename`
14
+ * queries that let an attacker enumerate the entire schema, then
15
+ * use that map to find sensitive fields, deprecated mutations,
16
+ * or unprotected admin paths. Production GraphQL endpoints should
17
+ * disable introspection by default.
18
+ *
19
+ * v1 is regex-based: count `{` / `}` for nesting depth (no escape
20
+ * handling — strings inside the query that contain `{` will
21
+ * over-count). False positives are an acceptable tradeoff for v1
22
+ * because (a) the depth threshold is well above legitimate query
23
+ * shapes, (b) a real GraphQL parser pulls in `graphql` as a runtime
24
+ * dep — significant for a sanitizer that ships in every Arcis
25
+ * install. Customers running queries near the threshold can either
26
+ * raise `maxDepth` or bring their own AST pre-pass.
27
+ *
28
+ * NOT included in v1:
29
+ * - Field-count limit (some servers have this; orthogonal to depth)
30
+ * - Alias-bomb detection (`q { f1: foo, f2: foo, ...}` — easier as
31
+ * a length-check than a parse)
32
+ * - Variable rebinding attacks
33
+ *
34
+ * Each is a follow-up if customers ask. Documented inline.
35
+ */
36
+ export interface GraphqlGuardOptions {
37
+ /** Maximum allowed nesting depth. Default: 10. Most legit queries are <8. */
38
+ maxDepth?: number;
39
+ /** Maximum query string length in characters. Default: 10000. */
40
+ maxLength?: number;
41
+ /**
42
+ * Block introspection queries (`__schema`, `__type`). Default: true.
43
+ * Set `false` in development if you rely on GraphiQL / Apollo
44
+ * Studio. Production should leave this on.
45
+ */
46
+ blockIntrospection?: boolean;
47
+ }
48
+ export type GraphqlViolation = 'depth' | 'length' | 'introspection';
49
+ export interface GraphqlGuardResult {
50
+ /** True if the query violated any configured limit. */
51
+ blocked: boolean;
52
+ /** Which limit fired first (depth → introspection → length precedence). */
53
+ reason?: GraphqlViolation;
54
+ /** Observed nesting depth. Always returned, even on clean queries. */
55
+ depth: number;
56
+ /** Observed length. Always returned. */
57
+ length: number;
58
+ }
59
+ /**
60
+ * Inspect a GraphQL query against the configured limits. Returns a
61
+ * structured result; the middleware below uses this directly. Pure
62
+ * function — no I/O, no res handle.
63
+ */
64
+ export declare function inspectGraphqlQuery(query: string, options?: GraphqlGuardOptions): GraphqlGuardResult;
65
+ /**
66
+ * Detect-only API matching the rest of the sanitizer module surface
67
+ * (`detectXss` / `detectSql` / `detectXxe` / etc.). Returns a boolean
68
+ * for callers that just want a yes/no — use `inspectGraphqlQuery` if
69
+ * you need the structured reason.
70
+ */
71
+ export declare function detectGraphqlAbuse(query: string, options?: GraphqlGuardOptions): boolean;
72
+ //# sourceMappingURL=graphql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/sanitizers/graphql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,QAAQ,GAAG,eAAe,CAAC;AAEpE,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB;AA2CD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,GAChC,kBAAkB,CAwBpB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAGT"}
@@ -43,4 +43,22 @@ export declare function sanitizeHeaders(headers: Record<string, string>): Record
43
43
  * @returns True if header injection patterns detected
44
44
  */
45
45
  export declare function detectHeaderInjection(input: string): boolean;
46
+ /**
47
+ * Email-header injection prevention. Same byte-level threat as HTTP
48
+ * header injection — `\r\n` in a user-controlled email field
49
+ * (`To`, `From`, `Subject`, etc.) lets an attacker inject extra headers
50
+ * (most commonly Bcc) and pivot a contact form into a spam relay.
51
+ *
52
+ * Aliased to the HTTP-header sanitizers because the wire-level fix is
53
+ * identical: strip CRLF + null bytes from the value before
54
+ * concatenating into the header. Use these in form-to-email handlers:
55
+ *
56
+ * ```ts
57
+ * const subject = sanitizeEmailHeader(req.body.subject);
58
+ * const to = sanitizeEmailHeader(req.body.to);
59
+ * if (detectEmailHeaderInjection(req.body.to)) reject(...);
60
+ * ```
61
+ */
62
+ export declare const sanitizeEmailHeader: typeof sanitizeHeaderValue;
63
+ export declare const detectEmailHeaderInjection: typeof detectHeaderInjection;
46
64
  //# sourceMappingURL=headers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/sanitizers/headers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,eAAe,CAAC;AAUhE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;AACnF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,GAAG,cAAc,CAAC;AAuCzF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcvF;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAK5D"}
1
+ {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/sanitizers/headers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,eAAe,CAAC;AAUhE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;AACnF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,GAAG,cAAc,CAAC;AAuCzF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcvF;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAK5D;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,4BAAsB,CAAC;AACvD,eAAO,MAAM,0BAA0B,8BAAwB,CAAC"}
@@ -13,9 +13,12 @@ export { isDangerousProtoKey, detectPrototypePollution, getDangerousProtoKeys }
13
13
  export { sanitizeSsti, detectSsti } from './ssti';
14
14
  export { sanitizeXxe, detectXxe } from './xxe';
15
15
  export { sanitizeJsonpCallback, detectJsonpInjection } from './jsonp';
16
- export { sanitizeHeaderValue, sanitizeHeaders, detectHeaderInjection } from './headers';
16
+ export { sanitizeHeaderValue, sanitizeHeaders, detectHeaderInjection, sanitizeEmailHeader, detectEmailHeaderInjection, } from './headers';
17
17
  export { scanPii, detectPii, redactPii, scanObjectPii, redactObjectPii } from './pii';
18
18
  export { encodeForHtml, encodeForAttribute, encodeForJs, encodeForUrl, encodeForCss } from './encode';
19
19
  export { sanitizeLdapFilter, sanitizeLdapDn, detectLdapInjection } from './ldap';
20
+ export { sanitizeXpath, detectXpathInjection } from './xpath';
21
+ export { inspectGraphqlQuery, detectGraphqlAbuse, } from './graphql';
22
+ export type { GraphqlGuardOptions, GraphqlGuardResult, GraphqlViolation, } from './graphql';
20
23
  export { encodeHtmlEntities, isPlainObject } from './utils';
21
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sanitizers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1F,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAGpE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAG3F,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGnG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAG/C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAGxF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAGtF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGtG,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAGjF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sanitizers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1F,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAGpE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAG3F,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGnG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAG/C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAGtF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGtG,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAGjF,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAG9D,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
@@ -67,8 +67,8 @@ var XSS_REMOVE_PATTERNS = [
67
67
  /** javascript: and vbscript: protocols (allow optional spaces before colon) */
68
68
  /javascript\s*:/gi,
69
69
  /vbscript\s*:/gi,
70
- /** data: URIs with HTML/script content */
71
- /data\s*:\s*text\/html[^>\s]*/gi,
70
+ /** data: URIs with HTML or SVG content (SVG can run JS via inline event handlers) */
71
+ /data\s*:\s*(?:text\/html|image\/svg)[^>\s]*/gi,
72
72
  /** form tag injection — phishing via action= redirection */
73
73
  /<form[\s>][^>]*/gi,
74
74
  /** meta tag injection — http-equiv refresh or CSP bypass */
@@ -593,6 +593,89 @@ function detectXxe(input) {
593
593
  return false;
594
594
  }
595
595
 
596
+ // src/sanitizers/ldap.ts
597
+ var LDAP_FILTER_CHARS = /[*()\\\x00]/g;
598
+ var LDAP_DN_CHARS = /[,+<>;"=\/\\\x00*()\x00]/g;
599
+ var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
600
+ var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
601
+ var escapeChar = (char) => "\\" + char.charCodeAt(0).toString(16).padStart(2, "0");
602
+ function sanitizeLdapFilter(input) {
603
+ if (typeof input !== "string") return String(input);
604
+ return input.replace(LDAP_FILTER_CHARS, escapeChar);
605
+ }
606
+ function sanitizeLdapDn(input) {
607
+ if (typeof input !== "string") return String(input);
608
+ return input.replace(LDAP_DN_CHARS, escapeChar);
609
+ }
610
+ function detectLdapInjection(input) {
611
+ if (typeof input !== "string") return false;
612
+ return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
613
+ }
614
+
615
+ // src/sanitizers/xpath.ts
616
+ var XPATH_INJECTION_CHARS = /['"|,()]/;
617
+ var XPATH_INJECTION_PATTERN = /('\s*(or|and)\s*'|"\s*(or|and)\s*"|\)\s*(or|and)\s*\(|\|\s*\/)/i;
618
+ function detectXpathInjection(input) {
619
+ if (typeof input !== "string" || input.length === 0) return false;
620
+ if (!XPATH_INJECTION_CHARS.test(input)) return false;
621
+ return XPATH_INJECTION_PATTERN.test(input);
622
+ }
623
+ function sanitizeXpath(input) {
624
+ if (typeof input !== "string") return String(input);
625
+ return input.replace(/['"|,]/g, "");
626
+ }
627
+
628
+ // src/sanitizers/headers.ts
629
+ var HEADER_INJECTION_PATTERN = /\r\n|\r|\n|\0/g;
630
+ function sanitizeHeaderValue(input, collectThreats = false) {
631
+ if (typeof input !== "string") {
632
+ return collectThreats ? { value: String(input), wasSanitized: false, threats: [] } : String(input);
633
+ }
634
+ const threats = [];
635
+ let wasSanitized = false;
636
+ if (HEADER_INJECTION_PATTERN.test(input)) {
637
+ HEADER_INJECTION_PATTERN.lastIndex = 0;
638
+ wasSanitized = true;
639
+ if (collectThreats) {
640
+ const matches = input.match(HEADER_INJECTION_PATTERN);
641
+ if (matches) {
642
+ for (const match of matches) {
643
+ threats.push({
644
+ type: "header_injection",
645
+ pattern: HEADER_INJECTION_PATTERN.source,
646
+ original: match
647
+ });
648
+ }
649
+ }
650
+ }
651
+ }
652
+ HEADER_INJECTION_PATTERN.lastIndex = 0;
653
+ const value = input.replace(HEADER_INJECTION_PATTERN, "");
654
+ if (collectThreats) {
655
+ return { value, wasSanitized, threats };
656
+ }
657
+ return value;
658
+ }
659
+ function sanitizeHeaders(headers) {
660
+ if (!headers || typeof headers !== "object") {
661
+ return {};
662
+ }
663
+ const result = {};
664
+ for (const [key, value] of Object.entries(headers)) {
665
+ const sanitizedKey = sanitizeHeaderValue(String(key));
666
+ const sanitizedValue = sanitizeHeaderValue(String(value));
667
+ result[sanitizedKey] = sanitizedValue;
668
+ }
669
+ return result;
670
+ }
671
+ function detectHeaderInjection(input) {
672
+ if (typeof input !== "string") return false;
673
+ HEADER_INJECTION_PATTERN.lastIndex = 0;
674
+ return HEADER_INJECTION_PATTERN.test(input);
675
+ }
676
+ var sanitizeEmailHeader = sanitizeHeaderValue;
677
+ var detectEmailHeaderInjection = detectHeaderInjection;
678
+
596
679
  // src/sanitizers/sanitize.ts
597
680
  function sanitizeString(value, options = {}) {
598
681
  if (typeof value !== "string") return value;
@@ -705,6 +788,15 @@ function scanThreats(data, depth = 0) {
705
788
  if (detectCommandInjection(data)) {
706
789
  return { vector: "command", rule: "command/match", matchedPattern: sample };
707
790
  }
791
+ if (detectLdapInjection(data)) {
792
+ return { vector: "ldap", rule: "ldap/match", matchedPattern: sample };
793
+ }
794
+ if (detectXpathInjection(data)) {
795
+ return { vector: "xpath", rule: "xpath/match", matchedPattern: sample };
796
+ }
797
+ if (detectHeaderInjection(data)) {
798
+ return { vector: "header", rule: "header/match", matchedPattern: sample };
799
+ }
708
800
  return null;
709
801
  }
710
802
  function createSanitizer(options = {}) {
@@ -839,55 +931,6 @@ function detectJsonpInjection(callback) {
839
931
  return false;
840
932
  }
841
933
 
842
- // src/sanitizers/headers.ts
843
- var HEADER_INJECTION_PATTERN = /\r\n|\r|\n|\0/g;
844
- function sanitizeHeaderValue(input, collectThreats = false) {
845
- if (typeof input !== "string") {
846
- return collectThreats ? { value: String(input), wasSanitized: false, threats: [] } : String(input);
847
- }
848
- const threats = [];
849
- let wasSanitized = false;
850
- if (HEADER_INJECTION_PATTERN.test(input)) {
851
- HEADER_INJECTION_PATTERN.lastIndex = 0;
852
- wasSanitized = true;
853
- if (collectThreats) {
854
- const matches = input.match(HEADER_INJECTION_PATTERN);
855
- if (matches) {
856
- for (const match of matches) {
857
- threats.push({
858
- type: "header_injection",
859
- pattern: HEADER_INJECTION_PATTERN.source,
860
- original: match
861
- });
862
- }
863
- }
864
- }
865
- }
866
- HEADER_INJECTION_PATTERN.lastIndex = 0;
867
- const value = input.replace(HEADER_INJECTION_PATTERN, "");
868
- if (collectThreats) {
869
- return { value, wasSanitized, threats };
870
- }
871
- return value;
872
- }
873
- function sanitizeHeaders(headers) {
874
- if (!headers || typeof headers !== "object") {
875
- return {};
876
- }
877
- const result = {};
878
- for (const [key, value] of Object.entries(headers)) {
879
- const sanitizedKey = sanitizeHeaderValue(String(key));
880
- const sanitizedValue = sanitizeHeaderValue(String(value));
881
- result[sanitizedKey] = sanitizedValue;
882
- }
883
- return result;
884
- }
885
- function detectHeaderInjection(input) {
886
- if (typeof input !== "string") return false;
887
- HEADER_INJECTION_PATTERN.lastIndex = 0;
888
- return HEADER_INJECTION_PATTERN.test(input);
889
- }
890
-
891
934
  // src/sanitizers/pii.ts
892
935
  var EMAIL_RE = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z]{2,})+/g;
893
936
  var PHONE_RE = /(?<!\d)(?:\+?1[-.\s]?)?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}(?!\d)/g;
@@ -1053,6 +1096,7 @@ function encodeForJs(value) {
1053
1096
  let result = "";
1054
1097
  for (const char of value) {
1055
1098
  const cp = char.codePointAt(0);
1099
+ if (cp === void 0) continue;
1056
1100
  if (cp >= 48 && cp <= 57 || // 0-9
1057
1101
  cp >= 65 && cp <= 90 || // A-Z
1058
1102
  cp >= 97 && cp <= 122) {
@@ -1089,27 +1133,53 @@ function encodeForCss(value) {
1089
1133
  return result;
1090
1134
  }
1091
1135
 
1092
- // src/sanitizers/ldap.ts
1093
- var LDAP_FILTER_CHARS = /[*()\\\x00]/g;
1094
- var LDAP_DN_CHARS = /[,+<>;"=\/\\\x00*()\x00]/g;
1095
- var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
1096
- var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
1097
- var escapeChar = (char) => "\\" + char.charCodeAt(0).toString(16).padStart(2, "0");
1098
- function sanitizeLdapFilter(input) {
1099
- if (typeof input !== "string") return String(input);
1100
- return input.replace(LDAP_FILTER_CHARS, escapeChar);
1136
+ // src/sanitizers/graphql.ts
1137
+ var DEFAULTS = {
1138
+ maxDepth: 10,
1139
+ maxLength: 1e4,
1140
+ blockIntrospection: true
1141
+ };
1142
+ var INTROSPECTION_PATTERN = /\b__(schema|type|typeKind|directive)\b/;
1143
+ function computeDepth(query) {
1144
+ let depth = 0;
1145
+ let max = 0;
1146
+ for (let i = 0; i < query.length; i++) {
1147
+ const c = query.charCodeAt(i);
1148
+ if (c === 123) {
1149
+ depth++;
1150
+ if (depth > max) max = depth;
1151
+ } else if (c === 125) {
1152
+ if (depth > 0) depth--;
1153
+ }
1154
+ }
1155
+ return max;
1101
1156
  }
1102
- function sanitizeLdapDn(input) {
1103
- if (typeof input !== "string") return String(input);
1104
- return input.replace(LDAP_DN_CHARS, escapeChar);
1157
+ function inspectGraphqlQuery(query, options = {}) {
1158
+ const maxDepth = options.maxDepth ?? DEFAULTS.maxDepth;
1159
+ const maxLength = options.maxLength ?? DEFAULTS.maxLength;
1160
+ const blockIntrospection = options.blockIntrospection ?? DEFAULTS.blockIntrospection;
1161
+ const length = query.length;
1162
+ const depth = computeDepth(query);
1163
+ if (depth > maxDepth) {
1164
+ return { blocked: true, reason: "depth", depth, length };
1165
+ }
1166
+ if (blockIntrospection && INTROSPECTION_PATTERN.test(query)) {
1167
+ return { blocked: true, reason: "introspection", depth, length };
1168
+ }
1169
+ if (length > maxLength) {
1170
+ return { blocked: true, reason: "length", depth, length };
1171
+ }
1172
+ return { blocked: false, depth, length };
1105
1173
  }
1106
- function detectLdapInjection(input) {
1107
- if (typeof input !== "string") return false;
1108
- return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
1174
+ function detectGraphqlAbuse(query, options) {
1175
+ if (typeof query !== "string" || query.length === 0) return false;
1176
+ return inspectGraphqlQuery(query, options).blocked;
1109
1177
  }
1110
1178
 
1111
1179
  exports.createSanitizer = createSanitizer;
1112
1180
  exports.detectCommandInjection = detectCommandInjection;
1181
+ exports.detectEmailHeaderInjection = detectEmailHeaderInjection;
1182
+ exports.detectGraphqlAbuse = detectGraphqlAbuse;
1113
1183
  exports.detectHeaderInjection = detectHeaderInjection;
1114
1184
  exports.detectJsonpInjection = detectJsonpInjection;
1115
1185
  exports.detectLdapInjection = detectLdapInjection;
@@ -1119,6 +1189,7 @@ exports.detectPii = detectPii;
1119
1189
  exports.detectPrototypePollution = detectPrototypePollution;
1120
1190
  exports.detectSql = detectSql;
1121
1191
  exports.detectSsti = detectSsti;
1192
+ exports.detectXpathInjection = detectXpathInjection;
1122
1193
  exports.detectXss = detectXss;
1123
1194
  exports.detectXxe = detectXxe;
1124
1195
  exports.encodeForAttribute = encodeForAttribute;
@@ -1129,12 +1200,14 @@ exports.encodeForUrl = encodeForUrl;
1129
1200
  exports.encodeHtmlEntities = encodeHtmlEntities;
1130
1201
  exports.getDangerousOperators = getDangerousOperators;
1131
1202
  exports.getDangerousProtoKeys = getDangerousProtoKeys;
1203
+ exports.inspectGraphqlQuery = inspectGraphqlQuery;
1132
1204
  exports.isDangerousNoSqlKey = isDangerousNoSqlKey;
1133
1205
  exports.isDangerousProtoKey = isDangerousProtoKey;
1134
1206
  exports.isPlainObject = isPlainObject;
1135
1207
  exports.redactObjectPii = redactObjectPii;
1136
1208
  exports.redactPii = redactPii;
1137
1209
  exports.sanitizeCommand = sanitizeCommand;
1210
+ exports.sanitizeEmailHeader = sanitizeEmailHeader;
1138
1211
  exports.sanitizeHeaderValue = sanitizeHeaderValue;
1139
1212
  exports.sanitizeHeaders = sanitizeHeaders;
1140
1213
  exports.sanitizeJsonpCallback = sanitizeJsonpCallback;
@@ -1145,6 +1218,7 @@ exports.sanitizePath = sanitizePath;
1145
1218
  exports.sanitizeSql = sanitizeSql;
1146
1219
  exports.sanitizeSsti = sanitizeSsti;
1147
1220
  exports.sanitizeString = sanitizeString;
1221
+ exports.sanitizeXpath = sanitizeXpath;
1148
1222
  exports.sanitizeXss = sanitizeXss;
1149
1223
  exports.sanitizeXxe = sanitizeXxe;
1150
1224
  exports.scanObjectPii = scanObjectPii;