@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,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE TAINT TRACER
|
|
3
|
+
* ================
|
|
4
|
+
*
|
|
5
|
+
* This is the engine that turns "signature-based" into "flow-verified".
|
|
6
|
+
*
|
|
7
|
+
* HOW IT WORKS, in one paragraph:
|
|
8
|
+
* We read each function body top to bottom, keeping a notebook of which
|
|
9
|
+
* variables currently hold dirty data. When a line assigns something, we work
|
|
10
|
+
* out whether the right-hand side is dirty and write that in the notebook. When
|
|
11
|
+
* a line calls something dangerous, we look up its arguments in the notebook.
|
|
12
|
+
* If a dirty value reaches a sink and no sanitiser washed it on the way, we can
|
|
13
|
+
* finally say the thing a pattern match never could: *this specific value, from
|
|
14
|
+
* this specific source, reaches this specific sink.* And we can show every hop.
|
|
15
|
+
*
|
|
16
|
+
* WHAT IT DELIBERATELY DOES NOT DO - each of these is a documented gap, not an
|
|
17
|
+
* oversight, and each is printed in the scan report:
|
|
18
|
+
*
|
|
19
|
+
* 1. BOUNDED REACH. Functions defined in this file are followed, and so are
|
|
20
|
+
* functions in another file when a relative import resolves to it. What
|
|
21
|
+
* still ends a trace: a call into a third-party package, a dynamic or
|
|
22
|
+
* aliased import, a name several modules define (we decline rather than
|
|
23
|
+
* guess), and any chain deeper than four calls. A cut at that depth, or at
|
|
24
|
+
* a call that looks recursive, does NOT end the value: it is carried past
|
|
25
|
+
* the unread call exactly as in 3 below, with the reason for the stop
|
|
26
|
+
* written into the path.
|
|
27
|
+
*
|
|
28
|
+
* 2. STATEMENT ORDER, NOT BRANCH LOGIC. We read lines in order and ignore
|
|
29
|
+
* whether an `if` is true. So a value cleaned only inside `if (isAdmin)`
|
|
30
|
+
* is treated as cleaned afterwards. That direction of error loses findings
|
|
31
|
+
* rather than inventing them, which is the right way to be wrong.
|
|
32
|
+
*
|
|
33
|
+
* 3. UNKNOWN FUNCTIONS CARRY TAINT, AND SAY SO. `foo(dirty)` keeps the taint
|
|
34
|
+
* and records the hop as UNMODELLED, naming `foo` on the finding. Treating
|
|
35
|
+
* unknowns as clean was tried first and produced ZERO verified flows across
|
|
36
|
+
* 2,766 Java files, every one a known vulnerability - so the quiet option
|
|
37
|
+
* was not the honest one. This is the weakest link in any path we print,
|
|
38
|
+
* which is exactly why it is printed rather than smoothed over.
|
|
39
|
+
*
|
|
40
|
+
* 4. CONTAINERS ARE TAINTED WHOLE. `list.add(dirty)` dirties the list, not
|
|
41
|
+
* the slot - so a clean element read back out of that list is treated as
|
|
42
|
+
* dirty too. Following values into objects at all requires this trade; the
|
|
43
|
+
* alternative was the previous behaviour, which was to lose the value
|
|
44
|
+
* entirely the moment it entered a StringBuilder or a List.
|
|
45
|
+
*
|
|
46
|
+
* 5. NO LOOP FIXPOINT. A variable that becomes dirty at the bottom of a loop
|
|
47
|
+
* and is used at the top is missed on the first pass. Real analysers
|
|
48
|
+
* iterate to a fixpoint; we do not, yet.
|
|
49
|
+
*
|
|
50
|
+
* Every one of those makes the tool MISS things (false negatives). None of them
|
|
51
|
+
* makes it invent things. That asymmetry is deliberate: a flow-verified label is
|
|
52
|
+
* a promise, and a promise that is sometimes wrong is worth less than no promise
|
|
53
|
+
* at all.
|
|
54
|
+
*/
|
|
55
|
+
import type { Node } from 'web-tree-sitter';
|
|
56
|
+
import type { FlowStep } from '../core/finding.js';
|
|
57
|
+
import type { LanguageId } from '../parse/languages.js';
|
|
58
|
+
import type { ParsedFile } from '../parse/parser.js';
|
|
59
|
+
import { type SinkKind, type TaintDictionary } from './types.js';
|
|
60
|
+
/**
|
|
61
|
+
* A flow that reached a sink but was correctly sanitised on the way.
|
|
62
|
+
* Not a vulnerability - evidence that this line is fine.
|
|
63
|
+
*/
|
|
64
|
+
export interface SanitizedFlow {
|
|
65
|
+
readonly ruleId: string;
|
|
66
|
+
readonly kind: SinkKind;
|
|
67
|
+
readonly sinkNode: Node;
|
|
68
|
+
/** The file the sink is in - may not be the file being scanned. */
|
|
69
|
+
readonly filePath: string;
|
|
70
|
+
/**
|
|
71
|
+
* What made it clean, in words, with the line it happened on - the receipt.
|
|
72
|
+
* See cleanReason: a line printed as "checked and cleared" has to say what
|
|
73
|
+
* checked it.
|
|
74
|
+
*/
|
|
75
|
+
readonly reason: string;
|
|
76
|
+
/**
|
|
77
|
+
* Functions Defuse does not model that the cleaned value passed through
|
|
78
|
+
* AFTER it was cleaned. Each was assumed to hand back what it was given; if
|
|
79
|
+
* one undoes the cleaning, the receipt is wrong, and it says so up front.
|
|
80
|
+
*/
|
|
81
|
+
readonly assumes?: readonly string[];
|
|
82
|
+
}
|
|
83
|
+
/** One confirmed source-to-sink flow, ready to become a finding. */
|
|
84
|
+
export interface FlowResult {
|
|
85
|
+
readonly ruleId: string;
|
|
86
|
+
readonly kind: SinkKind;
|
|
87
|
+
/** The exact tainted expression - what the finding points at. */
|
|
88
|
+
readonly node: Node;
|
|
89
|
+
/** The whole sink call or assignment - used to match up with signature findings. */
|
|
90
|
+
readonly sinkNode: Node;
|
|
91
|
+
readonly path: readonly FlowStep[];
|
|
92
|
+
readonly origin: string;
|
|
93
|
+
readonly sinkDescription: string;
|
|
94
|
+
/** Functions on the path that we do not model. Named on the finding. */
|
|
95
|
+
readonly unknownHops: readonly string[];
|
|
96
|
+
/**
|
|
97
|
+
* The value reached this sink only because a clean write was skipped for
|
|
98
|
+
* being conditional. The finding is still REPORTED - a missed bug is the one
|
|
99
|
+
* failure this scanner cannot see in itself - but it is reported as a guess,
|
|
100
|
+
* because a branch was assumed rather than followed. See branchAssumed.
|
|
101
|
+
*/
|
|
102
|
+
readonly branchAssumed?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* The value reached this sink out of a collection that took several elements,
|
|
105
|
+
* so which one came back is a guess. Reported, not certified. See
|
|
106
|
+
* containerGuess.
|
|
107
|
+
*/
|
|
108
|
+
readonly containerGuess?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* The value went through escaping that only protects inside quotes, and was
|
|
111
|
+
* put into the query somewhere this engine cannot read, so whether it is
|
|
112
|
+
* quoted is unknown. Reported, not certified - and not cleared either.
|
|
113
|
+
*/
|
|
114
|
+
readonly quoteUnknown?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* An SSRF flow whose request cannot go where the attacker chooses: the value
|
|
117
|
+
* sits after a fixed start of the address (`path`, with that start), or the
|
|
118
|
+
* code runs in a browser, so the request is the user's own (`browser`).
|
|
119
|
+
* Reported as a guess with the reason, never as proven SSRF and never cleared.
|
|
120
|
+
*/
|
|
121
|
+
readonly fixedDestination?: {
|
|
122
|
+
readonly kind: 'path' | 'browser';
|
|
123
|
+
readonly before: string;
|
|
124
|
+
} | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* A path-traversal flow whose value comes only from the program's own
|
|
127
|
+
* command line, environment or terminal. Reported as a guess with the
|
|
128
|
+
* reason, never as proven: the person who chose the path can read the file.
|
|
129
|
+
*/
|
|
130
|
+
readonly localSource?: boolean | undefined;
|
|
131
|
+
/** The file the sink is in - may not be the file being scanned. */
|
|
132
|
+
readonly filePath: string;
|
|
133
|
+
}
|
|
134
|
+
/** How a clean part of a string is written in a urlPrefix. */
|
|
135
|
+
export declare const CLEAN_PART = "\u0001";
|
|
136
|
+
/**
|
|
137
|
+
* Does the text before a value pin the destination of the address it is part
|
|
138
|
+
* of? Only when a path, query or fragment has already begun - after a host
|
|
139
|
+
* (`https://api.example.com/`), after a clean base (`${base}/`), or in a
|
|
140
|
+
* relative path (`./`, `../`, `/api/`, `docs/`).
|
|
141
|
+
*
|
|
142
|
+
* NOT pinned, and each is a real SSRF shape: nothing before the value; a
|
|
143
|
+
* scheme with the host still open (`https://`); a host with no slash after it
|
|
144
|
+
* (`https://api.example.com` + `@evil.example`); a clean base with nothing
|
|
145
|
+
* after it, which may itself end without a slash; `https:///`, where URL
|
|
146
|
+
* parsers skip the extra slash and the value becomes the host.
|
|
147
|
+
*
|
|
148
|
+
* One shape it cannot see: a clean base holding only a scheme with no slashes
|
|
149
|
+
* (`https:`) followed by `/` and the value - parsers read `https:/evil` as
|
|
150
|
+
* host `evil`. A configuration value shaped like that is rare enough to
|
|
151
|
+
* accept, and it is written down here rather than discovered later.
|
|
152
|
+
*/
|
|
153
|
+
export declare function destinationPinned(before: string): boolean;
|
|
154
|
+
/** A function we can follow a value into. */
|
|
155
|
+
export interface LocalFunction {
|
|
156
|
+
readonly name: string;
|
|
157
|
+
readonly params: readonly string[];
|
|
158
|
+
readonly body: Node;
|
|
159
|
+
/**
|
|
160
|
+
* How many arguments a call to this function may pass: fewer than minArgs or
|
|
161
|
+
* more than maxArgs and it cannot be this function. Recorded at indexing
|
|
162
|
+
* time, while the tree is hot - a cross-file target's tree may be evicted by
|
|
163
|
+
* the time a call to it is resolved. See ARITY_ENFORCED.
|
|
164
|
+
*/
|
|
165
|
+
readonly minArgs?: number | undefined;
|
|
166
|
+
readonly maxArgs?: number | undefined;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Index every function in a tree by name. Exported because the cross-file
|
|
170
|
+
* resolver needs exactly the same view of another file that the tracer has of
|
|
171
|
+
* this one - if the two disagreed about what counts as a function, a value
|
|
172
|
+
* could be followed into something that is not there.
|
|
173
|
+
*/
|
|
174
|
+
export declare function collectFunctions(root: Node, language: LanguageId): Map<string, LocalFunction>;
|
|
175
|
+
/**
|
|
176
|
+
* How the tracer asks "is there a function called X in a file this one imports?"
|
|
177
|
+
* Implemented by src/taint/project.ts; absent when scanning a single file.
|
|
178
|
+
*/
|
|
179
|
+
export interface CrossFileResolver {
|
|
180
|
+
resolve(fromPath: string, name: string): {
|
|
181
|
+
readonly path: string;
|
|
182
|
+
readonly fn: LocalFunction;
|
|
183
|
+
} | null;
|
|
184
|
+
/** Called when several files export that name and we refuse to guess. */
|
|
185
|
+
noteAmbiguous(fromPath: string, name: string, candidates: number): void;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Where this trace RAN OUT, counted.
|
|
189
|
+
*
|
|
190
|
+
* The engine has always documented these limits in prose - "call chains deeper
|
|
191
|
+
* than four functions are not followed" sits in the capability block on every
|
|
192
|
+
* scan. But a general disclaimer and a measurement are different products. The
|
|
193
|
+
* disclaimer says the gap could exist; the count says it happened 38 times in
|
|
194
|
+
* YOUR code, which is the version a person can act on.
|
|
195
|
+
*
|
|
196
|
+
* A comment in this file used to claim these were "recorded as misses". They
|
|
197
|
+
* were not - they returned null and vanished. Now the claim is true.
|
|
198
|
+
*/
|
|
199
|
+
export interface TraceLimits {
|
|
200
|
+
/** Calls not followed because the chain already ran four deep. */
|
|
201
|
+
readonly depthTruncations: number;
|
|
202
|
+
/** Calls not re-entered because the function was already on the stack. */
|
|
203
|
+
readonly recursionStops: number;
|
|
204
|
+
/** Hops through a function we do not model, where taint was carried anyway. */
|
|
205
|
+
readonly unmodelledHops: number;
|
|
206
|
+
/**
|
|
207
|
+
* How many distinct places an attacker-controlled SOURCE was recognised.
|
|
208
|
+
*
|
|
209
|
+
* This exists because of a silence. Elasticsearch - 3,999 Java files, a REST
|
|
210
|
+
* API, a network service - produced zero flow-verified findings, and the
|
|
211
|
+
* report had no way to say why. It contains 0 `@RequestParam` and 0
|
|
212
|
+
* `getParameter()`: it uses its own REST layer, which the dictionaries do not
|
|
213
|
+
* model, so the tracer never found a single source to start from.
|
|
214
|
+
*
|
|
215
|
+
* Every number in a report is downstream of this one. Zero sources means
|
|
216
|
+
* zero flows can exist no matter how good the tracer is, and a reader
|
|
217
|
+
* deserves to be told that rather than reading "0 flow-verified" as good news.
|
|
218
|
+
*/
|
|
219
|
+
readonly sourcesFound: number;
|
|
220
|
+
/**
|
|
221
|
+
* Statements not walked because the syntax tree was nested deeper than the
|
|
222
|
+
* walk will go.
|
|
223
|
+
*
|
|
224
|
+
* Distinct from depthTruncations, which counts CALL chains. This one counts
|
|
225
|
+
* places where a single EXPRESSION was too deep - and it exists because a
|
|
226
|
+
* 25,809-file scan of the TypeScript repository died with "Maximum call stack
|
|
227
|
+
* size exceeded" on a file containing one expression with 1,499 `+` operators.
|
|
228
|
+
* The walk recursed 1,499 frames and took the process with it.
|
|
229
|
+
*
|
|
230
|
+
* A crash is the worst possible answer: it produces no report at all, so a
|
|
231
|
+
* whole scan is lost to one pathological file. Stopping is better - but only
|
|
232
|
+
* if we SAY we stopped, which is what this counter is for.
|
|
233
|
+
*/
|
|
234
|
+
readonly astTruncations: number;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Code the tracer did not walk because a condition fixed at compile time means
|
|
238
|
+
* it cannot run - the dead arm of an `if`, a case a decided switch cannot
|
|
239
|
+
* reach. Not a finding, and not a proof about any one line: a record of a
|
|
240
|
+
* decision, so that a flow which disappeared because of it did not disappear
|
|
241
|
+
* silently.
|
|
242
|
+
*/
|
|
243
|
+
export interface UnreachableRegion {
|
|
244
|
+
readonly node: Node;
|
|
245
|
+
readonly filePath: string;
|
|
246
|
+
readonly why: string;
|
|
247
|
+
}
|
|
248
|
+
export interface TraceOutcome {
|
|
249
|
+
readonly flows: readonly FlowResult[];
|
|
250
|
+
readonly sanitized: readonly SanitizedFlow[];
|
|
251
|
+
readonly unreachable: readonly UnreachableRegion[];
|
|
252
|
+
/** Reads from a local map resolved to the one put that supplies them - see local-map.ts. */
|
|
253
|
+
readonly resolvedReads: readonly UnreachableRegion[];
|
|
254
|
+
/** Traces ended at a read whose result is new data - see FreshResultSpec. */
|
|
255
|
+
readonly freshStops: readonly UnreachableRegion[];
|
|
256
|
+
readonly limits: TraceLimits;
|
|
257
|
+
}
|
|
258
|
+
export declare function traceFile(file: ParsedFile, language: LanguageId, resolver?: CrossFileResolver,
|
|
259
|
+
/**
|
|
260
|
+
* Absolute path -> the path a human should see. Resolution needs absolute
|
|
261
|
+
* paths; reports need short ones. Keeping both straight matters more once a
|
|
262
|
+
* single finding can name two different files.
|
|
263
|
+
*/
|
|
264
|
+
toDisplay?: (absolutePath: string) => string): TraceOutcome;
|
|
265
|
+
export { type TaintDictionary };
|