@arcis/node 1.4.3 → 1.5.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/LICENSE +21 -0
- package/README.md +43 -5
- package/dist/astro/index.js +6141 -0
- package/dist/astro/index.js.map +1 -0
- package/dist/astro/index.mjs +6136 -0
- package/dist/astro/index.mjs.map +1 -0
- package/dist/bun/index.js +6195 -0
- package/dist/bun/index.js.map +1 -0
- package/dist/bun/index.mjs +6189 -0
- package/dist/bun/index.mjs.map +1 -0
- package/dist/core/constants.d.ts +4 -3
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/index.js +8 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +8 -4
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/types.d.ts +43 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/fastify/index.js +6160 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/fastify/index.mjs +6155 -0
- package/dist/fastify/index.mjs.map +1 -0
- package/dist/guards.d.ts +156 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/hono/index.js +6159 -0
- package/dist/hono/index.js.map +1 -0
- package/dist/hono/index.mjs +6154 -0
- package/dist/hono/index.mjs.map +1 -0
- package/dist/index.d.ts +23 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7365 -305
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7327 -306
- package/dist/index.mjs.map +1 -1
- package/dist/koa/index.js +6158 -0
- package/dist/koa/index.js.map +1 -0
- package/dist/koa/index.mjs +6153 -0
- package/dist/koa/index.mjs.map +1 -0
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/index.mjs.map +1 -1
- package/dist/logging/redactor.d.ts.map +1 -1
- package/dist/middleware/astro.d.ts +64 -0
- package/dist/middleware/astro.d.ts.map +1 -0
- package/dist/middleware/bot-detection.d.ts.map +1 -1
- package/dist/middleware/bun.d.ts +75 -0
- package/dist/middleware/bun.d.ts.map +1 -0
- package/dist/middleware/csrf.d.ts.map +1 -1
- package/dist/middleware/error-handler.d.ts.map +1 -1
- package/dist/middleware/fastify.d.ts +89 -0
- package/dist/middleware/fastify.d.ts.map +1 -0
- package/dist/middleware/graphql.d.ts +35 -0
- package/dist/middleware/graphql.d.ts.map +1 -0
- package/dist/middleware/hono.d.ts +63 -0
- package/dist/middleware/hono.d.ts.map +1 -0
- package/dist/middleware/index.d.ts +12 -0
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +6693 -122
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +6683 -123
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/middleware/koa.d.ts +84 -0
- package/dist/middleware/koa.d.ts.map +1 -0
- package/dist/middleware/main.d.ts +0 -30
- package/dist/middleware/main.d.ts.map +1 -1
- package/dist/middleware/mass-assign.d.ts +81 -0
- package/dist/middleware/mass-assign.d.ts.map +1 -0
- package/dist/middleware/method-allowlist.d.ts +66 -0
- package/dist/middleware/method-allowlist.d.ts.map +1 -0
- package/dist/middleware/nestjs.d.ts +62 -0
- package/dist/middleware/nestjs.d.ts.map +1 -0
- package/dist/middleware/nextjs.d.ts +102 -0
- package/dist/middleware/nextjs.d.ts.map +1 -0
- package/dist/middleware/nuxt.d.ts +61 -0
- package/dist/middleware/nuxt.d.ts.map +1 -0
- package/dist/middleware/overload.d.ts +92 -0
- package/dist/middleware/overload.d.ts.map +1 -0
- package/dist/middleware/protect.d.ts +91 -0
- package/dist/middleware/protect.d.ts.map +1 -0
- package/dist/middleware/rate-limit-sliding.d.ts.map +1 -1
- package/dist/middleware/rate-limit-token.d.ts.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/response-splitting.d.ts +83 -0
- package/dist/middleware/response-splitting.d.ts.map +1 -0
- package/dist/middleware/sveltekit.d.ts +68 -0
- package/dist/middleware/sveltekit.d.ts.map +1 -0
- package/dist/middleware/token-budget.d.ts +75 -0
- package/dist/middleware/token-budget.d.ts.map +1 -0
- package/dist/nestjs/index.js +1724 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/nestjs/index.mjs +1717 -0
- package/dist/nestjs/index.mjs.map +1 -0
- package/dist/nextjs/index.js +6184 -0
- package/dist/nextjs/index.js.map +1 -0
- package/dist/nextjs/index.mjs +6178 -0
- package/dist/nextjs/index.mjs.map +1 -0
- package/dist/nuxt/index.js +6141 -0
- package/dist/nuxt/index.js.map +1 -0
- package/dist/nuxt/index.mjs +6136 -0
- package/dist/nuxt/index.mjs.map +1 -0
- package/dist/sanitizers/encode.d.ts.map +1 -1
- package/dist/sanitizers/graphql.d.ts +72 -0
- package/dist/sanitizers/graphql.d.ts.map +1 -0
- package/dist/sanitizers/headers.d.ts +18 -0
- package/dist/sanitizers/headers.d.ts.map +1 -1
- package/dist/sanitizers/index.d.ts +6 -2
- package/dist/sanitizers/index.d.ts.map +1 -1
- package/dist/sanitizers/index.js +339 -197
- package/dist/sanitizers/index.js.map +1 -1
- package/dist/sanitizers/index.mjs +333 -198
- package/dist/sanitizers/index.mjs.map +1 -1
- package/dist/sanitizers/prompt-injection.d.ts +62 -0
- package/dist/sanitizers/prompt-injection.d.ts.map +1 -0
- package/dist/sanitizers/sanitize.d.ts +13 -0
- package/dist/sanitizers/sanitize.d.ts.map +1 -1
- package/dist/sanitizers/xpath.d.ts +37 -0
- package/dist/sanitizers/xpath.d.ts.map +1 -0
- package/dist/stores/index.js +4 -4
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/index.mjs +4 -4
- package/dist/stores/index.mjs.map +1 -1
- package/dist/stores/redis.d.ts +7 -1
- package/dist/stores/redis.d.ts.map +1 -1
- package/dist/sveltekit/index.js +6142 -0
- package/dist/sveltekit/index.js.map +1 -0
- package/dist/sveltekit/index.mjs +6137 -0
- package/dist/sveltekit/index.mjs.map +1 -0
- package/dist/telemetry/client.d.ts +3 -0
- package/dist/telemetry/client.d.ts.map +1 -1
- package/dist/telemetry/types.d.ts +12 -0
- package/dist/telemetry/types.d.ts.map +1 -1
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/index.js +137 -12
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/index.mjs +116 -13
- package/dist/validation/index.mjs.map +1 -1
- package/dist/validation/redirect.d.ts.map +1 -1
- package/dist/validation/schema.d.ts.map +1 -1
- package/dist/validation/url-async.d.ts +137 -0
- package/dist/validation/url-async.d.ts.map +1 -0
- package/package.json +52 -4
- package/scripts/postinstall.cjs +26 -0
package/dist/sanitizers/index.js
CHANGED
|
@@ -35,7 +35,10 @@ var XSS_PATTERNS = [
|
|
|
35
35
|
/** base href hijacking — redirects all relative URLs to attacker domain */
|
|
36
36
|
/<base[\s>]/gi,
|
|
37
37
|
/** link tag injection — stylesheet or preload CSRF attacks */
|
|
38
|
-
/<link[\s>]/gi
|
|
38
|
+
/<link[\s>]/gi,
|
|
39
|
+
/** style tag — CSS expression() / behavior: / IE-era attacks. Mirrors
|
|
40
|
+
* Python's xss-style-tag from packages/core/patterns.json. */
|
|
41
|
+
/<style[\s>]/gi
|
|
39
42
|
];
|
|
40
43
|
var XSS_REMOVE_PATTERNS = [
|
|
41
44
|
/** Full script blocks (content + tags) */
|
|
@@ -64,8 +67,8 @@ var XSS_REMOVE_PATTERNS = [
|
|
|
64
67
|
/** javascript: and vbscript: protocols (allow optional spaces before colon) */
|
|
65
68
|
/javascript\s*:/gi,
|
|
66
69
|
/vbscript\s*:/gi,
|
|
67
|
-
/** data: URIs with HTML
|
|
68
|
-
/data\s*:\s*text\/html[^>\s]*/gi,
|
|
70
|
+
/** data: URIs with HTML or SVG content (SVG can run JS via inline event handlers) */
|
|
71
|
+
/data\s*:\s*(?:text\/html|image\/svg)[^>\s]*/gi,
|
|
69
72
|
/** form tag injection — phishing via action= redirection */
|
|
70
73
|
/<form[\s>][^>]*/gi,
|
|
71
74
|
/** meta tag injection — http-equiv refresh or CSP bypass */
|
|
@@ -428,150 +431,6 @@ function detectCommandInjection(input) {
|
|
|
428
431
|
return false;
|
|
429
432
|
}
|
|
430
433
|
|
|
431
|
-
// src/sanitizers/sanitize.ts
|
|
432
|
-
function sanitizeString(value, options = {}) {
|
|
433
|
-
if (typeof value !== "string") return value;
|
|
434
|
-
const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
|
|
435
|
-
if (value.length > maxSize) {
|
|
436
|
-
throw new InputTooLargeError(maxSize, value.length);
|
|
437
|
-
}
|
|
438
|
-
const reject = options.mode === "reject";
|
|
439
|
-
let result = value;
|
|
440
|
-
if (options.sql !== false) {
|
|
441
|
-
if (reject) {
|
|
442
|
-
if (detectSql(result)) {
|
|
443
|
-
throw new SecurityThreatError("sql_injection", "SQL pattern detected in input");
|
|
444
|
-
}
|
|
445
|
-
} else {
|
|
446
|
-
result = sanitizeSql(result);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (options.path !== false) {
|
|
450
|
-
result = sanitizePath(result);
|
|
451
|
-
}
|
|
452
|
-
if (options.command !== false) {
|
|
453
|
-
if (reject) {
|
|
454
|
-
if (detectCommandInjection(result)) {
|
|
455
|
-
throw new SecurityThreatError("command_injection", "Shell metacharacter detected in input");
|
|
456
|
-
}
|
|
457
|
-
} else {
|
|
458
|
-
result = sanitizeCommand(result);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
if (options.xss !== false) {
|
|
462
|
-
result = sanitizeXss(result, false, options.htmlEncode ?? false);
|
|
463
|
-
}
|
|
464
|
-
return result;
|
|
465
|
-
}
|
|
466
|
-
function sanitizeObject(obj, options = {}) {
|
|
467
|
-
if (obj === null || obj === void 0) return obj;
|
|
468
|
-
if (typeof obj === "string") return sanitizeString(obj, options);
|
|
469
|
-
if (typeof obj !== "object") return obj;
|
|
470
|
-
if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
|
|
471
|
-
const result = sanitizeObjectDepth(obj, options, 0);
|
|
472
|
-
return options.freeze ? Object.freeze(result) : result;
|
|
473
|
-
}
|
|
474
|
-
function sanitizeObjectDepth(obj, options, depth) {
|
|
475
|
-
if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
|
|
476
|
-
const result = {};
|
|
477
|
-
for (const key of Object.keys(obj)) {
|
|
478
|
-
if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
|
|
479
|
-
continue;
|
|
480
|
-
}
|
|
481
|
-
if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {
|
|
482
|
-
continue;
|
|
483
|
-
}
|
|
484
|
-
const sanitizedKey = sanitizeString(key, options);
|
|
485
|
-
const value = obj[key];
|
|
486
|
-
if (value === null || value === void 0) {
|
|
487
|
-
result[sanitizedKey] = value;
|
|
488
|
-
} else if (typeof value === "string") {
|
|
489
|
-
result[sanitizedKey] = sanitizeString(value, options);
|
|
490
|
-
} else if (Array.isArray(value)) {
|
|
491
|
-
result[sanitizedKey] = value.map((item) => sanitizeObject(item, options));
|
|
492
|
-
} else if (typeof value === "object") {
|
|
493
|
-
result[sanitizedKey] = sanitizeObjectDepth(value, options, depth + 1);
|
|
494
|
-
} else {
|
|
495
|
-
result[sanitizedKey] = value;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
return result;
|
|
499
|
-
}
|
|
500
|
-
function createSanitizer(options = {}) {
|
|
501
|
-
return (req, _res, next) => {
|
|
502
|
-
try {
|
|
503
|
-
if (req.body && typeof req.body === "object") {
|
|
504
|
-
req.body = sanitizeObject(req.body, options);
|
|
505
|
-
}
|
|
506
|
-
if (req.query && typeof req.query === "object") {
|
|
507
|
-
const sanitizedQuery = sanitizeObject(req.query, options);
|
|
508
|
-
Object.defineProperty(req, "query", { value: sanitizedQuery, writable: true, configurable: true });
|
|
509
|
-
}
|
|
510
|
-
if (req.params && typeof req.params === "object") {
|
|
511
|
-
const sanitizedParams = sanitizeObject(req.params, options);
|
|
512
|
-
Object.defineProperty(req, "params", { value: sanitizedParams, writable: true, configurable: true });
|
|
513
|
-
}
|
|
514
|
-
next();
|
|
515
|
-
} catch (err) {
|
|
516
|
-
next(err);
|
|
517
|
-
}
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// src/sanitizers/nosql.ts
|
|
522
|
-
function isDangerousNoSqlKey(key) {
|
|
523
|
-
return NOSQL_DANGEROUS_KEYS.has(key);
|
|
524
|
-
}
|
|
525
|
-
function detectNoSqlInjection(obj, maxDepth = 10) {
|
|
526
|
-
if (maxDepth <= 0) return false;
|
|
527
|
-
if (obj === null || typeof obj !== "object") return false;
|
|
528
|
-
if (Array.isArray(obj)) {
|
|
529
|
-
return obj.some((item) => detectNoSqlInjection(item, maxDepth - 1));
|
|
530
|
-
}
|
|
531
|
-
for (const key of Object.keys(obj)) {
|
|
532
|
-
if (isDangerousNoSqlKey(key)) {
|
|
533
|
-
return true;
|
|
534
|
-
}
|
|
535
|
-
const value = obj[key];
|
|
536
|
-
if (typeof value === "object" && value !== null) {
|
|
537
|
-
if (detectNoSqlInjection(value, maxDepth - 1)) {
|
|
538
|
-
return true;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
return false;
|
|
543
|
-
}
|
|
544
|
-
function getDangerousOperators() {
|
|
545
|
-
return Array.from(NOSQL_DANGEROUS_KEYS);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// src/sanitizers/prototype.ts
|
|
549
|
-
function isDangerousProtoKey(key) {
|
|
550
|
-
return DANGEROUS_PROTO_KEYS.has(key.toLowerCase());
|
|
551
|
-
}
|
|
552
|
-
function detectPrototypePollution(obj, maxDepth = 10) {
|
|
553
|
-
if (maxDepth <= 0) return false;
|
|
554
|
-
if (obj === null || typeof obj !== "object") return false;
|
|
555
|
-
if (Array.isArray(obj)) {
|
|
556
|
-
return obj.some((item) => detectPrototypePollution(item, maxDepth - 1));
|
|
557
|
-
}
|
|
558
|
-
for (const key of Object.keys(obj)) {
|
|
559
|
-
if (DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
|
|
560
|
-
return true;
|
|
561
|
-
}
|
|
562
|
-
const value = obj[key];
|
|
563
|
-
if (typeof value === "object" && value !== null) {
|
|
564
|
-
if (detectPrototypePollution(value, maxDepth - 1)) {
|
|
565
|
-
return true;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
return false;
|
|
570
|
-
}
|
|
571
|
-
function getDangerousProtoKeys() {
|
|
572
|
-
return Array.from(DANGEROUS_PROTO_KEYS);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
434
|
// src/sanitizers/ssti.ts
|
|
576
435
|
var SSTI_DETECT_PATTERNS = [
|
|
577
436
|
/** Jinja2 / Twig / Nunjucks: {{ ... }} */
|
|
@@ -734,42 +593,36 @@ function detectXxe(input) {
|
|
|
734
593
|
return false;
|
|
735
594
|
}
|
|
736
595
|
|
|
737
|
-
// src/sanitizers/
|
|
738
|
-
var
|
|
739
|
-
var
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
function
|
|
744
|
-
if (typeof
|
|
745
|
-
|
|
746
|
-
}
|
|
747
|
-
if (callback.length > maxLength) {
|
|
748
|
-
return null;
|
|
749
|
-
}
|
|
750
|
-
if (!SAFE_CALLBACK_PATTERN.test(callback)) {
|
|
751
|
-
return null;
|
|
752
|
-
}
|
|
753
|
-
for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
|
|
754
|
-
if (pattern.test(callback)) {
|
|
755
|
-
return null;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return callback;
|
|
596
|
+
// src/sanitizers/ldap.ts
|
|
597
|
+
var LDAP_FILTER_CHARS = /[*()\\\x00]/g;
|
|
598
|
+
var LDAP_DN_CHARS = /[,+<>;"=\/\\\x00*()\x00]/g;
|
|
599
|
+
var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
|
|
600
|
+
var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
|
|
601
|
+
var escapeChar = (char) => "\\" + char.charCodeAt(0).toString(16).padStart(2, "0");
|
|
602
|
+
function sanitizeLdapFilter(input) {
|
|
603
|
+
if (typeof input !== "string") return String(input);
|
|
604
|
+
return input.replace(LDAP_FILTER_CHARS, escapeChar);
|
|
759
605
|
}
|
|
760
|
-
function
|
|
761
|
-
if (typeof
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
606
|
+
function sanitizeLdapDn(input) {
|
|
607
|
+
if (typeof input !== "string") return String(input);
|
|
608
|
+
return input.replace(LDAP_DN_CHARS, escapeChar);
|
|
609
|
+
}
|
|
610
|
+
function detectLdapInjection(input) {
|
|
611
|
+
if (typeof input !== "string") return false;
|
|
612
|
+
return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// src/sanitizers/xpath.ts
|
|
616
|
+
var XPATH_INJECTION_CHARS = /['"|,()]/;
|
|
617
|
+
var XPATH_INJECTION_PATTERN = /('\s*(or|and)\s*'|"\s*(or|and)\s*"|\)\s*(or|and)\s*\(|\|\s*\/)/i;
|
|
618
|
+
function detectXpathInjection(input) {
|
|
619
|
+
if (typeof input !== "string" || input.length === 0) return false;
|
|
620
|
+
if (!XPATH_INJECTION_CHARS.test(input)) return false;
|
|
621
|
+
return XPATH_INJECTION_PATTERN.test(input);
|
|
622
|
+
}
|
|
623
|
+
function sanitizeXpath(input) {
|
|
624
|
+
if (typeof input !== "string") return String(input);
|
|
625
|
+
return input.replace(/['"|,]/g, "");
|
|
773
626
|
}
|
|
774
627
|
|
|
775
628
|
// src/sanitizers/headers.ts
|
|
@@ -820,6 +673,263 @@ function detectHeaderInjection(input) {
|
|
|
820
673
|
HEADER_INJECTION_PATTERN.lastIndex = 0;
|
|
821
674
|
return HEADER_INJECTION_PATTERN.test(input);
|
|
822
675
|
}
|
|
676
|
+
var sanitizeEmailHeader = sanitizeHeaderValue;
|
|
677
|
+
var detectEmailHeaderInjection = detectHeaderInjection;
|
|
678
|
+
|
|
679
|
+
// src/sanitizers/sanitize.ts
|
|
680
|
+
function sanitizeString(value, options = {}) {
|
|
681
|
+
if (typeof value !== "string") return value;
|
|
682
|
+
const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
|
|
683
|
+
if (value.length > maxSize) {
|
|
684
|
+
throw new InputTooLargeError(maxSize, value.length);
|
|
685
|
+
}
|
|
686
|
+
const reject = options.mode === "reject";
|
|
687
|
+
let result = value;
|
|
688
|
+
if (options.sql !== false) {
|
|
689
|
+
if (reject) {
|
|
690
|
+
if (detectSql(result)) {
|
|
691
|
+
throw new SecurityThreatError("sql_injection", "SQL pattern detected in input");
|
|
692
|
+
}
|
|
693
|
+
} else {
|
|
694
|
+
result = sanitizeSql(result);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (options.path !== false) {
|
|
698
|
+
result = sanitizePath(result);
|
|
699
|
+
}
|
|
700
|
+
if (options.command !== false) {
|
|
701
|
+
if (reject) {
|
|
702
|
+
if (detectCommandInjection(result)) {
|
|
703
|
+
throw new SecurityThreatError("command_injection", "Shell metacharacter detected in input");
|
|
704
|
+
}
|
|
705
|
+
} else {
|
|
706
|
+
result = sanitizeCommand(result);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (options.xss !== false) {
|
|
710
|
+
result = sanitizeXss(result, false, options.htmlEncode ?? false);
|
|
711
|
+
}
|
|
712
|
+
return result;
|
|
713
|
+
}
|
|
714
|
+
function sanitizeObject(obj, options = {}) {
|
|
715
|
+
if (obj === null || obj === void 0) return obj;
|
|
716
|
+
if (typeof obj === "string") return sanitizeString(obj, options);
|
|
717
|
+
if (typeof obj !== "object") return obj;
|
|
718
|
+
if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
|
|
719
|
+
const result = sanitizeObjectDepth(obj, options, 0);
|
|
720
|
+
return options.freeze ? Object.freeze(result) : result;
|
|
721
|
+
}
|
|
722
|
+
function sanitizeObjectDepth(obj, options, depth) {
|
|
723
|
+
if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
|
|
724
|
+
const result = {};
|
|
725
|
+
for (const key of Object.keys(obj)) {
|
|
726
|
+
if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
const sanitizedKey = sanitizeString(key, options);
|
|
733
|
+
const value = obj[key];
|
|
734
|
+
if (value === null || value === void 0) {
|
|
735
|
+
result[sanitizedKey] = value;
|
|
736
|
+
} else if (typeof value === "string") {
|
|
737
|
+
result[sanitizedKey] = sanitizeString(value, options);
|
|
738
|
+
} else if (Array.isArray(value)) {
|
|
739
|
+
result[sanitizedKey] = value.map((item) => sanitizeObject(item, options));
|
|
740
|
+
} else if (typeof value === "object") {
|
|
741
|
+
result[sanitizedKey] = sanitizeObjectDepth(value, options, depth + 1);
|
|
742
|
+
} else {
|
|
743
|
+
result[sanitizedKey] = value;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return result;
|
|
747
|
+
}
|
|
748
|
+
function scanThreats(data, depth = 0) {
|
|
749
|
+
if (depth > INPUT.MAX_RECURSION_DEPTH) return null;
|
|
750
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
751
|
+
for (const key of Object.keys(data)) {
|
|
752
|
+
const lower = key.toLowerCase();
|
|
753
|
+
if (DANGEROUS_PROTO_KEYS.has(lower)) {
|
|
754
|
+
return { vector: "prototype", rule: "prototype/match", matchedPattern: key };
|
|
755
|
+
}
|
|
756
|
+
if (NOSQL_DANGEROUS_KEYS.has(key)) {
|
|
757
|
+
return { vector: "nosql", rule: "nosql/match", matchedPattern: key };
|
|
758
|
+
}
|
|
759
|
+
const inner = scanThreats(data[key], depth + 1);
|
|
760
|
+
if (inner) return inner;
|
|
761
|
+
}
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
if (Array.isArray(data)) {
|
|
765
|
+
for (const item of data) {
|
|
766
|
+
const inner = scanThreats(item, depth + 1);
|
|
767
|
+
if (inner) return inner;
|
|
768
|
+
}
|
|
769
|
+
return null;
|
|
770
|
+
}
|
|
771
|
+
if (typeof data !== "string") return null;
|
|
772
|
+
const sample = data.slice(0, 80);
|
|
773
|
+
if (detectXss(data)) {
|
|
774
|
+
return { vector: "xss", rule: "xss/match", matchedPattern: sample };
|
|
775
|
+
}
|
|
776
|
+
if (detectSsti(data)) {
|
|
777
|
+
return { vector: "ssti", rule: "ssti/match", matchedPattern: sample };
|
|
778
|
+
}
|
|
779
|
+
if (detectXxe(data)) {
|
|
780
|
+
return { vector: "xxe", rule: "xxe/match", matchedPattern: sample };
|
|
781
|
+
}
|
|
782
|
+
if (detectSql(data)) {
|
|
783
|
+
return { vector: "sql", rule: "sql/match", matchedPattern: sample };
|
|
784
|
+
}
|
|
785
|
+
if (detectPathTraversal(data)) {
|
|
786
|
+
return { vector: "path", rule: "path/match", matchedPattern: sample };
|
|
787
|
+
}
|
|
788
|
+
if (detectCommandInjection(data)) {
|
|
789
|
+
return { vector: "command", rule: "command/match", matchedPattern: sample };
|
|
790
|
+
}
|
|
791
|
+
if (detectLdapInjection(data)) {
|
|
792
|
+
return { vector: "ldap", rule: "ldap/match", matchedPattern: sample };
|
|
793
|
+
}
|
|
794
|
+
if (detectXpathInjection(data)) {
|
|
795
|
+
return { vector: "xpath", rule: "xpath/match", matchedPattern: sample };
|
|
796
|
+
}
|
|
797
|
+
if (detectHeaderInjection(data)) {
|
|
798
|
+
return { vector: "header", rule: "header/match", matchedPattern: sample };
|
|
799
|
+
}
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
function createSanitizer(options = {}) {
|
|
803
|
+
return (req, res, next) => {
|
|
804
|
+
try {
|
|
805
|
+
if (options.block) {
|
|
806
|
+
const hit = scanThreats(req.body) || scanThreats(req.query) || scanThreats(req.params) || scanThreats(req.path);
|
|
807
|
+
if (hit) {
|
|
808
|
+
req.__arcis = {
|
|
809
|
+
vector: hit.vector,
|
|
810
|
+
rule: hit.rule,
|
|
811
|
+
severity: "high",
|
|
812
|
+
matchedPattern: hit.matchedPattern,
|
|
813
|
+
reason: `${hit.vector} pattern detected in request`,
|
|
814
|
+
decision: "deny"
|
|
815
|
+
};
|
|
816
|
+
res.status(403).json({
|
|
817
|
+
error: "Request blocked for security reasons",
|
|
818
|
+
code: "SECURITY_THREAT",
|
|
819
|
+
vector: hit.vector
|
|
820
|
+
});
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (req.body && typeof req.body === "object") {
|
|
825
|
+
req.body = sanitizeObject(req.body, options);
|
|
826
|
+
}
|
|
827
|
+
if (req.query && typeof req.query === "object") {
|
|
828
|
+
const sanitizedQuery = sanitizeObject(req.query, options);
|
|
829
|
+
Object.defineProperty(req, "query", { value: sanitizedQuery, writable: true, configurable: true });
|
|
830
|
+
}
|
|
831
|
+
if (req.params && typeof req.params === "object") {
|
|
832
|
+
const sanitizedParams = sanitizeObject(req.params, options);
|
|
833
|
+
Object.defineProperty(req, "params", { value: sanitizedParams, writable: true, configurable: true });
|
|
834
|
+
}
|
|
835
|
+
next();
|
|
836
|
+
} catch (err) {
|
|
837
|
+
next(err);
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/sanitizers/nosql.ts
|
|
843
|
+
function isDangerousNoSqlKey(key) {
|
|
844
|
+
return NOSQL_DANGEROUS_KEYS.has(key);
|
|
845
|
+
}
|
|
846
|
+
function detectNoSqlInjection(obj, maxDepth = 10) {
|
|
847
|
+
if (maxDepth <= 0) return false;
|
|
848
|
+
if (obj === null || typeof obj !== "object") return false;
|
|
849
|
+
if (Array.isArray(obj)) {
|
|
850
|
+
return obj.some((item) => detectNoSqlInjection(item, maxDepth - 1));
|
|
851
|
+
}
|
|
852
|
+
for (const key of Object.keys(obj)) {
|
|
853
|
+
if (isDangerousNoSqlKey(key)) {
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
const value = obj[key];
|
|
857
|
+
if (typeof value === "object" && value !== null) {
|
|
858
|
+
if (detectNoSqlInjection(value, maxDepth - 1)) {
|
|
859
|
+
return true;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return false;
|
|
864
|
+
}
|
|
865
|
+
function getDangerousOperators() {
|
|
866
|
+
return Array.from(NOSQL_DANGEROUS_KEYS);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/sanitizers/prototype.ts
|
|
870
|
+
function isDangerousProtoKey(key) {
|
|
871
|
+
return DANGEROUS_PROTO_KEYS.has(key.toLowerCase());
|
|
872
|
+
}
|
|
873
|
+
function detectPrototypePollution(obj, maxDepth = 10) {
|
|
874
|
+
if (maxDepth <= 0) return false;
|
|
875
|
+
if (obj === null || typeof obj !== "object") return false;
|
|
876
|
+
if (Array.isArray(obj)) {
|
|
877
|
+
return obj.some((item) => detectPrototypePollution(item, maxDepth - 1));
|
|
878
|
+
}
|
|
879
|
+
for (const key of Object.keys(obj)) {
|
|
880
|
+
if (DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
883
|
+
const value = obj[key];
|
|
884
|
+
if (typeof value === "object" && value !== null) {
|
|
885
|
+
if (detectPrototypePollution(value, maxDepth - 1)) {
|
|
886
|
+
return true;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return false;
|
|
891
|
+
}
|
|
892
|
+
function getDangerousProtoKeys() {
|
|
893
|
+
return Array.from(DANGEROUS_PROTO_KEYS);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// src/sanitizers/jsonp.ts
|
|
897
|
+
var SAFE_CALLBACK_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/;
|
|
898
|
+
var DANGEROUS_CALLBACK_PATTERNS = [
|
|
899
|
+
/\.\./
|
|
900
|
+
// prototype chain traversal
|
|
901
|
+
];
|
|
902
|
+
function sanitizeJsonpCallback(callback, maxLength = 128) {
|
|
903
|
+
if (typeof callback !== "string" || callback.length === 0) {
|
|
904
|
+
return null;
|
|
905
|
+
}
|
|
906
|
+
if (callback.length > maxLength) {
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
909
|
+
if (!SAFE_CALLBACK_PATTERN.test(callback)) {
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
|
|
913
|
+
if (pattern.test(callback)) {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return callback;
|
|
918
|
+
}
|
|
919
|
+
function detectJsonpInjection(callback) {
|
|
920
|
+
if (typeof callback !== "string" || callback.length === 0) {
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
if (!SAFE_CALLBACK_PATTERN.test(callback)) {
|
|
924
|
+
return true;
|
|
925
|
+
}
|
|
926
|
+
for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
|
|
927
|
+
if (pattern.test(callback)) {
|
|
928
|
+
return true;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
823
933
|
|
|
824
934
|
// src/sanitizers/pii.ts
|
|
825
935
|
var EMAIL_RE = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z]{2,})+/g;
|
|
@@ -986,6 +1096,7 @@ function encodeForJs(value) {
|
|
|
986
1096
|
let result = "";
|
|
987
1097
|
for (const char of value) {
|
|
988
1098
|
const cp = char.codePointAt(0);
|
|
1099
|
+
if (cp === void 0) continue;
|
|
989
1100
|
if (cp >= 48 && cp <= 57 || // 0-9
|
|
990
1101
|
cp >= 65 && cp <= 90 || // A-Z
|
|
991
1102
|
cp >= 97 && cp <= 122) {
|
|
@@ -1022,27 +1133,53 @@ function encodeForCss(value) {
|
|
|
1022
1133
|
return result;
|
|
1023
1134
|
}
|
|
1024
1135
|
|
|
1025
|
-
// src/sanitizers/
|
|
1026
|
-
var
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1136
|
+
// src/sanitizers/graphql.ts
|
|
1137
|
+
var DEFAULTS = {
|
|
1138
|
+
maxDepth: 10,
|
|
1139
|
+
maxLength: 1e4,
|
|
1140
|
+
blockIntrospection: true
|
|
1141
|
+
};
|
|
1142
|
+
var INTROSPECTION_PATTERN = /\b__(schema|type|typeKind|directive)\b/;
|
|
1143
|
+
function computeDepth(query) {
|
|
1144
|
+
let depth = 0;
|
|
1145
|
+
let max = 0;
|
|
1146
|
+
for (let i = 0; i < query.length; i++) {
|
|
1147
|
+
const c = query.charCodeAt(i);
|
|
1148
|
+
if (c === 123) {
|
|
1149
|
+
depth++;
|
|
1150
|
+
if (depth > max) max = depth;
|
|
1151
|
+
} else if (c === 125) {
|
|
1152
|
+
if (depth > 0) depth--;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
return max;
|
|
1034
1156
|
}
|
|
1035
|
-
function
|
|
1036
|
-
|
|
1037
|
-
|
|
1157
|
+
function inspectGraphqlQuery(query, options = {}) {
|
|
1158
|
+
const maxDepth = options.maxDepth ?? DEFAULTS.maxDepth;
|
|
1159
|
+
const maxLength = options.maxLength ?? DEFAULTS.maxLength;
|
|
1160
|
+
const blockIntrospection = options.blockIntrospection ?? DEFAULTS.blockIntrospection;
|
|
1161
|
+
const length = query.length;
|
|
1162
|
+
const depth = computeDepth(query);
|
|
1163
|
+
if (depth > maxDepth) {
|
|
1164
|
+
return { blocked: true, reason: "depth", depth, length };
|
|
1165
|
+
}
|
|
1166
|
+
if (blockIntrospection && INTROSPECTION_PATTERN.test(query)) {
|
|
1167
|
+
return { blocked: true, reason: "introspection", depth, length };
|
|
1168
|
+
}
|
|
1169
|
+
if (length > maxLength) {
|
|
1170
|
+
return { blocked: true, reason: "length", depth, length };
|
|
1171
|
+
}
|
|
1172
|
+
return { blocked: false, depth, length };
|
|
1038
1173
|
}
|
|
1039
|
-
function
|
|
1040
|
-
if (typeof
|
|
1041
|
-
return
|
|
1174
|
+
function detectGraphqlAbuse(query, options) {
|
|
1175
|
+
if (typeof query !== "string" || query.length === 0) return false;
|
|
1176
|
+
return inspectGraphqlQuery(query, options).blocked;
|
|
1042
1177
|
}
|
|
1043
1178
|
|
|
1044
1179
|
exports.createSanitizer = createSanitizer;
|
|
1045
1180
|
exports.detectCommandInjection = detectCommandInjection;
|
|
1181
|
+
exports.detectEmailHeaderInjection = detectEmailHeaderInjection;
|
|
1182
|
+
exports.detectGraphqlAbuse = detectGraphqlAbuse;
|
|
1046
1183
|
exports.detectHeaderInjection = detectHeaderInjection;
|
|
1047
1184
|
exports.detectJsonpInjection = detectJsonpInjection;
|
|
1048
1185
|
exports.detectLdapInjection = detectLdapInjection;
|
|
@@ -1052,6 +1189,7 @@ exports.detectPii = detectPii;
|
|
|
1052
1189
|
exports.detectPrototypePollution = detectPrototypePollution;
|
|
1053
1190
|
exports.detectSql = detectSql;
|
|
1054
1191
|
exports.detectSsti = detectSsti;
|
|
1192
|
+
exports.detectXpathInjection = detectXpathInjection;
|
|
1055
1193
|
exports.detectXss = detectXss;
|
|
1056
1194
|
exports.detectXxe = detectXxe;
|
|
1057
1195
|
exports.encodeForAttribute = encodeForAttribute;
|
|
@@ -1062,12 +1200,14 @@ exports.encodeForUrl = encodeForUrl;
|
|
|
1062
1200
|
exports.encodeHtmlEntities = encodeHtmlEntities;
|
|
1063
1201
|
exports.getDangerousOperators = getDangerousOperators;
|
|
1064
1202
|
exports.getDangerousProtoKeys = getDangerousProtoKeys;
|
|
1203
|
+
exports.inspectGraphqlQuery = inspectGraphqlQuery;
|
|
1065
1204
|
exports.isDangerousNoSqlKey = isDangerousNoSqlKey;
|
|
1066
1205
|
exports.isDangerousProtoKey = isDangerousProtoKey;
|
|
1067
1206
|
exports.isPlainObject = isPlainObject;
|
|
1068
1207
|
exports.redactObjectPii = redactObjectPii;
|
|
1069
1208
|
exports.redactPii = redactPii;
|
|
1070
1209
|
exports.sanitizeCommand = sanitizeCommand;
|
|
1210
|
+
exports.sanitizeEmailHeader = sanitizeEmailHeader;
|
|
1071
1211
|
exports.sanitizeHeaderValue = sanitizeHeaderValue;
|
|
1072
1212
|
exports.sanitizeHeaders = sanitizeHeaders;
|
|
1073
1213
|
exports.sanitizeJsonpCallback = sanitizeJsonpCallback;
|
|
@@ -1078,9 +1218,11 @@ exports.sanitizePath = sanitizePath;
|
|
|
1078
1218
|
exports.sanitizeSql = sanitizeSql;
|
|
1079
1219
|
exports.sanitizeSsti = sanitizeSsti;
|
|
1080
1220
|
exports.sanitizeString = sanitizeString;
|
|
1221
|
+
exports.sanitizeXpath = sanitizeXpath;
|
|
1081
1222
|
exports.sanitizeXss = sanitizeXss;
|
|
1082
1223
|
exports.sanitizeXxe = sanitizeXxe;
|
|
1083
1224
|
exports.scanObjectPii = scanObjectPii;
|
|
1084
1225
|
exports.scanPii = scanPii;
|
|
1226
|
+
exports.scanThreats = scanThreats;
|
|
1085
1227
|
//# sourceMappingURL=index.js.map
|
|
1086
1228
|
//# sourceMappingURL=index.js.map
|