@arcis/node 1.4.0 → 1.4.2
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 +1 -1
- package/dist/core/constants.d.ts +2 -2
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/index.js +11 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +11 -3
- package/dist/core/index.mjs.map +1 -1
- package/dist/index.js +125 -46
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -47
- package/dist/index.mjs.map +1 -1
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/index.mjs.map +1 -1
- package/dist/middleware/csrf.d.ts.map +1 -1
- package/dist/middleware/index.js +62 -30
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +63 -31
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/sanitizers/encode.d.ts.map +1 -1
- package/dist/sanitizers/index.d.ts +1 -0
- package/dist/sanitizers/index.d.ts.map +1 -1
- package/dist/sanitizers/index.js +90 -32
- package/dist/sanitizers/index.js.map +1 -1
- package/dist/sanitizers/index.mjs +88 -33
- package/dist/sanitizers/index.mjs.map +1 -1
- package/dist/sanitizers/ldap.d.ts +42 -0
- package/dist/sanitizers/ldap.d.ts.map +1 -0
- package/dist/sanitizers/path.d.ts.map +1 -1
- package/dist/sanitizers/sanitize.d.ts.map +1 -1
- package/dist/sanitizers/ssti.d.ts.map +1 -1
- package/dist/stores/index.js +21 -1
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/index.mjs +21 -1
- package/dist/stores/index.mjs.map +1 -1
- package/dist/stores/memory.d.ts +4 -10
- package/dist/stores/memory.d.ts.map +1 -1
- package/dist/validation/index.js +38 -21
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/index.mjs +38 -21
- package/dist/validation/index.mjs.map +1 -1
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { randomBytes } from 'crypto';
|
|
1
|
+
import { randomBytes, timingSafeEqual } from 'crypto';
|
|
2
2
|
|
|
3
3
|
// src/core/constants.ts
|
|
4
4
|
var INPUT = {
|
|
@@ -65,7 +65,15 @@ var XSS_REMOVE_PATTERNS = [
|
|
|
65
65
|
/javascript\s*:/gi,
|
|
66
66
|
/vbscript\s*:/gi,
|
|
67
67
|
/** data: URIs with HTML/script content */
|
|
68
|
-
/data\s*:\s*text\/html[^>\s]*/gi
|
|
68
|
+
/data\s*:\s*text\/html[^>\s]*/gi,
|
|
69
|
+
/** form tag injection — phishing via action= redirection */
|
|
70
|
+
/<form[\s>][^>]*/gi,
|
|
71
|
+
/** meta tag injection — http-equiv refresh or CSP bypass */
|
|
72
|
+
/<meta[\s>][^>]*/gi,
|
|
73
|
+
/** base href hijacking */
|
|
74
|
+
/<base[\s>][^>]*/gi,
|
|
75
|
+
/** link tag injection — stylesheet or preload attacks */
|
|
76
|
+
/<link[\s>][^>]*/gi
|
|
69
77
|
];
|
|
70
78
|
var SQL_PATTERNS = [
|
|
71
79
|
/** SQL keywords */
|
|
@@ -129,8 +137,8 @@ var COMMAND_PATTERNS = [
|
|
|
129
137
|
/[;&|`]/g,
|
|
130
138
|
/** Command substitution: $( ... ) — matched as a pair to reduce false positives */
|
|
131
139
|
/\$\(/g,
|
|
132
|
-
/** URL-encoded
|
|
133
|
-
/%0[
|
|
140
|
+
/** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */
|
|
141
|
+
/%0[0-9a-f]/gi
|
|
134
142
|
];
|
|
135
143
|
var DANGEROUS_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
136
144
|
"__proto__",
|
|
@@ -418,7 +426,24 @@ function createRateLimiter(options = {}) {
|
|
|
418
426
|
}
|
|
419
427
|
next();
|
|
420
428
|
} catch (error) {
|
|
421
|
-
console.error("[arcis] Rate limiter error:", error);
|
|
429
|
+
console.error("[arcis] Rate limiter store error, using in-memory fallback:", error);
|
|
430
|
+
try {
|
|
431
|
+
const key = keyGenerator(req);
|
|
432
|
+
const now = Date.now();
|
|
433
|
+
if (!inMemoryStore[key] || inMemoryStore[key].resetTime < now) {
|
|
434
|
+
inMemoryStore[key] = { count: 1, resetTime: now + windowMs };
|
|
435
|
+
} else {
|
|
436
|
+
inMemoryStore[key].count++;
|
|
437
|
+
}
|
|
438
|
+
const count = inMemoryStore[key].count;
|
|
439
|
+
if (count > max) {
|
|
440
|
+
const resetSeconds = Math.ceil((inMemoryStore[key].resetTime - now) / 1e3);
|
|
441
|
+
res.setHeader("Retry-After", resetSeconds.toString());
|
|
442
|
+
res.status(statusCode).json({ error: message, retryAfter: resetSeconds });
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
} catch {
|
|
446
|
+
}
|
|
422
447
|
next();
|
|
423
448
|
}
|
|
424
449
|
};
|
|
@@ -628,26 +653,31 @@ function sanitizePath(input, collectThreats = false) {
|
|
|
628
653
|
const threats = [];
|
|
629
654
|
let value = input;
|
|
630
655
|
let wasSanitized = false;
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
656
|
+
value = value.normalize("NFKC");
|
|
657
|
+
let prev;
|
|
658
|
+
do {
|
|
659
|
+
prev = value;
|
|
660
|
+
for (const pattern of PATH_PATTERNS) {
|
|
634
661
|
pattern.lastIndex = 0;
|
|
635
|
-
if (
|
|
636
|
-
|
|
637
|
-
if (
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
662
|
+
if (pattern.test(value)) {
|
|
663
|
+
pattern.lastIndex = 0;
|
|
664
|
+
if (collectThreats) {
|
|
665
|
+
const matches = value.match(pattern);
|
|
666
|
+
if (matches) {
|
|
667
|
+
for (const match of matches) {
|
|
668
|
+
threats.push({
|
|
669
|
+
type: "path_traversal",
|
|
670
|
+
pattern: pattern.source,
|
|
671
|
+
original: match
|
|
672
|
+
});
|
|
673
|
+
}
|
|
644
674
|
}
|
|
645
675
|
}
|
|
676
|
+
value = value.replace(pattern, "");
|
|
677
|
+
wasSanitized = true;
|
|
646
678
|
}
|
|
647
|
-
value = value.replace(pattern, "");
|
|
648
|
-
wasSanitized = true;
|
|
649
679
|
}
|
|
650
|
-
}
|
|
680
|
+
} while (value !== prev);
|
|
651
681
|
if (collectThreats) {
|
|
652
682
|
return { value, wasSanitized, threats };
|
|
653
683
|
}
|
|
@@ -705,7 +735,7 @@ function sanitizeString(value, options = {}) {
|
|
|
705
735
|
if (value.length > maxSize) {
|
|
706
736
|
throw new InputTooLargeError(maxSize, value.length);
|
|
707
737
|
}
|
|
708
|
-
const reject = options.mode
|
|
738
|
+
const reject = options.mode === "reject";
|
|
709
739
|
let result = value;
|
|
710
740
|
if (options.sql !== false) {
|
|
711
741
|
if (reject) {
|
|
@@ -1582,11 +1612,9 @@ function generateCsrfToken(length = 32) {
|
|
|
1582
1612
|
function validateCsrfToken(cookieToken, requestToken) {
|
|
1583
1613
|
if (!cookieToken || !requestToken) return false;
|
|
1584
1614
|
if (cookieToken.length !== requestToken.length) return false;
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
}
|
|
1589
|
-
return result === 0;
|
|
1615
|
+
const a = Buffer.from(cookieToken);
|
|
1616
|
+
const b = Buffer.from(requestToken);
|
|
1617
|
+
return timingSafeEqual(a, b);
|
|
1590
1618
|
}
|
|
1591
1619
|
function getRequestToken(req, headerName, fieldName) {
|
|
1592
1620
|
const headerToken = req.headers[headerName.toLowerCase()];
|
|
@@ -1595,10 +1623,6 @@ function getRequestToken(req, headerName, fieldName) {
|
|
|
1595
1623
|
const bodyToken = req.body[fieldName];
|
|
1596
1624
|
if (typeof bodyToken === "string" && bodyToken) return bodyToken;
|
|
1597
1625
|
}
|
|
1598
|
-
if (req.query && fieldName in req.query) {
|
|
1599
|
-
const queryToken = req.query[fieldName];
|
|
1600
|
-
if (typeof queryToken === "string" && queryToken) return queryToken;
|
|
1601
|
-
}
|
|
1602
1626
|
return void 0;
|
|
1603
1627
|
}
|
|
1604
1628
|
function csrfProtection(options = {}) {
|
|
@@ -1681,7 +1705,15 @@ function setCsrfCookie(res, name, token, opts) {
|
|
|
1681
1705
|
if (opts.secure) parts.push("Secure");
|
|
1682
1706
|
parts.push(`SameSite=${opts.sameSite}`);
|
|
1683
1707
|
if (opts.domain) parts.push(`Domain=${opts.domain}`);
|
|
1684
|
-
|
|
1708
|
+
const newCookie = parts.join("; ");
|
|
1709
|
+
const existing = res.getHeader("Set-Cookie");
|
|
1710
|
+
if (existing === void 0) {
|
|
1711
|
+
res.setHeader("Set-Cookie", newCookie);
|
|
1712
|
+
} else if (Array.isArray(existing)) {
|
|
1713
|
+
res.setHeader("Set-Cookie", [...existing, newCookie]);
|
|
1714
|
+
} else {
|
|
1715
|
+
res.setHeader("Set-Cookie", [existing, newCookie]);
|
|
1716
|
+
}
|
|
1685
1717
|
}
|
|
1686
1718
|
function escapeRegex(str) {
|
|
1687
1719
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|