xploitscan-shared-rules 1.8.1 → 1.9.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 +1 -1
- package/dist/index.cjs +109 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -329,8 +329,69 @@ var TAINTED_REQUEST_OBJECTS = /* @__PURE__ */ new Set([
|
|
|
329
329
|
"event"
|
|
330
330
|
// Lambda
|
|
331
331
|
]);
|
|
332
|
+
var FS_READ_METHODS = /* @__PURE__ */ new Set(["readFile", "readFileSync"]);
|
|
333
|
+
var PARSE_OBJECTS = /* @__PURE__ */ new Set([
|
|
334
|
+
"JSON",
|
|
335
|
+
"csv",
|
|
336
|
+
"papa",
|
|
337
|
+
"Papa",
|
|
338
|
+
"yaml",
|
|
339
|
+
"YAML",
|
|
340
|
+
"toml",
|
|
341
|
+
"qs",
|
|
342
|
+
"querystring"
|
|
343
|
+
]);
|
|
344
|
+
var PARSE_BARE = /* @__PURE__ */ new Set(["parse", "parseSync"]);
|
|
345
|
+
var ARRAY_ELEMENT_METHODS = /* @__PURE__ */ new Set([
|
|
346
|
+
"map",
|
|
347
|
+
"filter",
|
|
348
|
+
"forEach",
|
|
349
|
+
"find",
|
|
350
|
+
"findIndex",
|
|
351
|
+
"flatMap",
|
|
352
|
+
"some",
|
|
353
|
+
"every"
|
|
354
|
+
]);
|
|
355
|
+
var ARRAY_REDUCE_METHODS = /* @__PURE__ */ new Set(["reduce", "reduceRight"]);
|
|
356
|
+
function callPreservesTaint(node, rec) {
|
|
357
|
+
const callee = node.callee;
|
|
358
|
+
const anyArgTainted = () => node.arguments.some((a) => a.type !== "SpreadElement" && rec(a));
|
|
359
|
+
if (callee.type === "MemberExpression" && callee.property.type === "Identifier") {
|
|
360
|
+
const pname = callee.property.name;
|
|
361
|
+
if (FS_READ_METHODS.has(pname)) return anyArgTainted();
|
|
362
|
+
if (pname === "parse" || pname === "parseSync") {
|
|
363
|
+
const obj = callee.object;
|
|
364
|
+
if (obj.type === "Identifier" && PARSE_OBJECTS.has(obj.name)) return anyArgTainted();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (callee.type === "Identifier") {
|
|
368
|
+
if (FS_READ_METHODS.has(callee.name)) return anyArgTainted();
|
|
369
|
+
if (PARSE_BARE.has(callee.name)) return anyArgTainted();
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
332
373
|
function buildTaintMap(parsed) {
|
|
333
374
|
const tainted = /* @__PURE__ */ new Set();
|
|
375
|
+
function markPatternTainted(target) {
|
|
376
|
+
if (target.type === "Identifier") {
|
|
377
|
+
tainted.add(target.name);
|
|
378
|
+
} else if (target.type === "ObjectPattern") {
|
|
379
|
+
for (const prop of target.properties) {
|
|
380
|
+
if (prop.type === "ObjectProperty" && prop.value.type === "Identifier") {
|
|
381
|
+
tainted.add(prop.value.name);
|
|
382
|
+
} else if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
|
|
383
|
+
tainted.add(prop.argument.name);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
} else if (target.type === "ArrayPattern") {
|
|
387
|
+
for (const el of target.elements) {
|
|
388
|
+
if (el && el.type === "Identifier") tainted.add(el.name);
|
|
389
|
+
else if (el && el.type === "RestElement" && el.argument.type === "Identifier") {
|
|
390
|
+
tainted.add(el.argument.name);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
334
395
|
function reachesRequestIdent(node) {
|
|
335
396
|
if (!node) return false;
|
|
336
397
|
if (node.type === "Identifier") return TAINTED_REQUEST_OBJECTS.has(node.name);
|
|
@@ -401,6 +462,7 @@ function buildTaintMap(parsed) {
|
|
|
401
462
|
}
|
|
402
463
|
if (node.type === "CallExpression") {
|
|
403
464
|
if (nodeIsTaintedSource(node)) return true;
|
|
465
|
+
if (callPreservesTaint(node, exprIsTainted)) return true;
|
|
404
466
|
if (node.callee.type === "MemberExpression") {
|
|
405
467
|
if (exprIsTainted(node.callee.object)) return true;
|
|
406
468
|
const obj = node.callee.object;
|
|
@@ -421,6 +483,16 @@ function buildTaintMap(parsed) {
|
|
|
421
483
|
if (node.type === "AwaitExpression") {
|
|
422
484
|
return exprIsTainted(node.argument);
|
|
423
485
|
}
|
|
486
|
+
if (node.type === "ArrayExpression") {
|
|
487
|
+
return node.elements.some(
|
|
488
|
+
(el) => el != null && el.type === "SpreadElement" && exprIsTainted(el.argument)
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
if (node.type === "ObjectExpression") {
|
|
492
|
+
return node.properties.some(
|
|
493
|
+
(p) => p.type === "SpreadElement" && exprIsTainted(p.argument)
|
|
494
|
+
);
|
|
495
|
+
}
|
|
424
496
|
return false;
|
|
425
497
|
}
|
|
426
498
|
traverse(parsed.ast, {
|
|
@@ -455,6 +527,32 @@ function buildTaintMap(parsed) {
|
|
|
455
527
|
if (exprIsTainted(node.right)) {
|
|
456
528
|
tainted.add(node.left.name);
|
|
457
529
|
}
|
|
530
|
+
},
|
|
531
|
+
// for (const row of <tainted>) → row tainted (and destructured names).
|
|
532
|
+
ForOfStatement(path) {
|
|
533
|
+
const node = path.node;
|
|
534
|
+
if (node.type !== "ForOfStatement") return;
|
|
535
|
+
if (!exprIsTainted(node.right)) return;
|
|
536
|
+
const decl = node.left;
|
|
537
|
+
const target = decl.type === "VariableDeclaration" && decl.declarations[0] ? decl.declarations[0].id : decl;
|
|
538
|
+
markPatternTainted(target);
|
|
539
|
+
},
|
|
540
|
+
// Array-iteration element taint: <tainted>.map((row) => …) marks `row`
|
|
541
|
+
// tainted inside the callback. Gated on the receiver already being
|
|
542
|
+
// tainted, so this never originates taint.
|
|
543
|
+
CallExpression(path) {
|
|
544
|
+
const node = path.node;
|
|
545
|
+
if (node.type !== "CallExpression") return;
|
|
546
|
+
const callee = node.callee;
|
|
547
|
+
if (callee.type !== "MemberExpression" || callee.property.type !== "Identifier") return;
|
|
548
|
+
const method = callee.property.name;
|
|
549
|
+
const isReduce = ARRAY_REDUCE_METHODS.has(method);
|
|
550
|
+
if (!ARRAY_ELEMENT_METHODS.has(method) && !isReduce) return;
|
|
551
|
+
if (!exprIsTainted(callee.object)) return;
|
|
552
|
+
const cb = node.arguments[0];
|
|
553
|
+
if (!cb || cb.type !== "ArrowFunctionExpression" && cb.type !== "FunctionExpression") return;
|
|
554
|
+
const elemParam = cb.params[isReduce ? 1 : 0];
|
|
555
|
+
if (elemParam) markPatternTainted(elemParam);
|
|
458
556
|
}
|
|
459
557
|
});
|
|
460
558
|
const isTainted = (node) => {
|
|
@@ -480,6 +578,7 @@ function buildTaintMap(parsed) {
|
|
|
480
578
|
return isTainted(node.object);
|
|
481
579
|
}
|
|
482
580
|
if (node.type === "CallExpression") {
|
|
581
|
+
if (callPreservesTaint(node, isTainted)) return true;
|
|
483
582
|
if (node.callee.type === "MemberExpression") {
|
|
484
583
|
if (isTainted(node.callee.object)) return true;
|
|
485
584
|
const obj = node.callee.object;
|
|
@@ -497,6 +596,16 @@ function buildTaintMap(parsed) {
|
|
|
497
596
|
}
|
|
498
597
|
}
|
|
499
598
|
}
|
|
599
|
+
if (node.type === "ArrayExpression") {
|
|
600
|
+
return node.elements.some(
|
|
601
|
+
(el) => el != null && el.type === "SpreadElement" && isTainted(el.argument)
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
if (node.type === "ObjectExpression") {
|
|
605
|
+
return node.properties.some(
|
|
606
|
+
(p) => p.type === "SpreadElement" && isTainted(p.argument)
|
|
607
|
+
);
|
|
608
|
+
}
|
|
500
609
|
return false;
|
|
501
610
|
};
|
|
502
611
|
return {
|