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