@arcis/node 1.5.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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/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/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/protect.d.ts +32 -0
- package/dist/middleware/protect.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 +2 -2
|
@@ -36,6 +36,32 @@ import { type CsrfOptions } from './csrf';
|
|
|
36
36
|
import type { CorsOptions } from './cors';
|
|
37
37
|
import { type SignupProtectionOptions } from './signup-protection';
|
|
38
38
|
import type { RateLimitOptions, SanitizeOptions } from '../core/types';
|
|
39
|
+
import { CorrelationWindow } from './correlation';
|
|
40
|
+
/**
|
|
41
|
+
* Per-protect-helper correlation-window wiring (improvements.md §1.4).
|
|
42
|
+
*
|
|
43
|
+
* Pass an instance of `CorrelationWindow` plus the vector tag this
|
|
44
|
+
* route represents ("login" / "signup" / "api"). The middleware
|
|
45
|
+
* records every request in the window and refuses the request when
|
|
46
|
+
* the window flags the IP as a scanner / credential stuffer / race
|
|
47
|
+
* probe. Detection-only otherwise.
|
|
48
|
+
*
|
|
49
|
+
* Pull-out fields:
|
|
50
|
+
* - `usernameField`: body key whose value is the distinct-value
|
|
51
|
+
* tracked for credential-stuffing detection. Defaults to
|
|
52
|
+
* `'username'`.
|
|
53
|
+
* - `route`: route label recorded in the window (so cross-route
|
|
54
|
+
* aggregation is meaningful). Defaults to the request path.
|
|
55
|
+
* - `statusCode` / `message`: response shape on a correlation block.
|
|
56
|
+
*/
|
|
57
|
+
export interface CorrelationOptions {
|
|
58
|
+
window: CorrelationWindow;
|
|
59
|
+
vector?: string;
|
|
60
|
+
usernameField?: string;
|
|
61
|
+
route?: string;
|
|
62
|
+
statusCode?: number;
|
|
63
|
+
message?: string;
|
|
64
|
+
}
|
|
39
65
|
/**
|
|
40
66
|
* Per-layer override knob: pass `false` to disable, an options object
|
|
41
67
|
* to merge into the layer's defaults, or omit to accept the helper's
|
|
@@ -47,6 +73,8 @@ export interface ProtectLoginOptions {
|
|
|
47
73
|
bot?: LayerOverride<BotProtectionOptions>;
|
|
48
74
|
csrf?: LayerOverride<CsrfOptions>;
|
|
49
75
|
sanitize?: LayerOverride<SanitizeOptions>;
|
|
76
|
+
/** Optional correlation-window wiring (improvements.md §1.4). */
|
|
77
|
+
correlation?: CorrelationOptions;
|
|
50
78
|
}
|
|
51
79
|
export interface ProtectSignupOptions {
|
|
52
80
|
rateLimit?: LayerOverride<RateLimitOptions>;
|
|
@@ -54,12 +82,16 @@ export interface ProtectSignupOptions {
|
|
|
54
82
|
sanitize?: LayerOverride<SanitizeOptions>;
|
|
55
83
|
/** signupProtection options (email-style validation, disposable-mail block, etc.). */
|
|
56
84
|
signup?: LayerOverride<SignupProtectionOptions>;
|
|
85
|
+
/** Optional correlation-window wiring (improvements.md §1.4). */
|
|
86
|
+
correlation?: CorrelationOptions;
|
|
57
87
|
}
|
|
58
88
|
export interface ProtectApiOptions {
|
|
59
89
|
rateLimit?: LayerOverride<RateLimitOptions>;
|
|
60
90
|
/** CORS is required to take an Origin/Methods config — no default origin. */
|
|
61
91
|
cors?: LayerOverride<CorsOptions>;
|
|
62
92
|
sanitize?: LayerOverride<SanitizeOptions>;
|
|
93
|
+
/** Optional correlation-window wiring (improvements.md §1.4). */
|
|
94
|
+
correlation?: CorrelationOptions;
|
|
63
95
|
}
|
|
64
96
|
/**
|
|
65
97
|
* Login endpoints get the strictest defaults: 5 req/min/IP, deny
|
|
@@ -1 +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;
|
|
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;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmDD;;;;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;IAC1C,iEAAiE;IACjE,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;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;IAChD,iEAAiE;IACjE,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;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;IAC1C,iEAAiE;IACjE,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAaD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,cAAc,EAAE,CA0BhF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,cAAc,EAAE,CA0BlF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,EAAE,CAsB5E"}
|
package/dist/nestjs/index.js
CHANGED
|
@@ -139,7 +139,16 @@ var SQL_PATTERNS = [
|
|
|
139
139
|
/** Time-based blind: PostgreSQL pg_sleep() */
|
|
140
140
|
/\bpg_sleep\s*\(/gi,
|
|
141
141
|
/** Time-based blind: MSSQL WAITFOR DELAY */
|
|
142
|
-
/\bWAITFOR\s+DELAY\b/gi
|
|
142
|
+
/\bWAITFOR\s+DELAY\b/gi,
|
|
143
|
+
/**
|
|
144
|
+
* Oracle DBMS_* stdlib packages used for time-based blind SQLi
|
|
145
|
+
* (DBMS_LOCK.SLEEP, DBMS_PIPE.RECEIVE_MESSAGE) and other Oracle
|
|
146
|
+
* abuse paths. No legitimate user input contains these. Mirrors
|
|
147
|
+
* `sqli-oracle-dbms-packages` in packages/core/patterns.json —
|
|
148
|
+
* improvements.md §1.1.e Q3. Must stay in sync until Node
|
|
149
|
+
* migrates to patterns.json-at-runtime (planned v1.7).
|
|
150
|
+
*/
|
|
151
|
+
/\bDBMS_(?:LOCK|PIPE|UTILITY|XSLPROCESSOR|JAVA|OUTPUT|SCHEDULER)\b/gi
|
|
143
152
|
];
|
|
144
153
|
var PATH_PATTERNS = [
|
|
145
154
|
/** Unix path traversal */
|
|
@@ -177,6 +186,15 @@ var COMMAND_PATTERNS = [
|
|
|
177
186
|
/[;&|`]/g,
|
|
178
187
|
/** Command substitution: $( ... ) — matched as a pair to reduce false positives */
|
|
179
188
|
/\$\(/g,
|
|
189
|
+
/**
|
|
190
|
+
* POSIX shell IFS-substitution: ${IFS} or ${IFS%??}.
|
|
191
|
+
* Attackers use this to inject spaces past metacharacter filters
|
|
192
|
+
* in payloads like `;cat${IFS}/etc/passwd`. Mirrors
|
|
193
|
+
* `cmdi-ifs-bypass` in packages/core/patterns.json — improvements.md
|
|
194
|
+
* §1.1.e Q5. Must stay in sync until Node migrates to
|
|
195
|
+
* patterns.json-at-runtime (planned v1.7).
|
|
196
|
+
*/
|
|
197
|
+
/\$\{IFS(?:%[^}]*)?\}/g,
|
|
180
198
|
/** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */
|
|
181
199
|
/%0[0-9a-f]/gi
|
|
182
200
|
];
|
|
@@ -947,6 +965,40 @@ function detectHeaderInjection(input) {
|
|
|
947
965
|
}
|
|
948
966
|
|
|
949
967
|
// src/sanitizers/sanitize.ts
|
|
968
|
+
function multiDecode(value, maxPasses = 4) {
|
|
969
|
+
for (let i = 0; i < maxPasses; i++) {
|
|
970
|
+
const prev = value;
|
|
971
|
+
try {
|
|
972
|
+
value = decodeURIComponent(value);
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
value = htmlEntityDecode(value);
|
|
976
|
+
if (value === prev) break;
|
|
977
|
+
}
|
|
978
|
+
return value;
|
|
979
|
+
}
|
|
980
|
+
function htmlEntityDecode(s) {
|
|
981
|
+
s = s.replace(/&#(\d+);/g, (_m, n) => {
|
|
982
|
+
const code = parseInt(n, 10);
|
|
983
|
+
return Number.isFinite(code) && code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : _m;
|
|
984
|
+
});
|
|
985
|
+
s = s.replace(/&#x([0-9a-fA-F]+);/g, (_m, h) => {
|
|
986
|
+
const code = parseInt(h, 16);
|
|
987
|
+
return Number.isFinite(code) && code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : _m;
|
|
988
|
+
});
|
|
989
|
+
const named = {
|
|
990
|
+
"<": "<",
|
|
991
|
+
">": ">",
|
|
992
|
+
"&": "&",
|
|
993
|
+
""": '"',
|
|
994
|
+
"'": "'",
|
|
995
|
+
" ": " "
|
|
996
|
+
};
|
|
997
|
+
for (const [entity, ch] of Object.entries(named)) {
|
|
998
|
+
s = s.split(entity).join(ch);
|
|
999
|
+
}
|
|
1000
|
+
return s;
|
|
1001
|
+
}
|
|
950
1002
|
function sanitizeString(value, options = {}) {
|
|
951
1003
|
if (typeof value !== "string") return value;
|
|
952
1004
|
const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
|
|
@@ -954,7 +1006,8 @@ function sanitizeString(value, options = {}) {
|
|
|
954
1006
|
throw new InputTooLargeError(maxSize, value.length);
|
|
955
1007
|
}
|
|
956
1008
|
const reject = options.mode === "reject";
|
|
957
|
-
let result = value;
|
|
1009
|
+
let result = value.normalize("NFKC");
|
|
1010
|
+
result = multiDecode(result);
|
|
958
1011
|
if (options.sql !== false) {
|
|
959
1012
|
if (reject) {
|
|
960
1013
|
if (detectSql(result)) {
|