@arcis/node 1.5.2 → 1.6.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.
- package/README.md +48 -7
- package/dist/astro/index.js.map +1 -1
- package/dist/astro/index.mjs.map +1 -1
- package/dist/bun/index.js.map +1 -1
- package/dist/bun/index.mjs.map +1 -1
- package/dist/core/constants.d.ts +2 -2
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/index.js +19 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +19 -1
- package/dist/core/index.mjs.map +1 -1
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/index.mjs.map +1 -1
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/index.mjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +407 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +407 -9
- package/dist/index.mjs.map +1 -1
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/index.mjs.map +1 -1
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/index.mjs.map +1 -1
- package/dist/middleware/astro.d.ts +6 -1
- package/dist/middleware/astro.d.ts.map +1 -1
- package/dist/middleware/bun.d.ts +8 -1
- package/dist/middleware/bun.d.ts.map +1 -1
- package/dist/middleware/correlation.d.ts +87 -0
- package/dist/middleware/correlation.d.ts.map +1 -0
- package/dist/middleware/graphql.d.ts.map +1 -1
- package/dist/middleware/hono.d.ts +6 -0
- package/dist/middleware/hono.d.ts.map +1 -1
- package/dist/middleware/index.d.ts +3 -1
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +366 -8
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +366 -9
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/middleware/koa.d.ts +5 -0
- package/dist/middleware/koa.d.ts.map +1 -1
- package/dist/middleware/nextjs.d.ts +9 -1
- package/dist/middleware/nextjs.d.ts.map +1 -1
- package/dist/middleware/nuxt.d.ts +6 -1
- package/dist/middleware/nuxt.d.ts.map +1 -1
- package/dist/middleware/protect.d.ts +32 -0
- package/dist/middleware/protect.d.ts.map +1 -1
- package/dist/middleware/sveltekit.d.ts +6 -1
- package/dist/middleware/sveltekit.d.ts.map +1 -1
- package/dist/nestjs/index.js +55 -2
- package/dist/nestjs/index.js.map +1 -1
- package/dist/nestjs/index.mjs +55 -2
- package/dist/nestjs/index.mjs.map +1 -1
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/index.mjs.map +1 -1
- package/dist/nuxt/index.js.map +1 -1
- package/dist/nuxt/index.mjs.map +1 -1
- package/dist/sanitizers/deserialization.d.ts +30 -0
- package/dist/sanitizers/deserialization.d.ts.map +1 -0
- package/dist/sanitizers/graphql.d.ts +20 -3
- package/dist/sanitizers/graphql.d.ts.map +1 -1
- package/dist/sanitizers/index.d.ts +2 -0
- package/dist/sanitizers/index.d.ts.map +1 -1
- package/dist/sanitizers/index.js +150 -7
- package/dist/sanitizers/index.js.map +1 -1
- package/dist/sanitizers/index.mjs +149 -8
- package/dist/sanitizers/index.mjs.map +1 -1
- package/dist/sanitizers/prompt-injection.d.ts.map +1 -1
- package/dist/sanitizers/sanitize.d.ts +0 -20
- package/dist/sanitizers/sanitize.d.ts.map +1 -1
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/index.mjs.map +1 -1
- package/dist/sveltekit/index.js.map +1 -1
- package/dist/sveltekit/index.mjs.map +1 -1
- package/dist/validation/index.js +55 -2
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/index.mjs +55 -2
- package/dist/validation/index.mjs.map +1 -1
- package/package.json +11 -11
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/sanitizers/deserialization
|
|
3
|
+
*
|
|
4
|
+
* V33 — Modern deserialization marker detection (improvements.md §1.2).
|
|
5
|
+
*
|
|
6
|
+
* Detect input that LOOKS like a serialized-object payload for
|
|
7
|
+
* runtimes where deserialization equals code execution: Python
|
|
8
|
+
* pickle, Java FastJSON, PHP unserialize, Ruby Marshal, .NET
|
|
9
|
+
* BinaryFormatter.
|
|
10
|
+
*
|
|
11
|
+
* Detection-only — the right response to a hit is "refuse the
|
|
12
|
+
* request" not "strip the bytes and pass through" (a forgiving
|
|
13
|
+
* parser might still deserialize the remainder to something
|
|
14
|
+
* dangerous). Caller decides.
|
|
15
|
+
*
|
|
16
|
+
* Mirrors `arcis-python/arcis/sanitizers/deserialization.py`. Both
|
|
17
|
+
* SDKs must accept the same base corpus per Pattern 7.
|
|
18
|
+
*/
|
|
19
|
+
export type DeserializeRuntime = 'python_pickle' | 'java_fastjson' | 'php_unserialize' | 'ruby_marshal' | 'dotnet_binary_formatter';
|
|
20
|
+
/**
|
|
21
|
+
* Detect a serialized-object marker for any known runtime.
|
|
22
|
+
*
|
|
23
|
+
* Returns the runtime tag if a marker matches, or null if the input
|
|
24
|
+
* looks safe. Precedence: head-byte markers (pickle / Ruby / .NET)
|
|
25
|
+
* before embedded markers (FastJSON / PHP).
|
|
26
|
+
*/
|
|
27
|
+
export declare function detectDeserialization(payload: string): DeserializeRuntime | null;
|
|
28
|
+
/** Convenience boolean wrapper around `detectDeserialization`. */
|
|
29
|
+
export declare function isSerializedPayload(payload: string): boolean;
|
|
30
|
+
//# sourceMappingURL=deserialization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deserialization.d.ts","sourceRoot":"","sources":["../../src/sanitizers/deserialization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,eAAe,GACf,eAAe,GACf,iBAAiB,GACjB,cAAc,GACd,yBAAyB,CAAC;AAiB9B;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,GACd,kBAAkB,GAAG,IAAI,CAU3B;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5D"}
|
|
@@ -44,17 +44,34 @@ export interface GraphqlGuardOptions {
|
|
|
44
44
|
* Studio. Production should leave this on.
|
|
45
45
|
*/
|
|
46
46
|
blockIntrospection?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Maximum number of field aliases per query (`label: field`).
|
|
49
|
+
* Default: 50. Alias-bomb attacks repeat the same expensive field
|
|
50
|
+
* under many labels to multiply backend cost. Real queries rarely
|
|
51
|
+
* use more than 20 aliases. improvements.md §1.2 V34.
|
|
52
|
+
*/
|
|
53
|
+
maxAliases?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Reject queries whose fragment definitions form a cycle (direct
|
|
56
|
+
* self-reference `fragment A on T { ...A }` or indirect
|
|
57
|
+
* `A → B → A`). Such cycles either infinite-loop a naive resolver
|
|
58
|
+
* or get rejected by `graphql-core` with a 500. Default: true.
|
|
59
|
+
* improvements.md §1.2 V34.
|
|
60
|
+
*/
|
|
61
|
+
blockFragmentCycles?: boolean;
|
|
47
62
|
}
|
|
48
|
-
export type GraphqlViolation = 'depth' | 'length' | 'introspection';
|
|
63
|
+
export type GraphqlViolation = 'depth' | 'length' | 'introspection' | 'aliases' | 'fragment_cycle';
|
|
49
64
|
export interface GraphqlGuardResult {
|
|
50
65
|
/** True if the query violated any configured limit. */
|
|
51
66
|
blocked: boolean;
|
|
52
|
-
/** Which limit fired first
|
|
67
|
+
/** Which limit fired first. Precedence: depth → introspection → aliases → fragment_cycle → length. */
|
|
53
68
|
reason?: GraphqlViolation;
|
|
54
|
-
/** Observed nesting depth. Always returned
|
|
69
|
+
/** Observed nesting depth. Always returned. */
|
|
55
70
|
depth: number;
|
|
56
71
|
/** Observed length. Always returned. */
|
|
57
72
|
length: number;
|
|
73
|
+
/** Observed alias count (improvements.md §1.2 V34). Always returned. */
|
|
74
|
+
aliases: number;
|
|
58
75
|
}
|
|
59
76
|
/**
|
|
60
77
|
* Inspect a GraphQL query against the configured limits. Returns a
|
|
@@ -1 +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;
|
|
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;IAC7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,MAAM,gBAAgB,GACxB,OAAO,GACP,QAAQ,GACR,eAAe,GACf,SAAS,GACT,gBAAgB,CAAC;AAErB,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,sGAAsG;IACtG,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;CACjB;AA8HD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,GAChC,kBAAkB,CAiCpB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAGT"}
|
|
@@ -20,5 +20,7 @@ export { sanitizeLdapFilter, sanitizeLdapDn, detectLdapInjection } from './ldap'
|
|
|
20
20
|
export { sanitizeXpath, detectXpathInjection } from './xpath';
|
|
21
21
|
export { inspectGraphqlQuery, detectGraphqlAbuse, } from './graphql';
|
|
22
22
|
export type { GraphqlGuardOptions, GraphqlGuardResult, GraphqlViolation, } from './graphql';
|
|
23
|
+
export { detectDeserialization, isSerializedPayload } from './deserialization';
|
|
24
|
+
export type { DeserializeRuntime } from './deserialization';
|
|
23
25
|
export { encodeHtmlEntities, isPlainObject } from './utils';
|
|
24
26
|
//# 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,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"}
|
|
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,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/sanitizers/index.js
CHANGED
|
@@ -102,7 +102,16 @@ var SQL_PATTERNS = [
|
|
|
102
102
|
/** Time-based blind: PostgreSQL pg_sleep() */
|
|
103
103
|
/\bpg_sleep\s*\(/gi,
|
|
104
104
|
/** Time-based blind: MSSQL WAITFOR DELAY */
|
|
105
|
-
/\bWAITFOR\s+DELAY\b/gi
|
|
105
|
+
/\bWAITFOR\s+DELAY\b/gi,
|
|
106
|
+
/**
|
|
107
|
+
* Oracle DBMS_* stdlib packages used for time-based blind SQLi
|
|
108
|
+
* (DBMS_LOCK.SLEEP, DBMS_PIPE.RECEIVE_MESSAGE) and other Oracle
|
|
109
|
+
* abuse paths. No legitimate user input contains these. Mirrors
|
|
110
|
+
* `sqli-oracle-dbms-packages` in packages/core/patterns.json —
|
|
111
|
+
* improvements.md §1.1.e Q3. Must stay in sync until Node
|
|
112
|
+
* migrates to patterns.json-at-runtime (planned v1.7).
|
|
113
|
+
*/
|
|
114
|
+
/\bDBMS_(?:LOCK|PIPE|UTILITY|XSLPROCESSOR|JAVA|OUTPUT|SCHEDULER)\b/gi
|
|
106
115
|
];
|
|
107
116
|
var PATH_PATTERNS = [
|
|
108
117
|
/** Unix path traversal */
|
|
@@ -140,6 +149,15 @@ var COMMAND_PATTERNS = [
|
|
|
140
149
|
/[;&|`]/g,
|
|
141
150
|
/** Command substitution: $( ... ) — matched as a pair to reduce false positives */
|
|
142
151
|
/\$\(/g,
|
|
152
|
+
/**
|
|
153
|
+
* POSIX shell IFS-substitution: ${IFS} or ${IFS%??}.
|
|
154
|
+
* Attackers use this to inject spaces past metacharacter filters
|
|
155
|
+
* in payloads like `;cat${IFS}/etc/passwd`. Mirrors
|
|
156
|
+
* `cmdi-ifs-bypass` in packages/core/patterns.json — improvements.md
|
|
157
|
+
* §1.1.e Q5. Must stay in sync until Node migrates to
|
|
158
|
+
* patterns.json-at-runtime (planned v1.7).
|
|
159
|
+
*/
|
|
160
|
+
/\$\{IFS(?:%[^}]*)?\}/g,
|
|
143
161
|
/** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */
|
|
144
162
|
/%0[0-9a-f]/gi
|
|
145
163
|
];
|
|
@@ -677,6 +695,40 @@ var sanitizeEmailHeader = sanitizeHeaderValue;
|
|
|
677
695
|
var detectEmailHeaderInjection = detectHeaderInjection;
|
|
678
696
|
|
|
679
697
|
// src/sanitizers/sanitize.ts
|
|
698
|
+
function multiDecode(value, maxPasses = 4) {
|
|
699
|
+
for (let i = 0; i < maxPasses; i++) {
|
|
700
|
+
const prev = value;
|
|
701
|
+
try {
|
|
702
|
+
value = decodeURIComponent(value);
|
|
703
|
+
} catch {
|
|
704
|
+
}
|
|
705
|
+
value = htmlEntityDecode(value);
|
|
706
|
+
if (value === prev) break;
|
|
707
|
+
}
|
|
708
|
+
return value;
|
|
709
|
+
}
|
|
710
|
+
function htmlEntityDecode(s) {
|
|
711
|
+
s = s.replace(/&#(\d+);/g, (_m, n) => {
|
|
712
|
+
const code = parseInt(n, 10);
|
|
713
|
+
return Number.isFinite(code) && code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : _m;
|
|
714
|
+
});
|
|
715
|
+
s = s.replace(/&#x([0-9a-fA-F]+);/g, (_m, h) => {
|
|
716
|
+
const code = parseInt(h, 16);
|
|
717
|
+
return Number.isFinite(code) && code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : _m;
|
|
718
|
+
});
|
|
719
|
+
const named = {
|
|
720
|
+
"<": "<",
|
|
721
|
+
">": ">",
|
|
722
|
+
"&": "&",
|
|
723
|
+
""": '"',
|
|
724
|
+
"'": "'",
|
|
725
|
+
" ": " "
|
|
726
|
+
};
|
|
727
|
+
for (const [entity, ch] of Object.entries(named)) {
|
|
728
|
+
s = s.split(entity).join(ch);
|
|
729
|
+
}
|
|
730
|
+
return s;
|
|
731
|
+
}
|
|
680
732
|
function sanitizeString(value, options = {}) {
|
|
681
733
|
if (typeof value !== "string") return value;
|
|
682
734
|
const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
|
|
@@ -684,7 +736,8 @@ function sanitizeString(value, options = {}) {
|
|
|
684
736
|
throw new InputTooLargeError(maxSize, value.length);
|
|
685
737
|
}
|
|
686
738
|
const reject = options.mode === "reject";
|
|
687
|
-
let result = value;
|
|
739
|
+
let result = value.normalize("NFKC");
|
|
740
|
+
result = multiDecode(result);
|
|
688
741
|
if (options.sql !== false) {
|
|
689
742
|
if (reject) {
|
|
690
743
|
if (detectSql(result)) {
|
|
@@ -1137,7 +1190,9 @@ function encodeForCss(value) {
|
|
|
1137
1190
|
var DEFAULTS = {
|
|
1138
1191
|
maxDepth: 10,
|
|
1139
1192
|
maxLength: 1e4,
|
|
1140
|
-
blockIntrospection: true
|
|
1193
|
+
blockIntrospection: true,
|
|
1194
|
+
maxAliases: 50,
|
|
1195
|
+
blockFragmentCycles: true
|
|
1141
1196
|
};
|
|
1142
1197
|
var INTROSPECTION_PATTERN = /\b__(schema|type|typeKind|directive)\b/;
|
|
1143
1198
|
function computeDepth(query) {
|
|
@@ -1154,30 +1209,117 @@ function computeDepth(query) {
|
|
|
1154
1209
|
}
|
|
1155
1210
|
return max;
|
|
1156
1211
|
}
|
|
1212
|
+
var ALIAS_PATTERN = /\b([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
|
|
1213
|
+
var FRAGMENT_DEF_PATTERN = /\bfragment\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+on\s+[a-zA-Z_][a-zA-Z0-9_]*\s*\{/g;
|
|
1214
|
+
var FRAGMENT_SPREAD_PATTERN = /\.\.\.\s*([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
|
|
1215
|
+
function countAliases(query) {
|
|
1216
|
+
let n = 0;
|
|
1217
|
+
ALIAS_PATTERN.lastIndex = 0;
|
|
1218
|
+
while (ALIAS_PATTERN.exec(query) !== null) n++;
|
|
1219
|
+
return n;
|
|
1220
|
+
}
|
|
1221
|
+
function hasFragmentCycle(query) {
|
|
1222
|
+
const deps = /* @__PURE__ */ new Map();
|
|
1223
|
+
FRAGMENT_DEF_PATTERN.lastIndex = 0;
|
|
1224
|
+
let match;
|
|
1225
|
+
while ((match = FRAGMENT_DEF_PATTERN.exec(query)) !== null) {
|
|
1226
|
+
const name = match[1];
|
|
1227
|
+
const bodyStart = match.index + match[0].length;
|
|
1228
|
+
let depth = 1;
|
|
1229
|
+
let i = bodyStart;
|
|
1230
|
+
while (i < query.length && depth > 0) {
|
|
1231
|
+
const ch = query[i];
|
|
1232
|
+
if (ch === "{") depth++;
|
|
1233
|
+
else if (ch === "}") depth--;
|
|
1234
|
+
i++;
|
|
1235
|
+
}
|
|
1236
|
+
const bodyEnd = depth === 0 ? i - 1 : i;
|
|
1237
|
+
const body = query.slice(bodyStart, bodyEnd);
|
|
1238
|
+
const spreads = /* @__PURE__ */ new Set();
|
|
1239
|
+
FRAGMENT_SPREAD_PATTERN.lastIndex = 0;
|
|
1240
|
+
let sm;
|
|
1241
|
+
while ((sm = FRAGMENT_SPREAD_PATTERN.exec(body)) !== null) {
|
|
1242
|
+
spreads.add(sm[1]);
|
|
1243
|
+
}
|
|
1244
|
+
deps.set(name, spreads);
|
|
1245
|
+
}
|
|
1246
|
+
if (deps.size === 0) return false;
|
|
1247
|
+
const WHITE = 0;
|
|
1248
|
+
const GRAY = 1;
|
|
1249
|
+
const BLACK = 2;
|
|
1250
|
+
const color = /* @__PURE__ */ new Map();
|
|
1251
|
+
for (const name of deps.keys()) color.set(name, WHITE);
|
|
1252
|
+
function visit(name) {
|
|
1253
|
+
if (color.get(name) === GRAY) return true;
|
|
1254
|
+
if (color.get(name) === BLACK) return false;
|
|
1255
|
+
if (!deps.has(name)) return false;
|
|
1256
|
+
color.set(name, GRAY);
|
|
1257
|
+
for (const child of deps.get(name)) {
|
|
1258
|
+
if (visit(child)) return true;
|
|
1259
|
+
}
|
|
1260
|
+
color.set(name, BLACK);
|
|
1261
|
+
return false;
|
|
1262
|
+
}
|
|
1263
|
+
for (const name of deps.keys()) {
|
|
1264
|
+
if (visit(name)) return true;
|
|
1265
|
+
}
|
|
1266
|
+
return false;
|
|
1267
|
+
}
|
|
1157
1268
|
function inspectGraphqlQuery(query, options = {}) {
|
|
1158
1269
|
const maxDepth = options.maxDepth ?? DEFAULTS.maxDepth;
|
|
1159
1270
|
const maxLength = options.maxLength ?? DEFAULTS.maxLength;
|
|
1160
1271
|
const blockIntrospection = options.blockIntrospection ?? DEFAULTS.blockIntrospection;
|
|
1272
|
+
const maxAliases = options.maxAliases ?? DEFAULTS.maxAliases;
|
|
1273
|
+
const blockFragmentCycles = options.blockFragmentCycles ?? DEFAULTS.blockFragmentCycles;
|
|
1161
1274
|
const length = query.length;
|
|
1162
1275
|
const depth = computeDepth(query);
|
|
1276
|
+
const aliases = countAliases(query);
|
|
1163
1277
|
if (depth > maxDepth) {
|
|
1164
|
-
return { blocked: true, reason: "depth", depth, length };
|
|
1278
|
+
return { blocked: true, reason: "depth", depth, length, aliases };
|
|
1165
1279
|
}
|
|
1166
1280
|
if (blockIntrospection && INTROSPECTION_PATTERN.test(query)) {
|
|
1167
|
-
return { blocked: true, reason: "introspection", depth, length };
|
|
1281
|
+
return { blocked: true, reason: "introspection", depth, length, aliases };
|
|
1282
|
+
}
|
|
1283
|
+
if (aliases > maxAliases) {
|
|
1284
|
+
return { blocked: true, reason: "aliases", depth, length, aliases };
|
|
1285
|
+
}
|
|
1286
|
+
if (blockFragmentCycles && hasFragmentCycle(query)) {
|
|
1287
|
+
return { blocked: true, reason: "fragment_cycle", depth, length, aliases };
|
|
1168
1288
|
}
|
|
1169
1289
|
if (length > maxLength) {
|
|
1170
|
-
return { blocked: true, reason: "length", depth, length };
|
|
1290
|
+
return { blocked: true, reason: "length", depth, length, aliases };
|
|
1171
1291
|
}
|
|
1172
|
-
return { blocked: false, depth, length };
|
|
1292
|
+
return { blocked: false, depth, length, aliases };
|
|
1173
1293
|
}
|
|
1174
1294
|
function detectGraphqlAbuse(query, options) {
|
|
1175
1295
|
if (typeof query !== "string" || query.length === 0) return false;
|
|
1176
1296
|
return inspectGraphqlQuery(query, options).blocked;
|
|
1177
1297
|
}
|
|
1178
1298
|
|
|
1299
|
+
// src/sanitizers/deserialization.ts
|
|
1300
|
+
var PICKLE_HEAD = /^\x80[\x02-\x05]/;
|
|
1301
|
+
var RUBY_MARSHAL_HEAD = /^\x04\x08/;
|
|
1302
|
+
var DOTNET_BINFMT_HEAD = /^\x00\x01\x00\x00\x00/;
|
|
1303
|
+
var FASTJSON_AUTOTYPE = /"@type"\s*:\s*"[a-zA-Z_$][\w$.]*"/;
|
|
1304
|
+
var PHP_UNSERIALIZE = /O:\d+:"[a-zA-Z_\\][\w\\]*":\d+:\{/;
|
|
1305
|
+
function detectDeserialization(payload) {
|
|
1306
|
+
if (typeof payload !== "string" || payload.length === 0) {
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
if (PICKLE_HEAD.test(payload)) return "python_pickle";
|
|
1310
|
+
if (RUBY_MARSHAL_HEAD.test(payload)) return "ruby_marshal";
|
|
1311
|
+
if (DOTNET_BINFMT_HEAD.test(payload)) return "dotnet_binary_formatter";
|
|
1312
|
+
if (FASTJSON_AUTOTYPE.test(payload)) return "java_fastjson";
|
|
1313
|
+
if (PHP_UNSERIALIZE.test(payload)) return "php_unserialize";
|
|
1314
|
+
return null;
|
|
1315
|
+
}
|
|
1316
|
+
function isSerializedPayload(payload) {
|
|
1317
|
+
return detectDeserialization(payload) !== null;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1179
1320
|
exports.createSanitizer = createSanitizer;
|
|
1180
1321
|
exports.detectCommandInjection = detectCommandInjection;
|
|
1322
|
+
exports.detectDeserialization = detectDeserialization;
|
|
1181
1323
|
exports.detectEmailHeaderInjection = detectEmailHeaderInjection;
|
|
1182
1324
|
exports.detectGraphqlAbuse = detectGraphqlAbuse;
|
|
1183
1325
|
exports.detectHeaderInjection = detectHeaderInjection;
|
|
@@ -1204,6 +1346,7 @@ exports.inspectGraphqlQuery = inspectGraphqlQuery;
|
|
|
1204
1346
|
exports.isDangerousNoSqlKey = isDangerousNoSqlKey;
|
|
1205
1347
|
exports.isDangerousProtoKey = isDangerousProtoKey;
|
|
1206
1348
|
exports.isPlainObject = isPlainObject;
|
|
1349
|
+
exports.isSerializedPayload = isSerializedPayload;
|
|
1207
1350
|
exports.redactObjectPii = redactObjectPii;
|
|
1208
1351
|
exports.redactPii = redactPii;
|
|
1209
1352
|
exports.sanitizeCommand = sanitizeCommand;
|