@sikkuumiii/defuse 0.0.0-stage → 0.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 +235 -0
- package/README.md +561 -2
- package/dist/src/cli.d.ts +20 -0
- package/dist/src/cli.js +619 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/core/analyze.d.ts +178 -0
- package/dist/src/core/analyze.js +834 -0
- package/dist/src/core/analyze.js.map +1 -0
- package/dist/src/core/coverage.d.ts +87 -0
- package/dist/src/core/coverage.js +181 -0
- package/dist/src/core/coverage.js.map +1 -0
- package/dist/src/core/finding.d.ts +196 -0
- package/dist/src/core/finding.js +178 -0
- package/dist/src/core/finding.js.map +1 -0
- package/dist/src/core/licence.d.ts +50 -0
- package/dist/src/core/licence.js +56 -0
- package/dist/src/core/licence.js.map +1 -0
- package/dist/src/core/paths.d.ts +21 -0
- package/dist/src/core/paths.js +58 -0
- package/dist/src/core/paths.js.map +1 -0
- package/dist/src/core/score.d.ts +137 -0
- package/dist/src/core/score.js +232 -0
- package/dist/src/core/score.js.map +1 -0
- package/dist/src/dast/targets.d.ts +30 -0
- package/dist/src/dast/targets.js +79 -0
- package/dist/src/dast/targets.js.map +1 -0
- package/dist/src/dast/zap.d.ts +42 -0
- package/dist/src/dast/zap.js +184 -0
- package/dist/src/dast/zap.js.map +1 -0
- package/dist/src/engine/scan.d.ts +80 -0
- package/dist/src/engine/scan.js +129 -0
- package/dist/src/engine/scan.js.map +1 -0
- package/dist/src/engine/shapes.d.ts +47 -0
- package/dist/src/engine/shapes.js +328 -0
- package/dist/src/engine/shapes.js.map +1 -0
- package/dist/src/engine/walk.d.ts +60 -0
- package/dist/src/engine/walk.js +156 -0
- package/dist/src/engine/walk.js.map +1 -0
- package/dist/src/parse/astDump.d.ts +31 -0
- package/dist/src/parse/astDump.js +66 -0
- package/dist/src/parse/astDump.js.map +1 -0
- package/dist/src/parse/languages.d.ts +70 -0
- package/dist/src/parse/languages.js +174 -0
- package/dist/src/parse/languages.js.map +1 -0
- package/dist/src/parse/location.d.ts +17 -0
- package/dist/src/parse/location.js +38 -0
- package/dist/src/parse/location.js.map +1 -0
- package/dist/src/parse/node-grammars.d.ts +12 -0
- package/dist/src/parse/node-grammars.js +35 -0
- package/dist/src/parse/node-grammars.js.map +1 -0
- package/dist/src/parse/parser.d.ts +126 -0
- package/dist/src/parse/parser.js +202 -0
- package/dist/src/parse/parser.js.map +1 -0
- package/dist/src/parse/tree-store.d.ts +139 -0
- package/dist/src/parse/tree-store.js +236 -0
- package/dist/src/parse/tree-store.js.map +1 -0
- package/dist/src/platform/dependency-rule.d.ts +27 -0
- package/dist/src/platform/dependency-rule.js +21 -0
- package/dist/src/platform/dependency-rule.js.map +1 -0
- package/dist/src/platform/deps.d.ts +86 -0
- package/dist/src/platform/deps.js +304 -0
- package/dist/src/platform/deps.js.map +1 -0
- package/dist/src/platform/php-facts.d.ts +49 -0
- package/dist/src/platform/php-facts.js +67 -0
- package/dist/src/platform/php-facts.js.map +1 -0
- package/dist/src/platform/runtime-version.d.ts +78 -0
- package/dist/src/platform/runtime-version.js +573 -0
- package/dist/src/platform/runtime-version.js.map +1 -0
- package/dist/src/report/colors.d.ts +50 -0
- package/dist/src/report/colors.js +97 -0
- package/dist/src/report/colors.js.map +1 -0
- package/dist/src/report/coverage-table.d.ts +90 -0
- package/dist/src/report/coverage-table.js +291 -0
- package/dist/src/report/coverage-table.js.map +1 -0
- package/dist/src/report/html-report.d.ts +66 -0
- package/dist/src/report/html-report.js +489 -0
- package/dist/src/report/html-report.js.map +1 -0
- package/dist/src/report/human.d.ts +44 -0
- package/dist/src/report/human.js +575 -0
- package/dist/src/report/human.js.map +1 -0
- package/dist/src/report/json.d.ts +24 -0
- package/dist/src/report/json.js +178 -0
- package/dist/src/report/json.js.map +1 -0
- package/dist/src/report/mark.d.ts +17 -0
- package/dist/src/report/mark.js +39 -0
- package/dist/src/report/mark.js.map +1 -0
- package/dist/src/report/pricing.d.ts +33 -0
- package/dist/src/report/pricing.js +343 -0
- package/dist/src/report/pricing.js.map +1 -0
- package/dist/src/report/sarif.d.ts +39 -0
- package/dist/src/report/sarif.js +219 -0
- package/dist/src/report/sarif.js.map +1 -0
- package/dist/src/report/site.d.ts +87 -0
- package/dist/src/report/site.js +742 -0
- package/dist/src/report/site.js.map +1 -0
- package/dist/src/rules/code-injection.d.ts +47 -0
- package/dist/src/rules/code-injection.js +326 -0
- package/dist/src/rules/code-injection.js.map +1 -0
- package/dist/src/rules/command-injection.d.ts +30 -0
- package/dist/src/rules/command-injection.js +286 -0
- package/dist/src/rules/command-injection.js.map +1 -0
- package/dist/src/rules/contract.d.ts +198 -0
- package/dist/src/rules/contract.js +39 -0
- package/dist/src/rules/contract.js.map +1 -0
- package/dist/src/rules/format-string.d.ts +46 -0
- package/dist/src/rules/format-string.js +146 -0
- package/dist/src/rules/format-string.js.map +1 -0
- package/dist/src/rules/hardcoded-secret.d.ts +38 -0
- package/dist/src/rules/hardcoded-secret.js +440 -0
- package/dist/src/rules/hardcoded-secret.js.map +1 -0
- package/dist/src/rules/lib/guards.d.ts +168 -0
- package/dist/src/rules/lib/guards.js +1179 -0
- package/dist/src/rules/lib/guards.js.map +1 -0
- package/dist/src/rules/lib/html-safety.d.ts +75 -0
- package/dist/src/rules/lib/html-safety.js +755 -0
- package/dist/src/rules/lib/html-safety.js.map +1 -0
- package/dist/src/rules/lib/local-build.d.ts +59 -0
- package/dist/src/rules/lib/local-build.js +320 -0
- package/dist/src/rules/lib/local-build.js.map +1 -0
- package/dist/src/rules/lib/strings.d.ts +208 -0
- package/dist/src/rules/lib/strings.js +1970 -0
- package/dist/src/rules/lib/strings.js.map +1 -0
- package/dist/src/rules/path-traversal.d.ts +33 -0
- package/dist/src/rules/path-traversal.js +96 -0
- package/dist/src/rules/path-traversal.js.map +1 -0
- package/dist/src/rules/registry.d.ts +19 -0
- package/dist/src/rules/registry.js +77 -0
- package/dist/src/rules/registry.js.map +1 -0
- package/dist/src/rules/sql-injection.d.ts +36 -0
- package/dist/src/rules/sql-injection.js +299 -0
- package/dist/src/rules/sql-injection.js.map +1 -0
- package/dist/src/rules/ssrf.d.ts +39 -0
- package/dist/src/rules/ssrf.js +111 -0
- package/dist/src/rules/ssrf.js.map +1 -0
- package/dist/src/rules/unbounded-copy.d.ts +37 -0
- package/dist/src/rules/unbounded-copy.js +147 -0
- package/dist/src/rules/unbounded-copy.js.map +1 -0
- package/dist/src/rules/unsafe-deserialization.d.ts +43 -0
- package/dist/src/rules/unsafe-deserialization.js +276 -0
- package/dist/src/rules/unsafe-deserialization.js.map +1 -0
- package/dist/src/rules/weak-hash.d.ts +37 -0
- package/dist/src/rules/weak-hash.js +197 -0
- package/dist/src/rules/weak-hash.js.map +1 -0
- package/dist/src/rules/xss.d.ts +38 -0
- package/dist/src/rules/xss.js +527 -0
- package/dist/src/rules/xss.js.map +1 -0
- package/dist/src/taint/content-type.d.ts +19 -0
- package/dist/src/taint/content-type.js +20 -0
- package/dist/src/taint/content-type.js.map +1 -0
- package/dist/src/taint/dictionaries.d.ts +23 -0
- package/dist/src/taint/dictionaries.js +1778 -0
- package/dist/src/taint/dictionaries.js.map +1 -0
- package/dist/src/taint/fold.d.ts +59 -0
- package/dist/src/taint/fold.js +1177 -0
- package/dist/src/taint/fold.js.map +1 -0
- package/dist/src/taint/java-scope.d.ts +61 -0
- package/dist/src/taint/java-scope.js +212 -0
- package/dist/src/taint/java-scope.js.map +1 -0
- package/dist/src/taint/local-map.d.ts +79 -0
- package/dist/src/taint/local-map.js +603 -0
- package/dist/src/taint/local-map.js.map +1 -0
- package/dist/src/taint/project.d.ts +172 -0
- package/dist/src/taint/project.js +617 -0
- package/dist/src/taint/project.js.map +1 -0
- package/dist/src/taint/tracer.d.ts +265 -0
- package/dist/src/taint/tracer.js +3817 -0
- package/dist/src/taint/tracer.js.map +1 -0
- package/dist/src/taint/types.d.ts +484 -0
- package/dist/src/taint/types.js +47 -0
- package/dist/src/taint/types.js.map +1 -0
- package/dist/src/taint/visibility.d.ts +25 -0
- package/dist/src/taint/visibility.js +96 -0
- package/dist/src/taint/visibility.js.map +1 -0
- package/docs/examples/defuse.json +20 -0
- package/docs/examples/dvwa-urls.json +14 -0
- package/docs/examples/github-code-scanning.yml +54 -0
- package/docs/examples/tiny.js +4 -0
- package/package.json +73 -4
- package/src/cli.ts +747 -0
- package/src/core/analyze.ts +1072 -0
- package/src/core/coverage.ts +269 -0
- package/src/core/finding.ts +311 -0
- package/src/core/licence.ts +61 -0
- package/src/core/paths.ts +59 -0
- package/src/core/score.ts +353 -0
- package/src/dast/targets.ts +121 -0
- package/src/dast/zap.ts +246 -0
- package/src/engine/scan.ts +200 -0
- package/src/engine/shapes.ts +348 -0
- package/src/engine/walk.ts +196 -0
- package/src/parse/astDump.ts +85 -0
- package/src/parse/languages.ts +232 -0
- package/src/parse/location.ts +44 -0
- package/src/parse/node-grammars.ts +41 -0
- package/src/parse/parser.ts +262 -0
- package/src/parse/tree-store.ts +301 -0
- package/src/platform/dependency-rule.ts +33 -0
- package/src/platform/deps.ts +374 -0
- package/src/platform/php-facts.ts +85 -0
- package/src/platform/runtime-version.ts +710 -0
- package/src/report/colors.ts +107 -0
- package/src/report/coverage-table.ts +359 -0
- package/src/report/html-report.ts +601 -0
- package/src/report/human.ts +811 -0
- package/src/report/json.ts +189 -0
- package/src/report/mark.ts +47 -0
- package/src/report/pricing.ts +374 -0
- package/src/report/sarif.ts +235 -0
- package/src/report/site.ts +760 -0
- package/src/rules/code-injection.ts +346 -0
- package/src/rules/command-injection.ts +296 -0
- package/src/rules/contract.ts +220 -0
- package/src/rules/format-string.ts +158 -0
- package/src/rules/hardcoded-secret.ts +485 -0
- package/src/rules/lib/guards.ts +1235 -0
- package/src/rules/lib/html-safety.ts +793 -0
- package/src/rules/lib/local-build.ts +329 -0
- package/src/rules/lib/strings.ts +2034 -0
- package/src/rules/path-traversal.ts +101 -0
- package/src/rules/registry.ts +90 -0
- package/src/rules/sql-injection.ts +311 -0
- package/src/rules/ssrf.ts +118 -0
- package/src/rules/unbounded-copy.ts +162 -0
- package/src/rules/unsafe-deserialization.ts +291 -0
- package/src/rules/weak-hash.ts +226 -0
- package/src/rules/xss.ts +572 -0
- package/src/taint/content-type.ts +20 -0
- package/src/taint/dictionaries.ts +1855 -0
- package/src/taint/fold.ts +1206 -0
- package/src/taint/java-scope.ts +200 -0
- package/src/taint/local-map.ts +594 -0
- package/src/taint/project.ts +774 -0
- package/src/taint/tracer.ts +4298 -0
- package/src/taint/types.ts +511 -0
- package/src/taint/visibility.ts +100 -0
- package/tsconfig.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site.js","sourceRoot":"","sources":["../../../src/report/site.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CODE INJECTION (CWE-94 / CWE-95, OWASP A05:2025 - Injection)
|
|
3
|
+
*
|
|
4
|
+
* WHAT THE BUG IS, in plain language:
|
|
5
|
+
* The other injection rules are about a string being read as instructions by
|
|
6
|
+
* something ELSE - a database, a shell, a browser. This one is about a string
|
|
7
|
+
* being read as instructions by YOUR OWN PROGRAM, in your own language, with
|
|
8
|
+
* every privilege your process has.
|
|
9
|
+
*
|
|
10
|
+
* const total = eval(req.body.preTax);
|
|
11
|
+
*
|
|
12
|
+
* The author wanted arithmetic. What they built is a remote code execution
|
|
13
|
+
* endpoint: whatever the request body says, the server runs. No escaping helps,
|
|
14
|
+
* because there is nothing to escape - the whole string IS the program.
|
|
15
|
+
*
|
|
16
|
+
* It is the shortest path from "user input" to "attacker owns the machine" in
|
|
17
|
+
* any language that has it, and it is startlingly common in code that meant to
|
|
18
|
+
* be clever about configuration, formulas, filters or plugins.
|
|
19
|
+
*
|
|
20
|
+
* THE FIX is always the same shape: stop evaluating, start parsing. A maths
|
|
21
|
+
* expression wants an expression parser. A filter wants a fixed vocabulary of
|
|
22
|
+
* allowed operations. A plugin wants a registry of known functions looked up by
|
|
23
|
+
* name. All three are more code than `eval` and none of them can be talked into
|
|
24
|
+
* running `rm -rf`.
|
|
25
|
+
*
|
|
26
|
+
* WHY IT NEEDED A NEW SINK KIND:
|
|
27
|
+
* `command` is a shell; `code` is the interpreter you are already inside. A
|
|
28
|
+
* shell-quoting sanitiser (`escapeshellarg`, `shlex.quote`) does nothing here,
|
|
29
|
+
* and the engine has to know that, which is exactly what kind-scoping is for.
|
|
30
|
+
*
|
|
31
|
+
* WHY IT EXISTS AT ALL:
|
|
32
|
+
* Three independent test files pointed at the same hole on the same day -
|
|
33
|
+
* OWASP NodeGoat's `eval(req.body.preTax)`, a Python file's
|
|
34
|
+
* `eval(expression, safe_globals)`, and a TypeScript app's
|
|
35
|
+
* `new RegExp(req.query.filter)`. In every one the tracer had already found the
|
|
36
|
+
* source and followed it; there was simply nothing at the far end to hand it
|
|
37
|
+
* to. The engine could see these all along.
|
|
38
|
+
*
|
|
39
|
+
* WHAT THIS RULE DELIBERATELY DOES NOT COVER:
|
|
40
|
+
* ReDoS - `new RegExp(userInput)` - is a real bug and was one of the three that
|
|
41
|
+
* prompted this rule, but it is CWE-1333 and an AVAILABILITY problem: a
|
|
42
|
+
* catastrophically backtracking pattern hangs the process, it does not run the
|
|
43
|
+
* attacker's code. Filing it under CWE-94 would put the wrong number on a real
|
|
44
|
+
* finding, so it is left out and named here rather than quietly folded in.
|
|
45
|
+
*/
|
|
46
|
+
import { type Rule } from './contract.js';
|
|
47
|
+
export declare const codeInjectionRule: Rule;
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CODE INJECTION (CWE-94 / CWE-95, OWASP A05:2025 - Injection)
|
|
3
|
+
*
|
|
4
|
+
* WHAT THE BUG IS, in plain language:
|
|
5
|
+
* The other injection rules are about a string being read as instructions by
|
|
6
|
+
* something ELSE - a database, a shell, a browser. This one is about a string
|
|
7
|
+
* being read as instructions by YOUR OWN PROGRAM, in your own language, with
|
|
8
|
+
* every privilege your process has.
|
|
9
|
+
*
|
|
10
|
+
* const total = eval(req.body.preTax);
|
|
11
|
+
*
|
|
12
|
+
* The author wanted arithmetic. What they built is a remote code execution
|
|
13
|
+
* endpoint: whatever the request body says, the server runs. No escaping helps,
|
|
14
|
+
* because there is nothing to escape - the whole string IS the program.
|
|
15
|
+
*
|
|
16
|
+
* It is the shortest path from "user input" to "attacker owns the machine" in
|
|
17
|
+
* any language that has it, and it is startlingly common in code that meant to
|
|
18
|
+
* be clever about configuration, formulas, filters or plugins.
|
|
19
|
+
*
|
|
20
|
+
* THE FIX is always the same shape: stop evaluating, start parsing. A maths
|
|
21
|
+
* expression wants an expression parser. A filter wants a fixed vocabulary of
|
|
22
|
+
* allowed operations. A plugin wants a registry of known functions looked up by
|
|
23
|
+
* name. All three are more code than `eval` and none of them can be talked into
|
|
24
|
+
* running `rm -rf`.
|
|
25
|
+
*
|
|
26
|
+
* WHY IT NEEDED A NEW SINK KIND:
|
|
27
|
+
* `command` is a shell; `code` is the interpreter you are already inside. A
|
|
28
|
+
* shell-quoting sanitiser (`escapeshellarg`, `shlex.quote`) does nothing here,
|
|
29
|
+
* and the engine has to know that, which is exactly what kind-scoping is for.
|
|
30
|
+
*
|
|
31
|
+
* WHY IT EXISTS AT ALL:
|
|
32
|
+
* Three independent test files pointed at the same hole on the same day -
|
|
33
|
+
* OWASP NodeGoat's `eval(req.body.preTax)`, a Python file's
|
|
34
|
+
* `eval(expression, safe_globals)`, and a TypeScript app's
|
|
35
|
+
* `new RegExp(req.query.filter)`. In every one the tracer had already found the
|
|
36
|
+
* source and followed it; there was simply nothing at the far end to hand it
|
|
37
|
+
* to. The engine could see these all along.
|
|
38
|
+
*
|
|
39
|
+
* WHAT THIS RULE DELIBERATELY DOES NOT COVER:
|
|
40
|
+
* ReDoS - `new RegExp(userInput)` - is a real bug and was one of the three that
|
|
41
|
+
* prompted this rule, but it is CWE-1333 and an AVAILABILITY problem: a
|
|
42
|
+
* catastrophically backtracking pattern hangs the process, it does not run the
|
|
43
|
+
* attacker's code. Filing it under CWE-94 would put the wrong number on a real
|
|
44
|
+
* finding, so it is left out and named here rather than quietly folded in.
|
|
45
|
+
*/
|
|
46
|
+
import { asCall } from './contract.js';
|
|
47
|
+
import { analyzeStringExpression, constantProof } from './lib/strings.js';
|
|
48
|
+
/**
|
|
49
|
+
* Calls that hand a string to the language's own interpreter.
|
|
50
|
+
*
|
|
51
|
+
* `setTimeout` and `setInterval` are NOT here, and the omission is deliberate.
|
|
52
|
+
* They only execute a string argument - `setTimeout("alert(1)", 100)` - and the
|
|
53
|
+
* overwhelmingly common form passes a FUNCTION. Reporting them on shape would
|
|
54
|
+
* light up every debounce and every retry in every codebase. They are listed as
|
|
55
|
+
* taint sinks instead, where the engine knows the value came from a request and
|
|
56
|
+
* a request cannot deliver a function.
|
|
57
|
+
*/
|
|
58
|
+
const EVALUATORS = {
|
|
59
|
+
javascript: ['eval', 'Function', 'execScript', 'runInNewContext', 'runInThisContext'],
|
|
60
|
+
typescript: ['eval', 'Function', 'execScript', 'runInNewContext', 'runInThisContext'],
|
|
61
|
+
// `compile` produces a code object that only `exec` can run, but it is the
|
|
62
|
+
// same decision one step earlier, and reporting it points at the real line.
|
|
63
|
+
python: ['eval', 'exec', 'compile'],
|
|
64
|
+
// `assert($x)` evaluates its argument as PHP when handed a string. It is a
|
|
65
|
+
// debugging aid that became an RCE primitive, removed in PHP 8 - which means
|
|
66
|
+
// the codebases that still use it are the old ones nobody is watching.
|
|
67
|
+
php: ['eval', 'assert', 'create_function'],
|
|
68
|
+
// ScriptEngine.eval("...") runs JavaScript inside the JVM.
|
|
69
|
+
java: ['eval'],
|
|
70
|
+
// Go has no eval. There is no interpreter at runtime to hand a string to, so
|
|
71
|
+
// the shape this rule detects cannot occur, and an empty list is the honest
|
|
72
|
+
// answer rather than a rule that never fires but implies coverage.
|
|
73
|
+
go: [],
|
|
74
|
+
// C has no interpreter to hand a string to. `dlopen`/`dlsym` load code, but
|
|
75
|
+
// from a PATH rather than from a string, which is a different bug (untrusted
|
|
76
|
+
// search path) and not one this rule models. Empty on purpose, and the
|
|
77
|
+
// coverage matrix says so rather than leaving a blank.
|
|
78
|
+
c: [],
|
|
79
|
+
cpp: [],
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Evaluators that are BUILT-IN FUNCTIONS, and are therefore only themselves
|
|
83
|
+
* when called with nothing in front of them.
|
|
84
|
+
*
|
|
85
|
+
* Scanning Django found 163 code-injection findings and 153 were this mistake:
|
|
86
|
+
*
|
|
87
|
+
* compiler.compile(value) Django's own SQL compiler x82
|
|
88
|
+
* re.compile(pattern) a regular expression x21
|
|
89
|
+
* self.compile(node) somebody's method x20
|
|
90
|
+
* x.eval(...) somebody else's method x25
|
|
91
|
+
*
|
|
92
|
+
* `compile`, `eval` and `exec` are builtins. `re.compile` shares three letters
|
|
93
|
+
* with the dangerous one and nothing else. Matching the bare method name meant
|
|
94
|
+
* every object in Python with a `.compile()` or `.eval()` method - and there are
|
|
95
|
+
* a lot of them, because those are natural names for "turn this into a runnable
|
|
96
|
+
* thing" in any DSL, ORM or template engine.
|
|
97
|
+
*
|
|
98
|
+
* Not on this list: vm.runInNewContext, which is a method and is supposed to
|
|
99
|
+
* have `vm` in front of it, and Java's ScriptEngine.eval, which is handled by
|
|
100
|
+
* JAVA_ENGINE below.
|
|
101
|
+
*/
|
|
102
|
+
const BARE_BUILTINS = new Set([
|
|
103
|
+
'eval',
|
|
104
|
+
'exec',
|
|
105
|
+
'compile',
|
|
106
|
+
'execScript',
|
|
107
|
+
'assert',
|
|
108
|
+
'create_function',
|
|
109
|
+
'Function',
|
|
110
|
+
]);
|
|
111
|
+
/**
|
|
112
|
+
* Receivers that ARE evaluators, and so keep the finding despite BARE_BUILTINS.
|
|
113
|
+
*
|
|
114
|
+
* The bare-only rule was too blunt on its own: OWASP's dvna has
|
|
115
|
+
*
|
|
116
|
+
* mathjs.eval(req.body.eqn)
|
|
117
|
+
*
|
|
118
|
+
* which is a genuine remote-code-execution sink - mathjs's `eval` has its own
|
|
119
|
+
* CVEs - and it has a receiver, so the first version of this guard silently
|
|
120
|
+
* dropped it. That is the exact trade this rule has to get right: `re.compile`
|
|
121
|
+
* and `compiler.compile` are somebody's method, `mathjs.eval` and `window.eval`
|
|
122
|
+
* are the interpreter. The difference is WHICH object, not whether there is one.
|
|
123
|
+
*/
|
|
124
|
+
const EVALUATOR_RECEIVERS = new Set([
|
|
125
|
+
'mathjs',
|
|
126
|
+
'math',
|
|
127
|
+
'vm',
|
|
128
|
+
'safeEval',
|
|
129
|
+
'eval5',
|
|
130
|
+
'builtins',
|
|
131
|
+
'__builtins__',
|
|
132
|
+
]);
|
|
133
|
+
/**
|
|
134
|
+
* Receivers that are the GLOBAL OBJECT, and therefore only in JavaScript.
|
|
135
|
+
*
|
|
136
|
+
* `self` was the expensive one. In a browser - and especially in a web worker,
|
|
137
|
+
* where it is the only name for the global scope - `self.eval(src)` IS the
|
|
138
|
+
* interpreter, so it belonged on the allowlist. In Python `self` is the
|
|
139
|
+
* instance, and `self.eval(expr)` is a method somebody wrote.
|
|
140
|
+
*
|
|
141
|
+
* Scanning pandas reported 42 code injections and seventeen were `self.eval` -
|
|
142
|
+
* pandas' own `DataFrame.eval`, a public API that parses a restricted
|
|
143
|
+
* expression grammar and cannot reach the interpreter. The allowlist was
|
|
144
|
+
* correct; applying a JavaScript fact to Python was not. `window`, `global`
|
|
145
|
+
* and `globalThis` move here for the same reason, even though no other language
|
|
146
|
+
* happened to collide with them yet.
|
|
147
|
+
*/
|
|
148
|
+
const JS_GLOBAL_RECEIVERS = new Set([
|
|
149
|
+
'window',
|
|
150
|
+
'global',
|
|
151
|
+
'globalThis',
|
|
152
|
+
'self',
|
|
153
|
+
]);
|
|
154
|
+
/**
|
|
155
|
+
* Is this dangerous-looking name actually bound to something else in this file?
|
|
156
|
+
*
|
|
157
|
+
* pandas/core/computation/ops.py, with the import one line above the call:
|
|
158
|
+
*
|
|
159
|
+
* from pandas.core.computation.eval import eval
|
|
160
|
+
* res = eval(self, local_dict=env, engine=engine, parser=parser)
|
|
161
|
+
*
|
|
162
|
+
* `eval`, `exec` and `compile` are only dangerous because of what the
|
|
163
|
+
* interpreter binds those names to. An explicit import rebinds them, and this
|
|
164
|
+
* scanner is holding the very file that says so. Reporting it anyway was
|
|
165
|
+
* matching a name rather than a function.
|
|
166
|
+
*
|
|
167
|
+
* Deliberately narrow: only an explicit import of the same name counts. A
|
|
168
|
+
* reassignment (`eval = my_evaluator`) or a `import *` that happens to shadow
|
|
169
|
+
* it is NOT detected, and is a documented gap rather than a guess.
|
|
170
|
+
*/
|
|
171
|
+
function isImportedName(source, name) {
|
|
172
|
+
const n = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
173
|
+
// from x.y import (a, eval, b) / from x import eval as e / import eval
|
|
174
|
+
return (new RegExp(`^[ \\t]*from[ \\t]+[\\w.]+[ \\t]+import[ \\t]*\\(?[^\\n]*\\b${n}\\b`, 'm').test(source) || new RegExp(`^[ \\t]*import[ \\t]+${n}\\b`, 'm').test(source));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Receivers that make `eval` unambiguous in Java, where the bare name is far
|
|
178
|
+
* too common. `engine.eval(script)` is the JSR-223 scripting API.
|
|
179
|
+
*/
|
|
180
|
+
const JAVA_ENGINE = /engine|script|nashorn|graal|interpreter/i;
|
|
181
|
+
export const codeInjectionRule = {
|
|
182
|
+
id: 'code-injection',
|
|
183
|
+
name: 'User input evaluated as program code',
|
|
184
|
+
cwe: 'CWE-94',
|
|
185
|
+
owasp: 'A05:2025 Injection',
|
|
186
|
+
severity: 'critical',
|
|
187
|
+
summary: 'A string is run as program code; if any of it comes from outside, the sender decides what your server does.',
|
|
188
|
+
fix: 'Never eval input: parse it instead (a maths parser, a fixed list of allowed commands, or a lookup table).',
|
|
189
|
+
explanation: 'Passing a string to eval (or exec, Function, assert) tells your own language ' +
|
|
190
|
+
'to run it as a program, with every privilege the process has. If any part of ' +
|
|
191
|
+
'that string came from a request, the request decides what your server does. ' +
|
|
192
|
+
'There is nothing to escape here - the string IS the program - so the fix is ' +
|
|
193
|
+
'always to stop evaluating and start parsing: an expression parser for maths, ' +
|
|
194
|
+
'a fixed vocabulary for filters, a lookup table for plugins.',
|
|
195
|
+
limitations: 'UNVERIFIED (signature-based): we confirmed a value reaches an evaluator and ' +
|
|
196
|
+
'that it is not a fixed literal. We did NOT confirm it is attacker-controlled - ' +
|
|
197
|
+
'the data-flow pass is what upgrades this to flow-verified, and evaluating your ' +
|
|
198
|
+
'own constant is harmless. DELIBERATE GAP: setTimeout and setInterval accept a ' +
|
|
199
|
+
'string and run it, but almost always receive a function, so they are taint ' +
|
|
200
|
+
'sinks only and are never reported on shape alone. ALSO NOT COVERED: ReDoS - ' +
|
|
201
|
+
'new RegExp(userInput) - is a real bug but it is CWE-1333 and an availability ' +
|
|
202
|
+
'problem rather than code execution, so filing it here would put the wrong ' +
|
|
203
|
+
'identifier on a real finding. Go is not covered because it has no runtime ' +
|
|
204
|
+
'evaluator for the shape to occur in.',
|
|
205
|
+
shapes: ['call'],
|
|
206
|
+
support: {
|
|
207
|
+
javascript: {
|
|
208
|
+
status: 'implemented',
|
|
209
|
+
note: 'Covers eval, new Function, and the vm module\'s runInNewContext/runInThisContext. setTimeout/setInterval with a string are taint sinks only, because the common form passes a function and reporting on shape would flag every debounce in the codebase.',
|
|
210
|
+
},
|
|
211
|
+
typescript: {
|
|
212
|
+
status: 'implemented',
|
|
213
|
+
note: 'Identical to JavaScript.',
|
|
214
|
+
},
|
|
215
|
+
python: {
|
|
216
|
+
status: 'implemented',
|
|
217
|
+
note: 'Covers eval, exec and compile. NOT covered: __import__ with a built name, and the ast.literal_eval / eval confusion - literal_eval is safe and is deliberately absent.',
|
|
218
|
+
},
|
|
219
|
+
java: {
|
|
220
|
+
status: 'partial',
|
|
221
|
+
note: 'PARTIAL: ScriptEngine.eval only, and only on a receiver whose name looks like a script engine (engine, script, nashorn, graal). `eval` is a common method name in Java, so an engine stored under an unrelated name is missed rather than guessed at.',
|
|
222
|
+
},
|
|
223
|
+
php: {
|
|
224
|
+
status: 'implemented',
|
|
225
|
+
note: 'Covers eval, assert with a string argument, and create_function. assert() evaluating strings was removed in PHP 8, so a hit here also tells you the file predates that.',
|
|
226
|
+
},
|
|
227
|
+
go: {
|
|
228
|
+
status: 'not-implemented',
|
|
229
|
+
note: 'NOT IMPLEMENTED: Go has no runtime evaluator - no eval, no exec of source, no scripting engine in the standard library. The shape this rule detects cannot occur, so a Go entry would imply coverage of a risk that does not exist.',
|
|
230
|
+
},
|
|
231
|
+
c: {
|
|
232
|
+
status: 'not-implemented',
|
|
233
|
+
note: 'C has no interpreter to hand a string to. dlopen/dlsym load code, but from a PATH rather than from a string - that is an untrusted-search-path weakness, which this rule does not model. Empty on purpose rather than half-filled.',
|
|
234
|
+
},
|
|
235
|
+
cpp: {
|
|
236
|
+
status: 'not-implemented',
|
|
237
|
+
note: 'C++ has no interpreter to hand a string to either. Embedded scripting engines (Lua, V8, Python-C-API) genuinely do evaluate strings and are NOT modelled - a tainted string reaching luaL_dostring or v8::Script::Compile is missed entirely.',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
check(shape, ctx) {
|
|
241
|
+
const language = ctx.language;
|
|
242
|
+
const call = asCall(shape);
|
|
243
|
+
if (!call)
|
|
244
|
+
return null;
|
|
245
|
+
if (!EVALUATORS[language].includes(call.calleeName))
|
|
246
|
+
return null;
|
|
247
|
+
// Java's `eval` needs a receiver that looks like a scripting engine.
|
|
248
|
+
if (language === 'java' && !JAVA_ENGINE.test(call.receiverText))
|
|
249
|
+
return null;
|
|
250
|
+
// Everywhere else, a builtin with something in front of it is somebody
|
|
251
|
+
// else's method that happens to share the name.
|
|
252
|
+
const isJs = language === 'javascript' || language === 'typescript';
|
|
253
|
+
if (language !== 'java' && BARE_BUILTINS.has(call.calleeName) && call.receiverText !== '') {
|
|
254
|
+
const owner = call.receiverText.split(/[.\s([]/).filter(Boolean).pop() ?? call.receiverText;
|
|
255
|
+
const known = EVALUATOR_RECEIVERS.has(owner) || (isJs && JS_GLOBAL_RECEIVERS.has(owner));
|
|
256
|
+
if (!known)
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
// A builtin the file has explicitly imported over is not the builtin.
|
|
260
|
+
if (BARE_BUILTINS.has(call.calleeName) && isImportedName(ctx.file.source, call.calleeName)) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
// PHP's assert() is only dangerous with a STRING argument; assert($a > $b)
|
|
264
|
+
// is an ordinary boolean check and is not evaluated as source.
|
|
265
|
+
const argument = call.args[0];
|
|
266
|
+
if (!argument)
|
|
267
|
+
return null;
|
|
268
|
+
if (language === 'php' && call.calleeName === 'assert') {
|
|
269
|
+
if (argument.type !== 'string' && argument.type !== 'encapsed_string') {
|
|
270
|
+
// A variable could still hold a string, so we do not drop it outright -
|
|
271
|
+
// but a visible comparison is provably not source text.
|
|
272
|
+
if (argument.type === 'binary_expression')
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/*
|
|
277
|
+
* `exec(compile(source, name, "exec"))` is ONE decision, and this rule saw
|
|
278
|
+
* two: once for the `exec` and once for the `compile` inside it. Flask's
|
|
279
|
+
* own config loader produced a duplicate pair on the same line.
|
|
280
|
+
*
|
|
281
|
+
* `compile` earns its place in the list because it is sometimes reached
|
|
282
|
+
* alone, but when its result is being handed straight to an evaluator the
|
|
283
|
+
* outer call is the honest place to report - it is where the code actually
|
|
284
|
+
* runs, and one line of source should not become two findings.
|
|
285
|
+
*/
|
|
286
|
+
if (call.calleeName === 'compile') {
|
|
287
|
+
let ancestor = call.node.parent;
|
|
288
|
+
for (let depth = 0; ancestor && depth < 4; depth++) {
|
|
289
|
+
const text = ancestor.text ?? '';
|
|
290
|
+
if (/\b(eval|exec)\s*\(/.test(text) && ancestor.type !== 'call' && ancestor.type !== 'call_expression') {
|
|
291
|
+
ancestor = ancestor.parent;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if ((ancestor.type === 'call' || ancestor.type === 'call_expression') &&
|
|
295
|
+
/^\s*(eval|exec)\s*\(/.test(text)) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
ancestor = ancestor.parent;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const built = analyzeStringExpression(argument, language);
|
|
302
|
+
// `eval("1 + 1")` is a constant. Nobody can put anything inside it.
|
|
303
|
+
if (!built.isDynamic)
|
|
304
|
+
return null;
|
|
305
|
+
// `bar` written in a branch that cannot run, every live write a literal:
|
|
306
|
+
// the same proof the SQL rule withdraws on, with the same receipt.
|
|
307
|
+
const proof = constantProof(argument, built.dynamicParts, language, { staticMethod: ctx.staticMethod, javaTypeInScan: ctx.javaTypeInScan });
|
|
308
|
+
if (proof) {
|
|
309
|
+
if (proof.reasoned)
|
|
310
|
+
ctx.noteClean?.('code-injection', argument, proof.reason);
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
node: call.node,
|
|
315
|
+
message: `Value passed to ${call.calleeName}() is executed as ${language === 'php' ? 'PHP' : language} code`,
|
|
316
|
+
reasoning: `\`${call.calleeName}()\` hands its argument to the interpreter this program is ` +
|
|
317
|
+
`already running inside, so whatever the string says, the process does - with ` +
|
|
318
|
+
`every permission it has. The value here is ${built.mechanism === 'opaque' ? 'a runtime value' : `built at runtime via ${built.mechanism}`}, ` +
|
|
319
|
+
`so its text is not fixed at the time you are reading this line. Escaping cannot ` +
|
|
320
|
+
`help, because the whole string is the program. Replace the evaluation with a ` +
|
|
321
|
+
`parser for the thing you actually wanted: an expression evaluator for arithmetic, ` +
|
|
322
|
+
`a fixed set of allowed operations for filters, a lookup table for plugin names.`,
|
|
323
|
+
};
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
//# sourceMappingURL=code-injection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-injection.js","sourceRoot":"","sources":["../../../src/rules/code-injection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS COMMAND INJECTION (CWE-78, OWASP A05:2025 - Injection)
|
|
3
|
+
*
|
|
4
|
+
* WHAT THE BUG IS, in plain language:
|
|
5
|
+
* Some functions hand a string to the operating system's SHELL - the same
|
|
6
|
+
* program that runs your terminal. The shell has its own punctuation with
|
|
7
|
+
* special meaning: `;` ends a command and starts another, `|` pipes output,
|
|
8
|
+
* `&&` chains, and backticks or `$( )` run a command and paste its output.
|
|
9
|
+
*
|
|
10
|
+
* exec("ping -c 1 " + host)
|
|
11
|
+
*
|
|
12
|
+
* If `host` is `example.com; rm -rf /`, the shell sees TWO commands and runs
|
|
13
|
+
* both. Same disease as SQL injection: data got mixed into instructions.
|
|
14
|
+
*
|
|
15
|
+
* THE FIX:
|
|
16
|
+
* Don't use a shell. Every language has a way to launch a program with its
|
|
17
|
+
* arguments as a LIST rather than a sentence - `execFile`/`spawn` without
|
|
18
|
+
* `shell: true` in Node, `subprocess.run([...])` without `shell=True` in
|
|
19
|
+
* Python, `exec.Command("ping", "-c", "1", host)` in Go. When arguments are a
|
|
20
|
+
* list, `;` is just a character in a string. Nothing parses it.
|
|
21
|
+
*
|
|
22
|
+
* WHY THIS RULE IS FUSSIER THAN THE SQL ONE:
|
|
23
|
+
* `spawn("ls", [dir])` is safe, `spawn("ls " + dir, {shell:true})` is not, and
|
|
24
|
+
* they are the same function. Where the safety depends on an options object we
|
|
25
|
+
* check for it textually and say so; where we cannot tell, we stay quiet rather
|
|
26
|
+
* than guess. A scanner that cries wolf on every subprocess call gets muted,
|
|
27
|
+
* and a muted scanner finds nothing.
|
|
28
|
+
*/
|
|
29
|
+
import { type Rule } from './contract.js';
|
|
30
|
+
export declare const commandInjectionRule: Rule;
|