@arcis/node 1.5.1 → 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
package/dist/index.d.ts
CHANGED
|
@@ -46,9 +46,11 @@ export type { MethodAllowlistOptions } from './middleware/method-allowlist';
|
|
|
46
46
|
export { massAssign } from './middleware/mass-assign';
|
|
47
47
|
export type { MassAssignOptions } from './middleware/mass-assign';
|
|
48
48
|
export { protectLogin, protectSignup, protectApi } from './middleware/protect';
|
|
49
|
-
export type { ProtectLoginOptions, ProtectSignupOptions, ProtectApiOptions, } from './middleware/protect';
|
|
49
|
+
export type { ProtectLoginOptions, ProtectSignupOptions, ProtectApiOptions, CorrelationOptions, } from './middleware/protect';
|
|
50
50
|
export { graphqlGuard } from './middleware/graphql';
|
|
51
51
|
export type { GraphqlGuardMiddlewareOptions } from './middleware/graphql';
|
|
52
|
+
export { CorrelationWindow } from './middleware/correlation';
|
|
53
|
+
export type { CorrelationEvent, CorrelationDetections, CorrelationWindowOptions, } from './middleware/correlation';
|
|
52
54
|
export { inspectGraphqlQuery, detectGraphqlAbuse, } from './sanitizers/graphql';
|
|
53
55
|
export type { GraphqlGuardOptions, GraphqlGuardResult, GraphqlViolation, } from './sanitizers/graphql';
|
|
54
56
|
export { Guards } from './guards';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAK5C,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACtG,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACrG,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,YAAY,EACV,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EACV,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,YAAY,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -165,7 +165,16 @@ var SQL_PATTERNS = [
|
|
|
165
165
|
/** Time-based blind: PostgreSQL pg_sleep() */
|
|
166
166
|
/\bpg_sleep\s*\(/gi,
|
|
167
167
|
/** Time-based blind: MSSQL WAITFOR DELAY */
|
|
168
|
-
/\bWAITFOR\s+DELAY\b/gi
|
|
168
|
+
/\bWAITFOR\s+DELAY\b/gi,
|
|
169
|
+
/**
|
|
170
|
+
* Oracle DBMS_* stdlib packages used for time-based blind SQLi
|
|
171
|
+
* (DBMS_LOCK.SLEEP, DBMS_PIPE.RECEIVE_MESSAGE) and other Oracle
|
|
172
|
+
* abuse paths. No legitimate user input contains these. Mirrors
|
|
173
|
+
* `sqli-oracle-dbms-packages` in packages/core/patterns.json —
|
|
174
|
+
* improvements.md §1.1.e Q3. Must stay in sync until Node
|
|
175
|
+
* migrates to patterns.json-at-runtime (planned v1.7).
|
|
176
|
+
*/
|
|
177
|
+
/\bDBMS_(?:LOCK|PIPE|UTILITY|XSLPROCESSOR|JAVA|OUTPUT|SCHEDULER)\b/gi
|
|
169
178
|
];
|
|
170
179
|
var PATH_PATTERNS = [
|
|
171
180
|
/** Unix path traversal */
|
|
@@ -203,6 +212,15 @@ var COMMAND_PATTERNS = [
|
|
|
203
212
|
/[;&|`]/g,
|
|
204
213
|
/** Command substitution: $( ... ) — matched as a pair to reduce false positives */
|
|
205
214
|
/\$\(/g,
|
|
215
|
+
/**
|
|
216
|
+
* POSIX shell IFS-substitution: ${IFS} or ${IFS%??}.
|
|
217
|
+
* Attackers use this to inject spaces past metacharacter filters
|
|
218
|
+
* in payloads like `;cat${IFS}/etc/passwd`. Mirrors
|
|
219
|
+
* `cmdi-ifs-bypass` in packages/core/patterns.json — improvements.md
|
|
220
|
+
* §1.1.e Q5. Must stay in sync until Node migrates to
|
|
221
|
+
* patterns.json-at-runtime (planned v1.7).
|
|
222
|
+
*/
|
|
223
|
+
/\$\{IFS(?:%[^}]*)?\}/g,
|
|
206
224
|
/** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */
|
|
207
225
|
/%0[0-9a-f]/gi
|
|
208
226
|
];
|
|
@@ -1147,6 +1165,40 @@ function detectHeaderInjection(input) {
|
|
|
1147
1165
|
}
|
|
1148
1166
|
|
|
1149
1167
|
// src/sanitizers/sanitize.ts
|
|
1168
|
+
function multiDecode(value, maxPasses = 4) {
|
|
1169
|
+
for (let i = 0; i < maxPasses; i++) {
|
|
1170
|
+
const prev = value;
|
|
1171
|
+
try {
|
|
1172
|
+
value = decodeURIComponent(value);
|
|
1173
|
+
} catch {
|
|
1174
|
+
}
|
|
1175
|
+
value = htmlEntityDecode(value);
|
|
1176
|
+
if (value === prev) break;
|
|
1177
|
+
}
|
|
1178
|
+
return value;
|
|
1179
|
+
}
|
|
1180
|
+
function htmlEntityDecode(s) {
|
|
1181
|
+
s = s.replace(/&#(\d+);/g, (_m, n) => {
|
|
1182
|
+
const code = parseInt(n, 10);
|
|
1183
|
+
return Number.isFinite(code) && code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : _m;
|
|
1184
|
+
});
|
|
1185
|
+
s = s.replace(/&#x([0-9a-fA-F]+);/g, (_m, h) => {
|
|
1186
|
+
const code = parseInt(h, 16);
|
|
1187
|
+
return Number.isFinite(code) && code >= 0 && code <= 1114111 ? String.fromCodePoint(code) : _m;
|
|
1188
|
+
});
|
|
1189
|
+
const named = {
|
|
1190
|
+
"<": "<",
|
|
1191
|
+
">": ">",
|
|
1192
|
+
"&": "&",
|
|
1193
|
+
""": '"',
|
|
1194
|
+
"'": "'",
|
|
1195
|
+
" ": " "
|
|
1196
|
+
};
|
|
1197
|
+
for (const [entity, ch] of Object.entries(named)) {
|
|
1198
|
+
s = s.split(entity).join(ch);
|
|
1199
|
+
}
|
|
1200
|
+
return s;
|
|
1201
|
+
}
|
|
1150
1202
|
function sanitizeString(value, options = {}) {
|
|
1151
1203
|
if (typeof value !== "string") return value;
|
|
1152
1204
|
const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
|
|
@@ -1154,7 +1206,8 @@ function sanitizeString(value, options = {}) {
|
|
|
1154
1206
|
throw new InputTooLargeError(maxSize, value.length);
|
|
1155
1207
|
}
|
|
1156
1208
|
const reject = options.mode === "reject";
|
|
1157
|
-
let result = value;
|
|
1209
|
+
let result = value.normalize("NFKC");
|
|
1210
|
+
result = multiDecode(result);
|
|
1158
1211
|
if (options.sql !== false) {
|
|
1159
1212
|
if (reject) {
|
|
1160
1213
|
if (detectSql(result)) {
|
|
@@ -1601,7 +1654,9 @@ function encodeForCss(value) {
|
|
|
1601
1654
|
var DEFAULTS = {
|
|
1602
1655
|
maxDepth: 10,
|
|
1603
1656
|
maxLength: 1e4,
|
|
1604
|
-
blockIntrospection: true
|
|
1657
|
+
blockIntrospection: true,
|
|
1658
|
+
maxAliases: 50,
|
|
1659
|
+
blockFragmentCycles: true
|
|
1605
1660
|
};
|
|
1606
1661
|
var INTROSPECTION_PATTERN = /\b__(schema|type|typeKind|directive)\b/;
|
|
1607
1662
|
function computeDepth(query) {
|
|
@@ -1618,22 +1673,87 @@ function computeDepth(query) {
|
|
|
1618
1673
|
}
|
|
1619
1674
|
return max;
|
|
1620
1675
|
}
|
|
1676
|
+
var ALIAS_PATTERN = /\b([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
|
|
1677
|
+
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;
|
|
1678
|
+
var FRAGMENT_SPREAD_PATTERN = /\.\.\.\s*([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
|
|
1679
|
+
function countAliases(query) {
|
|
1680
|
+
let n = 0;
|
|
1681
|
+
ALIAS_PATTERN.lastIndex = 0;
|
|
1682
|
+
while (ALIAS_PATTERN.exec(query) !== null) n++;
|
|
1683
|
+
return n;
|
|
1684
|
+
}
|
|
1685
|
+
function hasFragmentCycle(query) {
|
|
1686
|
+
const deps = /* @__PURE__ */ new Map();
|
|
1687
|
+
FRAGMENT_DEF_PATTERN.lastIndex = 0;
|
|
1688
|
+
let match;
|
|
1689
|
+
while ((match = FRAGMENT_DEF_PATTERN.exec(query)) !== null) {
|
|
1690
|
+
const name = match[1];
|
|
1691
|
+
const bodyStart = match.index + match[0].length;
|
|
1692
|
+
let depth = 1;
|
|
1693
|
+
let i = bodyStart;
|
|
1694
|
+
while (i < query.length && depth > 0) {
|
|
1695
|
+
const ch = query[i];
|
|
1696
|
+
if (ch === "{") depth++;
|
|
1697
|
+
else if (ch === "}") depth--;
|
|
1698
|
+
i++;
|
|
1699
|
+
}
|
|
1700
|
+
const bodyEnd = depth === 0 ? i - 1 : i;
|
|
1701
|
+
const body = query.slice(bodyStart, bodyEnd);
|
|
1702
|
+
const spreads = /* @__PURE__ */ new Set();
|
|
1703
|
+
FRAGMENT_SPREAD_PATTERN.lastIndex = 0;
|
|
1704
|
+
let sm;
|
|
1705
|
+
while ((sm = FRAGMENT_SPREAD_PATTERN.exec(body)) !== null) {
|
|
1706
|
+
spreads.add(sm[1]);
|
|
1707
|
+
}
|
|
1708
|
+
deps.set(name, spreads);
|
|
1709
|
+
}
|
|
1710
|
+
if (deps.size === 0) return false;
|
|
1711
|
+
const WHITE = 0;
|
|
1712
|
+
const GRAY = 1;
|
|
1713
|
+
const BLACK = 2;
|
|
1714
|
+
const color = /* @__PURE__ */ new Map();
|
|
1715
|
+
for (const name of deps.keys()) color.set(name, WHITE);
|
|
1716
|
+
function visit(name) {
|
|
1717
|
+
if (color.get(name) === GRAY) return true;
|
|
1718
|
+
if (color.get(name) === BLACK) return false;
|
|
1719
|
+
if (!deps.has(name)) return false;
|
|
1720
|
+
color.set(name, GRAY);
|
|
1721
|
+
for (const child of deps.get(name)) {
|
|
1722
|
+
if (visit(child)) return true;
|
|
1723
|
+
}
|
|
1724
|
+
color.set(name, BLACK);
|
|
1725
|
+
return false;
|
|
1726
|
+
}
|
|
1727
|
+
for (const name of deps.keys()) {
|
|
1728
|
+
if (visit(name)) return true;
|
|
1729
|
+
}
|
|
1730
|
+
return false;
|
|
1731
|
+
}
|
|
1621
1732
|
function inspectGraphqlQuery(query, options = {}) {
|
|
1622
1733
|
const maxDepth = options.maxDepth ?? DEFAULTS.maxDepth;
|
|
1623
1734
|
const maxLength = options.maxLength ?? DEFAULTS.maxLength;
|
|
1624
1735
|
const blockIntrospection = options.blockIntrospection ?? DEFAULTS.blockIntrospection;
|
|
1736
|
+
const maxAliases = options.maxAliases ?? DEFAULTS.maxAliases;
|
|
1737
|
+
const blockFragmentCycles = options.blockFragmentCycles ?? DEFAULTS.blockFragmentCycles;
|
|
1625
1738
|
const length = query.length;
|
|
1626
1739
|
const depth = computeDepth(query);
|
|
1740
|
+
const aliases = countAliases(query);
|
|
1627
1741
|
if (depth > maxDepth) {
|
|
1628
|
-
return { blocked: true, reason: "depth", depth, length };
|
|
1742
|
+
return { blocked: true, reason: "depth", depth, length, aliases };
|
|
1629
1743
|
}
|
|
1630
1744
|
if (blockIntrospection && INTROSPECTION_PATTERN.test(query)) {
|
|
1631
|
-
return { blocked: true, reason: "introspection", depth, length };
|
|
1745
|
+
return { blocked: true, reason: "introspection", depth, length, aliases };
|
|
1746
|
+
}
|
|
1747
|
+
if (aliases > maxAliases) {
|
|
1748
|
+
return { blocked: true, reason: "aliases", depth, length, aliases };
|
|
1749
|
+
}
|
|
1750
|
+
if (blockFragmentCycles && hasFragmentCycle(query)) {
|
|
1751
|
+
return { blocked: true, reason: "fragment_cycle", depth, length, aliases };
|
|
1632
1752
|
}
|
|
1633
1753
|
if (length > maxLength) {
|
|
1634
|
-
return { blocked: true, reason: "length", depth, length };
|
|
1754
|
+
return { blocked: true, reason: "length", depth, length, aliases };
|
|
1635
1755
|
}
|
|
1636
|
-
return { blocked: false, depth, length };
|
|
1756
|
+
return { blocked: false, depth, length, aliases };
|
|
1637
1757
|
}
|
|
1638
1758
|
function detectGraphqlAbuse(query, options) {
|
|
1639
1759
|
if (typeof query !== "string" || query.length === 0) return false;
|
|
@@ -9969,6 +10089,46 @@ function signupProtection(options = {}) {
|
|
|
9969
10089
|
}
|
|
9970
10090
|
|
|
9971
10091
|
// src/middleware/protect.ts
|
|
10092
|
+
function getClientIp(req) {
|
|
10093
|
+
const xff = req?.headers?.["x-forwarded-for"] ?? req?.headers?.["X-Forwarded-For"];
|
|
10094
|
+
if (typeof xff === "string" && xff.length > 0) {
|
|
10095
|
+
const first = xff.split(",")[0]?.trim();
|
|
10096
|
+
if (first) return first;
|
|
10097
|
+
}
|
|
10098
|
+
if (typeof req?.ip === "string") return req.ip;
|
|
10099
|
+
const remote = req?.socket?.remoteAddress;
|
|
10100
|
+
return typeof remote === "string" ? remote : "";
|
|
10101
|
+
}
|
|
10102
|
+
function correlationMiddleware(opts) {
|
|
10103
|
+
const vector = opts.vector ?? "request";
|
|
10104
|
+
const usernameField = opts.usernameField ?? "username";
|
|
10105
|
+
const statusCode = opts.statusCode ?? 429;
|
|
10106
|
+
const message = opts.message ?? "Suspicious request pattern detected.";
|
|
10107
|
+
return function(req, res, next) {
|
|
10108
|
+
const ip = getClientIp(req);
|
|
10109
|
+
if (!ip) return next();
|
|
10110
|
+
const route = opts.route ?? (req.path || req.url || "/");
|
|
10111
|
+
const username = req?.body?.[usernameField];
|
|
10112
|
+
const distinctValue = typeof username === "string" && username.length > 0 ? username : void 0;
|
|
10113
|
+
const detections = opts.window.record(
|
|
10114
|
+
ip,
|
|
10115
|
+
vector,
|
|
10116
|
+
route,
|
|
10117
|
+
req.method || "GET",
|
|
10118
|
+
distinctValue
|
|
10119
|
+
);
|
|
10120
|
+
if (detections.scanner || detections.credentialStuffing || detections.raceWindow) {
|
|
10121
|
+
res.status(statusCode).json({
|
|
10122
|
+
error: message,
|
|
10123
|
+
scanner: detections.scanner,
|
|
10124
|
+
credential_stuffing: detections.credentialStuffing,
|
|
10125
|
+
race_window: detections.raceWindow
|
|
10126
|
+
});
|
|
10127
|
+
return;
|
|
10128
|
+
}
|
|
10129
|
+
next();
|
|
10130
|
+
};
|
|
10131
|
+
}
|
|
9972
10132
|
function resolve(override, defaults) {
|
|
9973
10133
|
if (override === false) return null;
|
|
9974
10134
|
if (override === void 0) return defaults;
|
|
@@ -9988,6 +10148,11 @@ function protectLogin(options = {}) {
|
|
|
9988
10148
|
if (csrf) middlewares.push(csrfProtection(csrf));
|
|
9989
10149
|
const sanitize = resolve(options.sanitize, {});
|
|
9990
10150
|
if (sanitize) middlewares.push(createSanitizer(sanitize));
|
|
10151
|
+
if (options.correlation) {
|
|
10152
|
+
middlewares.push(
|
|
10153
|
+
correlationMiddleware({ vector: "login", ...options.correlation })
|
|
10154
|
+
);
|
|
10155
|
+
}
|
|
9991
10156
|
return middlewares;
|
|
9992
10157
|
}
|
|
9993
10158
|
function protectSignup(options = {}) {
|
|
@@ -10004,6 +10169,11 @@ function protectSignup(options = {}) {
|
|
|
10004
10169
|
if (sanitize) middlewares.push(createSanitizer(sanitize));
|
|
10005
10170
|
const signup = resolve(options.signup, {});
|
|
10006
10171
|
if (signup) middlewares.push(signupProtection(signup));
|
|
10172
|
+
if (options.correlation) {
|
|
10173
|
+
middlewares.push(
|
|
10174
|
+
correlationMiddleware({ vector: "signup", ...options.correlation })
|
|
10175
|
+
);
|
|
10176
|
+
}
|
|
10007
10177
|
return middlewares;
|
|
10008
10178
|
}
|
|
10009
10179
|
function protectApi(options = {}) {
|
|
@@ -10014,6 +10184,11 @@ function protectApi(options = {}) {
|
|
|
10014
10184
|
if (cors) middlewares.push(safeCors(cors));
|
|
10015
10185
|
const sanitize = resolve(options.sanitize, {});
|
|
10016
10186
|
if (sanitize) middlewares.push(createSanitizer(sanitize));
|
|
10187
|
+
if (options.correlation) {
|
|
10188
|
+
middlewares.push(
|
|
10189
|
+
correlationMiddleware({ vector: "api", ...options.correlation })
|
|
10190
|
+
);
|
|
10191
|
+
}
|
|
10017
10192
|
return middlewares;
|
|
10018
10193
|
}
|
|
10019
10194
|
|
|
@@ -10021,7 +10196,9 @@ function protectApi(options = {}) {
|
|
|
10021
10196
|
var DEFAULT_MESSAGES = {
|
|
10022
10197
|
depth: "Query exceeds maximum nesting depth",
|
|
10023
10198
|
length: "Query exceeds maximum length",
|
|
10024
|
-
introspection: "Introspection queries are disabled"
|
|
10199
|
+
introspection: "Introspection queries are disabled",
|
|
10200
|
+
aliases: "Query exceeds maximum alias count (alias-bomb protection)",
|
|
10201
|
+
fragment_cycle: "Query contains a cyclic fragment definition"
|
|
10025
10202
|
};
|
|
10026
10203
|
function extractQuery(req) {
|
|
10027
10204
|
const bodyQuery = typeof req.body === "object" && req.body !== null ? req.body.query : void 0;
|
|
@@ -10057,6 +10234,186 @@ function graphqlGuard(options = {}) {
|
|
|
10057
10234
|
};
|
|
10058
10235
|
}
|
|
10059
10236
|
|
|
10237
|
+
// src/middleware/correlation.ts
|
|
10238
|
+
var EMPTY_DETECTIONS = Object.freeze({
|
|
10239
|
+
scanner: false,
|
|
10240
|
+
credentialStuffing: false,
|
|
10241
|
+
raceWindow: false,
|
|
10242
|
+
distinctVectors: 0,
|
|
10243
|
+
distinctValues: 0,
|
|
10244
|
+
requestsInWindow: 0
|
|
10245
|
+
});
|
|
10246
|
+
function normalizePair(a, b) {
|
|
10247
|
+
return a < b ? `${a}${b}` : `${b}${a}`;
|
|
10248
|
+
}
|
|
10249
|
+
var CorrelationWindow = class {
|
|
10250
|
+
constructor(options = {}) {
|
|
10251
|
+
// Map iteration order in JS is insertion order, so re-inserting on
|
|
10252
|
+
// access gives us LRU behaviour without a separate linked list.
|
|
10253
|
+
this.buckets = /* @__PURE__ */ new Map();
|
|
10254
|
+
const {
|
|
10255
|
+
windowSeconds = 60,
|
|
10256
|
+
maxIps = 1e4,
|
|
10257
|
+
maxEventsPerIp = 200,
|
|
10258
|
+
scannerDistinctVectors = 3,
|
|
10259
|
+
scannerMinRequests = 20,
|
|
10260
|
+
credentialStuffingDistinctValues = 10,
|
|
10261
|
+
raceWindowMs = 200,
|
|
10262
|
+
racePairs
|
|
10263
|
+
} = options;
|
|
10264
|
+
if (windowSeconds <= 0) throw new Error("windowSeconds must be > 0");
|
|
10265
|
+
if (maxIps < 1) throw new Error("maxIps must be >= 1");
|
|
10266
|
+
if (maxEventsPerIp < 1) throw new Error("maxEventsPerIp must be >= 1");
|
|
10267
|
+
this.windowSeconds = windowSeconds;
|
|
10268
|
+
this.maxIps = maxIps;
|
|
10269
|
+
this.maxEventsPerIp = maxEventsPerIp;
|
|
10270
|
+
this.scannerDistinctVectors = scannerDistinctVectors;
|
|
10271
|
+
this.scannerMinRequests = scannerMinRequests;
|
|
10272
|
+
this.csDistinctValues = credentialStuffingDistinctValues;
|
|
10273
|
+
this.raceWindowSeconds = raceWindowMs / 1e3;
|
|
10274
|
+
this.racePairKeys = /* @__PURE__ */ new Set();
|
|
10275
|
+
this.racePairTuples = [];
|
|
10276
|
+
if (racePairs) {
|
|
10277
|
+
for (const [a, b] of racePairs) {
|
|
10278
|
+
const key = normalizePair(a, b);
|
|
10279
|
+
if (!this.racePairKeys.has(key)) {
|
|
10280
|
+
this.racePairKeys.add(key);
|
|
10281
|
+
const sorted = a < b ? [a, b] : [b, a];
|
|
10282
|
+
this.racePairTuples.push(sorted);
|
|
10283
|
+
}
|
|
10284
|
+
}
|
|
10285
|
+
}
|
|
10286
|
+
}
|
|
10287
|
+
record(ip, vector, route, method = "GET", distinctValue, now) {
|
|
10288
|
+
if (!ip) return EMPTY_DETECTIONS;
|
|
10289
|
+
const ts = now ?? Date.now() / 1e3;
|
|
10290
|
+
const event = {
|
|
10291
|
+
timestamp: ts,
|
|
10292
|
+
vector,
|
|
10293
|
+
route,
|
|
10294
|
+
method,
|
|
10295
|
+
distinctValue
|
|
10296
|
+
};
|
|
10297
|
+
let bucket = this.buckets.get(ip);
|
|
10298
|
+
if (bucket === void 0) {
|
|
10299
|
+
bucket = { events: [] };
|
|
10300
|
+
this.buckets.set(ip, bucket);
|
|
10301
|
+
while (this.buckets.size > this.maxIps) {
|
|
10302
|
+
const oldest = this.buckets.keys().next().value;
|
|
10303
|
+
if (oldest === void 0) break;
|
|
10304
|
+
this.buckets.delete(oldest);
|
|
10305
|
+
}
|
|
10306
|
+
} else {
|
|
10307
|
+
this.buckets.delete(ip);
|
|
10308
|
+
this.buckets.set(ip, bucket);
|
|
10309
|
+
}
|
|
10310
|
+
bucket.events.push(event);
|
|
10311
|
+
this.evictStale(bucket, ts);
|
|
10312
|
+
return this.evaluate(bucket, route);
|
|
10313
|
+
}
|
|
10314
|
+
detectScanner(ip, now) {
|
|
10315
|
+
const bucket = this.buckets.get(ip);
|
|
10316
|
+
if (bucket === void 0) return false;
|
|
10317
|
+
this.evictStale(bucket, now ?? Date.now() / 1e3);
|
|
10318
|
+
return this.isScanner(bucket);
|
|
10319
|
+
}
|
|
10320
|
+
detectCredentialStuffing(ip, route, now) {
|
|
10321
|
+
const bucket = this.buckets.get(ip);
|
|
10322
|
+
if (bucket === void 0) return false;
|
|
10323
|
+
this.evictStale(bucket, now ?? Date.now() / 1e3);
|
|
10324
|
+
return this.isCredentialStuffing(bucket, route);
|
|
10325
|
+
}
|
|
10326
|
+
detectRaceWindow(ip, routePair, now) {
|
|
10327
|
+
const bucket = this.buckets.get(ip);
|
|
10328
|
+
if (bucket === void 0) return false;
|
|
10329
|
+
this.evictStale(bucket, now ?? Date.now() / 1e3);
|
|
10330
|
+
const sorted = routePair[0] < routePair[1] ? routePair : [routePair[1], routePair[0]];
|
|
10331
|
+
return this.racePairInBucket(bucket, sorted);
|
|
10332
|
+
}
|
|
10333
|
+
reset(ip) {
|
|
10334
|
+
if (ip === void 0) {
|
|
10335
|
+
this.buckets.clear();
|
|
10336
|
+
} else {
|
|
10337
|
+
this.buckets.delete(ip);
|
|
10338
|
+
}
|
|
10339
|
+
}
|
|
10340
|
+
stats() {
|
|
10341
|
+
let events = 0;
|
|
10342
|
+
for (const b of this.buckets.values()) events += b.events.length;
|
|
10343
|
+
return { trackedIps: this.buckets.size, eventsInWindow: events };
|
|
10344
|
+
}
|
|
10345
|
+
// -------------------------------------------------------- internals
|
|
10346
|
+
evictStale(bucket, now) {
|
|
10347
|
+
const cutoff = now - this.windowSeconds;
|
|
10348
|
+
let drop = 0;
|
|
10349
|
+
while (drop < bucket.events.length && bucket.events[drop].timestamp < cutoff) {
|
|
10350
|
+
drop++;
|
|
10351
|
+
}
|
|
10352
|
+
if (drop > 0) bucket.events.splice(0, drop);
|
|
10353
|
+
if (bucket.events.length > this.maxEventsPerIp) {
|
|
10354
|
+
bucket.events.splice(0, bucket.events.length - this.maxEventsPerIp);
|
|
10355
|
+
}
|
|
10356
|
+
}
|
|
10357
|
+
evaluate(bucket, route) {
|
|
10358
|
+
const vectors = /* @__PURE__ */ new Set();
|
|
10359
|
+
const values = /* @__PURE__ */ new Set();
|
|
10360
|
+
for (const e of bucket.events) {
|
|
10361
|
+
vectors.add(e.vector);
|
|
10362
|
+
if (e.route === route && e.distinctValue !== void 0) {
|
|
10363
|
+
values.add(e.distinctValue);
|
|
10364
|
+
}
|
|
10365
|
+
}
|
|
10366
|
+
return {
|
|
10367
|
+
scanner: this.isScanner(bucket),
|
|
10368
|
+
credentialStuffing: this.isCredentialStuffing(bucket, route),
|
|
10369
|
+
raceWindow: this.isRaceAny(bucket),
|
|
10370
|
+
distinctVectors: vectors.size,
|
|
10371
|
+
distinctValues: values.size,
|
|
10372
|
+
requestsInWindow: bucket.events.length
|
|
10373
|
+
};
|
|
10374
|
+
}
|
|
10375
|
+
isScanner(bucket) {
|
|
10376
|
+
if (bucket.events.length < this.scannerMinRequests) return false;
|
|
10377
|
+
const vectors = /* @__PURE__ */ new Set();
|
|
10378
|
+
for (const e of bucket.events) vectors.add(e.vector);
|
|
10379
|
+
return vectors.size >= this.scannerDistinctVectors;
|
|
10380
|
+
}
|
|
10381
|
+
isCredentialStuffing(bucket, route) {
|
|
10382
|
+
const values = /* @__PURE__ */ new Set();
|
|
10383
|
+
for (const e of bucket.events) {
|
|
10384
|
+
if (e.route === route && e.distinctValue !== void 0) {
|
|
10385
|
+
values.add(e.distinctValue);
|
|
10386
|
+
}
|
|
10387
|
+
}
|
|
10388
|
+
return values.size >= this.csDistinctValues;
|
|
10389
|
+
}
|
|
10390
|
+
racePairInBucket(bucket, sorted) {
|
|
10391
|
+
const [a, b] = sorted;
|
|
10392
|
+
const aTs = [];
|
|
10393
|
+
const bTs = [];
|
|
10394
|
+
for (const e of bucket.events) {
|
|
10395
|
+
if (e.route === a) aTs.push(e.timestamp);
|
|
10396
|
+
else if (e.route === b) bTs.push(e.timestamp);
|
|
10397
|
+
}
|
|
10398
|
+
if (aTs.length === 0 || bTs.length === 0) return false;
|
|
10399
|
+
let ai = 0;
|
|
10400
|
+
let bi = 0;
|
|
10401
|
+
while (ai < aTs.length && bi < bTs.length) {
|
|
10402
|
+
const diff = aTs[ai] - bTs[bi];
|
|
10403
|
+
if (Math.abs(diff) <= this.raceWindowSeconds) return true;
|
|
10404
|
+
if (diff < 0) ai++;
|
|
10405
|
+
else bi++;
|
|
10406
|
+
}
|
|
10407
|
+
return false;
|
|
10408
|
+
}
|
|
10409
|
+
isRaceAny(bucket) {
|
|
10410
|
+
for (const pair of this.racePairTuples) {
|
|
10411
|
+
if (this.racePairInBucket(bucket, pair)) return true;
|
|
10412
|
+
}
|
|
10413
|
+
return false;
|
|
10414
|
+
}
|
|
10415
|
+
};
|
|
10416
|
+
|
|
10060
10417
|
// src/sanitizers/prompt-injection.ts
|
|
10061
10418
|
var SIGNATURES = [
|
|
10062
10419
|
// --- HIGH severity: clear override / jailbreak attempts ---
|
|
@@ -10186,6 +10543,47 @@ var SIGNATURES = [
|
|
|
10186
10543
|
severity: "medium",
|
|
10187
10544
|
description: "ROT13 / Caesar-cipher decode hint"
|
|
10188
10545
|
},
|
|
10546
|
+
// ── V32: AI agent toolcall injection (improvements.md §1.2) ────────
|
|
10547
|
+
// Modern LLM agents (Claude tool-use, OpenAI function-calling,
|
|
10548
|
+
// ReAct loops) read tool definitions from the system prompt and
|
|
10549
|
+
// JSON-shaped requests from the model. A malicious user can embed
|
|
10550
|
+
// those shapes in their input to make the host think they invoked
|
|
10551
|
+
// a tool, or to trick the model into echoing a synthesized
|
|
10552
|
+
// tool_call that the runtime then executes.
|
|
10553
|
+
//
|
|
10554
|
+
// Narrow patterns — match the literal JSON keys and inline
|
|
10555
|
+
// tool-name shapes. Won't false-positive on plain English text
|
|
10556
|
+
// discussing tools.
|
|
10557
|
+
{
|
|
10558
|
+
rule: "agent-toolcall-marker",
|
|
10559
|
+
pattern: /"(?:tool_call|function_call|call_tool|tool_use|toolUse)"\s*:\s*\{/i,
|
|
10560
|
+
severity: "high",
|
|
10561
|
+
description: 'Injected agent tool-call JSON shape (e.g. {"tool_call":{...}})'
|
|
10562
|
+
},
|
|
10563
|
+
{
|
|
10564
|
+
rule: "agent-tool-name-spoof",
|
|
10565
|
+
pattern: /"name"\s*:\s*"(?:exec|shell|run_command|system|bash|cmd|python|eval|read_file|write_file|delete_file)"/i,
|
|
10566
|
+
severity: "high",
|
|
10567
|
+
description: "Forged tool-name attempting privileged tool invocation"
|
|
10568
|
+
},
|
|
10569
|
+
{
|
|
10570
|
+
rule: "agent-tool-result-marker",
|
|
10571
|
+
pattern: /"(?:tool_result|function_result|tool_output)"\s*:\s*[\{\["]/i,
|
|
10572
|
+
severity: "high",
|
|
10573
|
+
description: "Injected fake tool-result block (trick agent into trusting fabricated output)"
|
|
10574
|
+
},
|
|
10575
|
+
{
|
|
10576
|
+
rule: "ansi-escape-sequence",
|
|
10577
|
+
pattern: /\x1b\[/,
|
|
10578
|
+
severity: "medium",
|
|
10579
|
+
description: "ANSI escape sequence (terminal hijack / output spoofing on CLI agents)"
|
|
10580
|
+
},
|
|
10581
|
+
{
|
|
10582
|
+
rule: "claude-tool-use-tags",
|
|
10583
|
+
pattern: /<\/?\s*(?:tool_use|tool_result|invoke|function_calls?|parameter)\b/i,
|
|
10584
|
+
severity: "high",
|
|
10585
|
+
description: "Claude/OpenAI tool-use XML-style tag forgery"
|
|
10586
|
+
},
|
|
10189
10587
|
// --- LOW severity: ambiguous but worth flagging in strict mode ---
|
|
10190
10588
|
{
|
|
10191
10589
|
rule: "from-now-on",
|
|
@@ -10742,6 +11140,7 @@ function createRedisStore(options) {
|
|
|
10742
11140
|
exports.ArcisError = ArcisError;
|
|
10743
11141
|
exports.ArcisValidationError = ValidationError;
|
|
10744
11142
|
exports.BLOCKED = BLOCKED;
|
|
11143
|
+
exports.CorrelationWindow = CorrelationWindow;
|
|
10745
11144
|
exports.ERRORS = ERRORS;
|
|
10746
11145
|
exports.Guards = Guards;
|
|
10747
11146
|
exports.HEADERS = HEADERS;
|