@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
|
@@ -100,7 +100,16 @@ var SQL_PATTERNS = [
|
|
|
100
100
|
/** Time-based blind: PostgreSQL pg_sleep() */
|
|
101
101
|
/\bpg_sleep\s*\(/gi,
|
|
102
102
|
/** Time-based blind: MSSQL WAITFOR DELAY */
|
|
103
|
-
/\bWAITFOR\s+DELAY\b/gi
|
|
103
|
+
/\bWAITFOR\s+DELAY\b/gi,
|
|
104
|
+
/**
|
|
105
|
+
* Oracle DBMS_* stdlib packages used for time-based blind SQLi
|
|
106
|
+
* (DBMS_LOCK.SLEEP, DBMS_PIPE.RECEIVE_MESSAGE) and other Oracle
|
|
107
|
+
* abuse paths. No legitimate user input contains these. Mirrors
|
|
108
|
+
* `sqli-oracle-dbms-packages` in packages/core/patterns.json —
|
|
109
|
+
* improvements.md §1.1.e Q3. Must stay in sync until Node
|
|
110
|
+
* migrates to patterns.json-at-runtime (planned v1.7).
|
|
111
|
+
*/
|
|
112
|
+
/\bDBMS_(?:LOCK|PIPE|UTILITY|XSLPROCESSOR|JAVA|OUTPUT|SCHEDULER)\b/gi
|
|
104
113
|
];
|
|
105
114
|
var PATH_PATTERNS = [
|
|
106
115
|
/** Unix path traversal */
|
|
@@ -138,6 +147,15 @@ var COMMAND_PATTERNS = [
|
|
|
138
147
|
/[;&|`]/g,
|
|
139
148
|
/** Command substitution: $( ... ) — matched as a pair to reduce false positives */
|
|
140
149
|
/\$\(/g,
|
|
150
|
+
/**
|
|
151
|
+
* POSIX shell IFS-substitution: ${IFS} or ${IFS%??}.
|
|
152
|
+
* Attackers use this to inject spaces past metacharacter filters
|
|
153
|
+
* in payloads like `;cat${IFS}/etc/passwd`. Mirrors
|
|
154
|
+
* `cmdi-ifs-bypass` in packages/core/patterns.json — improvements.md
|
|
155
|
+
* §1.1.e Q5. Must stay in sync until Node migrates to
|
|
156
|
+
* patterns.json-at-runtime (planned v1.7).
|
|
157
|
+
*/
|
|
158
|
+
/\$\{IFS(?:%[^}]*)?\}/g,
|
|
141
159
|
/** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */
|
|
142
160
|
/%0[0-9a-f]/gi
|
|
143
161
|
];
|
|
@@ -675,6 +693,40 @@ var sanitizeEmailHeader = sanitizeHeaderValue;
|
|
|
675
693
|
var detectEmailHeaderInjection = detectHeaderInjection;
|
|
676
694
|
|
|
677
695
|
// src/sanitizers/sanitize.ts
|
|
696
|
+
function multiDecode(value, maxPasses = 4) {
|
|
697
|
+
for (let i = 0; i < maxPasses; i++) {
|
|
698
|
+
const prev = value;
|
|
699
|
+
try {
|
|
700
|
+
value = decodeURIComponent(value);
|
|
701
|
+
} catch {
|
|
702
|
+
}
|
|
703
|
+
value = htmlEntityDecode(value);
|
|
704
|
+
if (value === prev) break;
|
|
705
|
+
}
|
|
706
|
+
return value;
|
|
707
|
+
}
|
|
708
|
+
function htmlEntityDecode(s) {
|
|
709
|
+
s = s.replace(/&#(\d+);/g, (_m, n) => {
|
|
710
|
+
const code = parseInt(n, 10);
|
|
711
|
+
return Number.isFinite(code) && code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : _m;
|
|
712
|
+
});
|
|
713
|
+
s = s.replace(/&#x([0-9a-fA-F]+);/g, (_m, h) => {
|
|
714
|
+
const code = parseInt(h, 16);
|
|
715
|
+
return Number.isFinite(code) && code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : _m;
|
|
716
|
+
});
|
|
717
|
+
const named = {
|
|
718
|
+
"<": "<",
|
|
719
|
+
">": ">",
|
|
720
|
+
"&": "&",
|
|
721
|
+
""": '"',
|
|
722
|
+
"'": "'",
|
|
723
|
+
" ": " "
|
|
724
|
+
};
|
|
725
|
+
for (const [entity, ch] of Object.entries(named)) {
|
|
726
|
+
s = s.split(entity).join(ch);
|
|
727
|
+
}
|
|
728
|
+
return s;
|
|
729
|
+
}
|
|
678
730
|
function sanitizeString(value, options = {}) {
|
|
679
731
|
if (typeof value !== "string") return value;
|
|
680
732
|
const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
|
|
@@ -682,7 +734,8 @@ function sanitizeString(value, options = {}) {
|
|
|
682
734
|
throw new InputTooLargeError(maxSize, value.length);
|
|
683
735
|
}
|
|
684
736
|
const reject = options.mode === "reject";
|
|
685
|
-
let result = value;
|
|
737
|
+
let result = value.normalize("NFKC");
|
|
738
|
+
result = multiDecode(result);
|
|
686
739
|
if (options.sql !== false) {
|
|
687
740
|
if (reject) {
|
|
688
741
|
if (detectSql(result)) {
|
|
@@ -1135,7 +1188,9 @@ function encodeForCss(value) {
|
|
|
1135
1188
|
var DEFAULTS = {
|
|
1136
1189
|
maxDepth: 10,
|
|
1137
1190
|
maxLength: 1e4,
|
|
1138
|
-
blockIntrospection: true
|
|
1191
|
+
blockIntrospection: true,
|
|
1192
|
+
maxAliases: 50,
|
|
1193
|
+
blockFragmentCycles: true
|
|
1139
1194
|
};
|
|
1140
1195
|
var INTROSPECTION_PATTERN = /\b__(schema|type|typeKind|directive)\b/;
|
|
1141
1196
|
function computeDepth(query) {
|
|
@@ -1152,28 +1207,114 @@ function computeDepth(query) {
|
|
|
1152
1207
|
}
|
|
1153
1208
|
return max;
|
|
1154
1209
|
}
|
|
1210
|
+
var ALIAS_PATTERN = /\b([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
|
|
1211
|
+
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;
|
|
1212
|
+
var FRAGMENT_SPREAD_PATTERN = /\.\.\.\s*([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
|
|
1213
|
+
function countAliases(query) {
|
|
1214
|
+
let n = 0;
|
|
1215
|
+
ALIAS_PATTERN.lastIndex = 0;
|
|
1216
|
+
while (ALIAS_PATTERN.exec(query) !== null) n++;
|
|
1217
|
+
return n;
|
|
1218
|
+
}
|
|
1219
|
+
function hasFragmentCycle(query) {
|
|
1220
|
+
const deps = /* @__PURE__ */ new Map();
|
|
1221
|
+
FRAGMENT_DEF_PATTERN.lastIndex = 0;
|
|
1222
|
+
let match;
|
|
1223
|
+
while ((match = FRAGMENT_DEF_PATTERN.exec(query)) !== null) {
|
|
1224
|
+
const name = match[1];
|
|
1225
|
+
const bodyStart = match.index + match[0].length;
|
|
1226
|
+
let depth = 1;
|
|
1227
|
+
let i = bodyStart;
|
|
1228
|
+
while (i < query.length && depth > 0) {
|
|
1229
|
+
const ch = query[i];
|
|
1230
|
+
if (ch === "{") depth++;
|
|
1231
|
+
else if (ch === "}") depth--;
|
|
1232
|
+
i++;
|
|
1233
|
+
}
|
|
1234
|
+
const bodyEnd = depth === 0 ? i - 1 : i;
|
|
1235
|
+
const body = query.slice(bodyStart, bodyEnd);
|
|
1236
|
+
const spreads = /* @__PURE__ */ new Set();
|
|
1237
|
+
FRAGMENT_SPREAD_PATTERN.lastIndex = 0;
|
|
1238
|
+
let sm;
|
|
1239
|
+
while ((sm = FRAGMENT_SPREAD_PATTERN.exec(body)) !== null) {
|
|
1240
|
+
spreads.add(sm[1]);
|
|
1241
|
+
}
|
|
1242
|
+
deps.set(name, spreads);
|
|
1243
|
+
}
|
|
1244
|
+
if (deps.size === 0) return false;
|
|
1245
|
+
const WHITE = 0;
|
|
1246
|
+
const GRAY = 1;
|
|
1247
|
+
const BLACK = 2;
|
|
1248
|
+
const color = /* @__PURE__ */ new Map();
|
|
1249
|
+
for (const name of deps.keys()) color.set(name, WHITE);
|
|
1250
|
+
function visit(name) {
|
|
1251
|
+
if (color.get(name) === GRAY) return true;
|
|
1252
|
+
if (color.get(name) === BLACK) return false;
|
|
1253
|
+
if (!deps.has(name)) return false;
|
|
1254
|
+
color.set(name, GRAY);
|
|
1255
|
+
for (const child of deps.get(name)) {
|
|
1256
|
+
if (visit(child)) return true;
|
|
1257
|
+
}
|
|
1258
|
+
color.set(name, BLACK);
|
|
1259
|
+
return false;
|
|
1260
|
+
}
|
|
1261
|
+
for (const name of deps.keys()) {
|
|
1262
|
+
if (visit(name)) return true;
|
|
1263
|
+
}
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1155
1266
|
function inspectGraphqlQuery(query, options = {}) {
|
|
1156
1267
|
const maxDepth = options.maxDepth ?? DEFAULTS.maxDepth;
|
|
1157
1268
|
const maxLength = options.maxLength ?? DEFAULTS.maxLength;
|
|
1158
1269
|
const blockIntrospection = options.blockIntrospection ?? DEFAULTS.blockIntrospection;
|
|
1270
|
+
const maxAliases = options.maxAliases ?? DEFAULTS.maxAliases;
|
|
1271
|
+
const blockFragmentCycles = options.blockFragmentCycles ?? DEFAULTS.blockFragmentCycles;
|
|
1159
1272
|
const length = query.length;
|
|
1160
1273
|
const depth = computeDepth(query);
|
|
1274
|
+
const aliases = countAliases(query);
|
|
1161
1275
|
if (depth > maxDepth) {
|
|
1162
|
-
return { blocked: true, reason: "depth", depth, length };
|
|
1276
|
+
return { blocked: true, reason: "depth", depth, length, aliases };
|
|
1163
1277
|
}
|
|
1164
1278
|
if (blockIntrospection && INTROSPECTION_PATTERN.test(query)) {
|
|
1165
|
-
return { blocked: true, reason: "introspection", depth, length };
|
|
1279
|
+
return { blocked: true, reason: "introspection", depth, length, aliases };
|
|
1280
|
+
}
|
|
1281
|
+
if (aliases > maxAliases) {
|
|
1282
|
+
return { blocked: true, reason: "aliases", depth, length, aliases };
|
|
1283
|
+
}
|
|
1284
|
+
if (blockFragmentCycles && hasFragmentCycle(query)) {
|
|
1285
|
+
return { blocked: true, reason: "fragment_cycle", depth, length, aliases };
|
|
1166
1286
|
}
|
|
1167
1287
|
if (length > maxLength) {
|
|
1168
|
-
return { blocked: true, reason: "length", depth, length };
|
|
1288
|
+
return { blocked: true, reason: "length", depth, length, aliases };
|
|
1169
1289
|
}
|
|
1170
|
-
return { blocked: false, depth, length };
|
|
1290
|
+
return { blocked: false, depth, length, aliases };
|
|
1171
1291
|
}
|
|
1172
1292
|
function detectGraphqlAbuse(query, options) {
|
|
1173
1293
|
if (typeof query !== "string" || query.length === 0) return false;
|
|
1174
1294
|
return inspectGraphqlQuery(query, options).blocked;
|
|
1175
1295
|
}
|
|
1176
1296
|
|
|
1177
|
-
|
|
1297
|
+
// src/sanitizers/deserialization.ts
|
|
1298
|
+
var PICKLE_HEAD = /^\x80[\x02-\x05]/;
|
|
1299
|
+
var RUBY_MARSHAL_HEAD = /^\x04\x08/;
|
|
1300
|
+
var DOTNET_BINFMT_HEAD = /^\x00\x01\x00\x00\x00/;
|
|
1301
|
+
var FASTJSON_AUTOTYPE = /"@type"\s*:\s*"[a-zA-Z_$][\w$.]*"/;
|
|
1302
|
+
var PHP_UNSERIALIZE = /O:\d+:"[a-zA-Z_\\][\w\\]*":\d+:\{/;
|
|
1303
|
+
function detectDeserialization(payload) {
|
|
1304
|
+
if (typeof payload !== "string" || payload.length === 0) {
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
if (PICKLE_HEAD.test(payload)) return "python_pickle";
|
|
1308
|
+
if (RUBY_MARSHAL_HEAD.test(payload)) return "ruby_marshal";
|
|
1309
|
+
if (DOTNET_BINFMT_HEAD.test(payload)) return "dotnet_binary_formatter";
|
|
1310
|
+
if (FASTJSON_AUTOTYPE.test(payload)) return "java_fastjson";
|
|
1311
|
+
if (PHP_UNSERIALIZE.test(payload)) return "php_unserialize";
|
|
1312
|
+
return null;
|
|
1313
|
+
}
|
|
1314
|
+
function isSerializedPayload(payload) {
|
|
1315
|
+
return detectDeserialization(payload) !== null;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
export { createSanitizer, detectCommandInjection, detectDeserialization, detectEmailHeaderInjection, detectGraphqlAbuse, detectHeaderInjection, detectJsonpInjection, detectLdapInjection, detectNoSqlInjection, detectPathTraversal, detectPii, detectPrototypePollution, detectSql, detectSsti, detectXpathInjection, detectXss, detectXxe, encodeForAttribute, encodeForCss, encodeForHtml, encodeForJs, encodeForUrl, encodeHtmlEntities, getDangerousOperators, getDangerousProtoKeys, inspectGraphqlQuery, isDangerousNoSqlKey, isDangerousProtoKey, isPlainObject, isSerializedPayload, redactObjectPii, redactPii, sanitizeCommand, sanitizeEmailHeader, sanitizeHeaderValue, sanitizeHeaders, sanitizeJsonpCallback, sanitizeLdapDn, sanitizeLdapFilter, sanitizeObject, sanitizePath, sanitizeSql, sanitizeSsti, sanitizeString, sanitizeXpath, sanitizeXss, sanitizeXxe, scanObjectPii, scanPii, scanThreats };
|
|
1178
1319
|
//# sourceMappingURL=index.mjs.map
|
|
1179
1320
|
//# sourceMappingURL=index.mjs.map
|