@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,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE TREE STORE (how a scan stops running out of memory)
|
|
3
|
+
* ========================================================
|
|
4
|
+
*
|
|
5
|
+
* THE PROBLEM, STATED HONESTLY.
|
|
6
|
+
*
|
|
7
|
+
* A tree-sitter syntax tree does not live in JavaScript memory. It lives in a
|
|
8
|
+
* WebAssembly heap that the garbage collector cannot see into, so a tree is
|
|
9
|
+
* never collected, however dead it looks - the only thing that frees one is an
|
|
10
|
+
* explicit call to tree.delete().
|
|
11
|
+
*
|
|
12
|
+
* Until this module existed, nothing in this project called delete(). Every file
|
|
13
|
+
* parsed stayed parsed until the process exited. Measured on Django:
|
|
14
|
+
*
|
|
15
|
+
* 248 files 0.9 MB source 173 MB peak
|
|
16
|
+
* 992 files 6.9 MB source 525 MB peak ~59x per marginal byte
|
|
17
|
+
*
|
|
18
|
+
* which puts the hard ceiling at roughly 44 MB of source on an 8 GB machine.
|
|
19
|
+
* Most serious repositories are bigger than that. The scan does not slow down
|
|
20
|
+
* at the ceiling; it is killed by the operating system, which is a worse
|
|
21
|
+
* failure than a slow answer because it produces no answer at all.
|
|
22
|
+
*
|
|
23
|
+
* THE EXPLANATION THAT WAS IN THE CODE, AND WHY IT WAS WRONG.
|
|
24
|
+
*
|
|
25
|
+
* A comment above the parse loop said the trees had to be held because
|
|
26
|
+
* cross-file tracing must be able to resolve into any file at any time, and
|
|
27
|
+
* that this was why `--no-cross-file` existed. Plausible, and false. Measured:
|
|
28
|
+
*
|
|
29
|
+
* cross-file ON 528.8 MB
|
|
30
|
+
* cross-file OFF 535.8 MB
|
|
31
|
+
*
|
|
32
|
+
* Turning the feature off freed nothing, because the trees were never freed in
|
|
33
|
+
* either mode. The stated cause was a rationalisation of an accident. It is
|
|
34
|
+
* recorded here because a wrong explanation is more expensive than no
|
|
35
|
+
* explanation: it stops anybody looking.
|
|
36
|
+
*
|
|
37
|
+
* WHAT THIS MODULE DOES.
|
|
38
|
+
*
|
|
39
|
+
* It owns every tree in the scan and holds as many as a stated budget allows,
|
|
40
|
+
* evicting the least recently used one when the budget is exceeded. A file
|
|
41
|
+
* whose tree was evicted is simply parsed again when something needs it. So:
|
|
42
|
+
*
|
|
43
|
+
* - a project that fits in the budget behaves exactly as before, parses each
|
|
44
|
+
* file exactly once, and runs at exactly the old speed;
|
|
45
|
+
* - a project that does not fit gets slower instead of getting killed.
|
|
46
|
+
*
|
|
47
|
+
* Time is a renewable resource and a dead process is not.
|
|
48
|
+
*
|
|
49
|
+
* PINNING - THE PART THAT WOULD BE A CRASH IF IT WERE WRONG.
|
|
50
|
+
*
|
|
51
|
+
* Evicting a tree frees the WebAssembly memory that every Node object from it
|
|
52
|
+
* points into. A Node whose tree has been deleted is not an error; it is a
|
|
53
|
+
* dangling pointer into a heap that now holds something else, and reading it
|
|
54
|
+
* yields silent nonsense. So a tree is pinned for as long as anything might
|
|
55
|
+
* still hold a node from it, and eviction skips pinned entries entirely, even
|
|
56
|
+
* when that means going over budget. Going over budget is visible in the stats
|
|
57
|
+
* and is the right answer: correctness first, then memory, then speed.
|
|
58
|
+
*/
|
|
59
|
+
import { parseSourceSync } from './parser.js';
|
|
60
|
+
/**
|
|
61
|
+
* How many bytes of WebAssembly heap one byte of source costs once parsed.
|
|
62
|
+
*
|
|
63
|
+
* MEASURED, not guessed: 6.9 MB of Django source produced a 525 MB peak against
|
|
64
|
+
* a ~120 MB floor (Node plus seven loaded grammars), which is about 59x. Sixty
|
|
65
|
+
* is used because the number varies by language and an underestimate is the
|
|
66
|
+
* dangerous direction - it would let the store believe it is inside a budget it
|
|
67
|
+
* has already blown.
|
|
68
|
+
*
|
|
69
|
+
* The estimate only decides WHEN to evict. Nothing is ever wrong because this
|
|
70
|
+
* number is off; the scan just holds slightly more or less than intended.
|
|
71
|
+
*/
|
|
72
|
+
export const TREE_BYTES_PER_SOURCE_BYTE = 60;
|
|
73
|
+
/**
|
|
74
|
+
* Default budget: roughly a gigabyte of trees, which is about 17 MB of source
|
|
75
|
+
* held at once. Chosen so that every project this tool has been run against so
|
|
76
|
+
* far fits entirely and therefore parses each file exactly once - the redesign
|
|
77
|
+
* should cost nothing on the sizes people actually scan, and only start trading
|
|
78
|
+
* time for memory past the point where the old code died.
|
|
79
|
+
*/
|
|
80
|
+
export const DEFAULT_TREE_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
81
|
+
/**
|
|
82
|
+
* Find the node at an exact byte range in a freshly parsed tree.
|
|
83
|
+
*
|
|
84
|
+
* This is the hinge of the whole design. The index records a function body as
|
|
85
|
+
* two byte offsets instead of a Node, because a Node keeps its tree alive and
|
|
86
|
+
* the point is to let trees go. When the tracer finally wants that body, the
|
|
87
|
+
* file is parsed again and the node is looked up here.
|
|
88
|
+
*
|
|
89
|
+
* Two things make it safe. The parse is deterministic - same bytes, same
|
|
90
|
+
* grammar, same tree - so the node is certainly there. And the result is
|
|
91
|
+
* CHECKED: the range and the node type must both match what was recorded, or
|
|
92
|
+
* this returns null and the caller declines to resolve. Tracing into a node we
|
|
93
|
+
* merely believe is the right one would manufacture exactly the kind of
|
|
94
|
+
* confident, wrong proof this project exists to avoid.
|
|
95
|
+
*
|
|
96
|
+
* The walk upward is needed because descendantForIndex returns the SMALLEST
|
|
97
|
+
* node covering a range, and a node can share its exact extent with its parent
|
|
98
|
+
* (a block that is its own statement, an expression that is its own body). The
|
|
99
|
+
* recorded type disambiguates them.
|
|
100
|
+
*/
|
|
101
|
+
export function nodeAtRange(root, startIndex, endIndex, type) {
|
|
102
|
+
let node = root.descendantForIndex(startIndex, endIndex);
|
|
103
|
+
let hops = 0;
|
|
104
|
+
while (node && hops < 64) {
|
|
105
|
+
if (node.startIndex === startIndex && node.endIndex === endIndex && node.type === type) {
|
|
106
|
+
return node;
|
|
107
|
+
}
|
|
108
|
+
node = node.parent;
|
|
109
|
+
hops++;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
export function createTreeStore(budgetBytes = DEFAULT_TREE_BUDGET_BYTES) {
|
|
114
|
+
const entries = new Map();
|
|
115
|
+
let heldBytes = 0;
|
|
116
|
+
let peakEstimatedBytes = 0;
|
|
117
|
+
let clock = 0;
|
|
118
|
+
let parses = 0;
|
|
119
|
+
let reparses = 0;
|
|
120
|
+
let evictions = 0;
|
|
121
|
+
let spilled = false;
|
|
122
|
+
let overBudgetWhileAllPinned = 0;
|
|
123
|
+
const touch = (entry) => {
|
|
124
|
+
entry.lastUsed = ++clock;
|
|
125
|
+
};
|
|
126
|
+
const drop = (entry) => {
|
|
127
|
+
if (!entry.tree)
|
|
128
|
+
return;
|
|
129
|
+
entry.tree.delete();
|
|
130
|
+
entry.tree = null;
|
|
131
|
+
heldBytes -= entry.estimatedBytes;
|
|
132
|
+
evictions++;
|
|
133
|
+
spilled = true;
|
|
134
|
+
};
|
|
135
|
+
/** Evict least-recently-used unpinned trees until we are inside the budget. */
|
|
136
|
+
const makeRoom = () => {
|
|
137
|
+
if (heldBytes <= budgetBytes)
|
|
138
|
+
return;
|
|
139
|
+
const candidates = [...entries.values()]
|
|
140
|
+
.filter((e) => e.tree !== null && e.pins === 0)
|
|
141
|
+
.sort((a, b) => a.lastUsed - b.lastUsed);
|
|
142
|
+
for (const candidate of candidates) {
|
|
143
|
+
if (heldBytes <= budgetBytes)
|
|
144
|
+
return;
|
|
145
|
+
drop(candidate);
|
|
146
|
+
}
|
|
147
|
+
// Everything still resident is pinned. We go over budget rather than free
|
|
148
|
+
// a tree somebody is holding a node from. Counted, never hidden.
|
|
149
|
+
if (heldBytes > budgetBytes)
|
|
150
|
+
overBudgetWhileAllPinned++;
|
|
151
|
+
};
|
|
152
|
+
const note = () => {
|
|
153
|
+
if (heldBytes > peakEstimatedBytes)
|
|
154
|
+
peakEstimatedBytes = heldBytes;
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
admit(path, source, grammar, tree) {
|
|
158
|
+
const existing = entries.get(path);
|
|
159
|
+
if (existing) {
|
|
160
|
+
// Re-admitting the same path would leak the tree already held for it.
|
|
161
|
+
if (existing.tree && existing.tree !== tree)
|
|
162
|
+
existing.tree.delete();
|
|
163
|
+
else if (existing.tree === null)
|
|
164
|
+
heldBytes += existing.estimatedBytes;
|
|
165
|
+
existing.tree = tree;
|
|
166
|
+
touch(existing);
|
|
167
|
+
note();
|
|
168
|
+
makeRoom();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const entry = {
|
|
172
|
+
path,
|
|
173
|
+
source,
|
|
174
|
+
grammar,
|
|
175
|
+
estimatedBytes: source.length * TREE_BYTES_PER_SOURCE_BYTE,
|
|
176
|
+
tree,
|
|
177
|
+
pins: 0,
|
|
178
|
+
lastUsed: ++clock,
|
|
179
|
+
};
|
|
180
|
+
entries.set(path, entry);
|
|
181
|
+
heldBytes += entry.estimatedBytes;
|
|
182
|
+
parses++;
|
|
183
|
+
note();
|
|
184
|
+
makeRoom();
|
|
185
|
+
},
|
|
186
|
+
get(path) {
|
|
187
|
+
const entry = entries.get(path);
|
|
188
|
+
if (!entry) {
|
|
189
|
+
throw new Error(`No file registered at ${path}. Every file must be admitted during the ` +
|
|
190
|
+
'first pass; asking for one that was not is a bug, not a missing file.');
|
|
191
|
+
}
|
|
192
|
+
if (entry.tree) {
|
|
193
|
+
touch(entry);
|
|
194
|
+
return entry.tree;
|
|
195
|
+
}
|
|
196
|
+
entry.tree = parseSourceSync(entry.source, entry.grammar);
|
|
197
|
+
heldBytes += entry.estimatedBytes;
|
|
198
|
+
parses++;
|
|
199
|
+
reparses++;
|
|
200
|
+
touch(entry);
|
|
201
|
+
note();
|
|
202
|
+
makeRoom();
|
|
203
|
+
return entry.tree;
|
|
204
|
+
},
|
|
205
|
+
pin(path) {
|
|
206
|
+
const entry = entries.get(path);
|
|
207
|
+
if (entry)
|
|
208
|
+
entry.pins++;
|
|
209
|
+
},
|
|
210
|
+
releaseAll() {
|
|
211
|
+
for (const entry of entries.values())
|
|
212
|
+
entry.pins = 0;
|
|
213
|
+
makeRoom();
|
|
214
|
+
},
|
|
215
|
+
disposeAll() {
|
|
216
|
+
for (const entry of entries.values()) {
|
|
217
|
+
if (entry.tree)
|
|
218
|
+
entry.tree.delete();
|
|
219
|
+
entry.tree = null;
|
|
220
|
+
}
|
|
221
|
+
heldBytes = 0;
|
|
222
|
+
entries.clear();
|
|
223
|
+
},
|
|
224
|
+
stats: () => ({
|
|
225
|
+
budgetBytes,
|
|
226
|
+
filesResident: [...entries.values()].filter((e) => e.tree !== null).length,
|
|
227
|
+
peakEstimatedBytes,
|
|
228
|
+
parses,
|
|
229
|
+
reparses,
|
|
230
|
+
evictions,
|
|
231
|
+
spilled,
|
|
232
|
+
overBudgetWhileAllPinned,
|
|
233
|
+
}),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=tree-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-store.js","sourceRoot":"","sources":["../../../src/parse/tree-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dependency check's rule text and report shape, kept apart from deps.ts
|
|
3
|
+
* because the reports (which also run in the browser) need them and deps.ts
|
|
4
|
+
* reads the file system. Browser-safe: no imports.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEPENDENCY_RULE: {
|
|
7
|
+
readonly id: "vulnerable-dependency";
|
|
8
|
+
readonly name: "Library version with a published advisory";
|
|
9
|
+
readonly owasp: "A03:2025 Software Supply Chain Failures";
|
|
10
|
+
readonly summary: "A library this project pins has a published security advisory for that exact version.";
|
|
11
|
+
readonly fix: "Upgrade the library to a fixed version and regenerate the lockfile.";
|
|
12
|
+
readonly explanation: string;
|
|
13
|
+
readonly limitations: string;
|
|
14
|
+
};
|
|
15
|
+
/** What the dependency check read and asked, for the report's "what was checked" section. */
|
|
16
|
+
export interface DependencyReport {
|
|
17
|
+
readonly source: 'api.osv.dev';
|
|
18
|
+
readonly checkedAt: string;
|
|
19
|
+
readonly read: ReadonlyArray<{
|
|
20
|
+
readonly file: string;
|
|
21
|
+
readonly packages: number;
|
|
22
|
+
readonly skipped: readonly string[];
|
|
23
|
+
}>;
|
|
24
|
+
readonly notRead: readonly string[];
|
|
25
|
+
readonly checked: number;
|
|
26
|
+
readonly incomplete: readonly string[];
|
|
27
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dependency check's rule text and report shape, kept apart from deps.ts
|
|
3
|
+
* because the reports (which also run in the browser) need them and deps.ts
|
|
4
|
+
* reads the file system. Browser-safe: no imports.
|
|
5
|
+
*/
|
|
6
|
+
export const DEPENDENCY_RULE = {
|
|
7
|
+
id: 'vulnerable-dependency',
|
|
8
|
+
name: 'Library version with a published advisory',
|
|
9
|
+
owasp: 'A03:2025 Software Supply Chain Failures',
|
|
10
|
+
summary: 'A library this project pins has a published security advisory for that exact version.',
|
|
11
|
+
fix: 'Upgrade the library to a fixed version and regenerate the lockfile.',
|
|
12
|
+
explanation: 'Most of the code a project runs was written by someone else and arrives through its lockfile. ' +
|
|
13
|
+
'When a pinned library version has a published advisory, the flaw is installed with it - whatever ' +
|
|
14
|
+
'the project\'s own code does. The fix is usually an upgrade, not a code change.',
|
|
15
|
+
limitations: 'Says the vulnerable version is installed, not that this project calls the vulnerable part - many ' +
|
|
16
|
+
'advisories cover one function the project never uses. Reads package-lock.json, composer.lock, ' +
|
|
17
|
+
'requirements.txt (== pins only) and go.mod; other lockfiles are listed as not read. Advisories come ' +
|
|
18
|
+
'from osv.dev at scan time, so the same lockfile can gain findings tomorrow. Runs only with --deps, ' +
|
|
19
|
+
'because it is the one check that sends anything off the machine: package names and versions, never code.',
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=dependency-rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-rule.js","sourceRoot":"","sources":["../../../src/platform/dependency-rule.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,EAAE,EAAE,uBAAuB;IAC3B,IAAI,EAAE,2CAA2C;IACjD,KAAK,EAAE,yCAAyC;IAChD,OAAO,EAAE,uFAAuF;IAChG,GAAG,EAAE,qEAAqE;IAC1E,WAAW,EACT,gGAAgG;QAChG,mGAAmG;QACnG,iFAAiF;IACnF,WAAW,EACT,mGAAmG;QACnG,gGAAgG;QAChG,sGAAsG;QACtG,qGAAqG;QACrG,0GAA0G;CACpG,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE DEPENDENCY CHECK: DO THE LIBRARIES THIS PROJECT PINS HAVE KNOWN ADVISORIES?
|
|
3
|
+
* ==============================================================================
|
|
4
|
+
*
|
|
5
|
+
* Every other check reads the code a project wrote. Most of what a project
|
|
6
|
+
* runs, it did not write: it is in the lockfile. This reads the versions a
|
|
7
|
+
* lockfile pins and asks OSV (osv.dev, the open advisory database behind
|
|
8
|
+
* OSV-Scanner) which of them have published advisories.
|
|
9
|
+
*
|
|
10
|
+
* WHY IT IS A SEPARATE COMMAND. It is the one check that talks to the network.
|
|
11
|
+
* `defuse scan` promises nothing leaves the machine, and keeps that promise;
|
|
12
|
+
* `defuse deps` sends package names and versions - never code - to api.osv.dev,
|
|
13
|
+
* and says so before it does.
|
|
14
|
+
*
|
|
15
|
+
* WHAT IT CAN AND CANNOT CLAIM. A pinned version that matches an advisory says
|
|
16
|
+
* the vulnerable code is installed. It says nothing about whether this project
|
|
17
|
+
* ever calls it. Signature-grade by nature, and every result says so.
|
|
18
|
+
*
|
|
19
|
+
* Lockfiles it recognises but cannot read yet are listed by name, and lines it
|
|
20
|
+
* could not read are counted with the reason - never silently skipped.
|
|
21
|
+
*/
|
|
22
|
+
import { type SignatureFinding } from '../core/finding.js';
|
|
23
|
+
import { DEPENDENCY_RULE, type DependencyReport } from './dependency-rule.js';
|
|
24
|
+
export { DEPENDENCY_RULE, type DependencyReport };
|
|
25
|
+
export type Ecosystem = 'npm' | 'PyPI' | 'Packagist' | 'Go';
|
|
26
|
+
export interface Pinned {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly version: string;
|
|
29
|
+
readonly ecosystem: Ecosystem;
|
|
30
|
+
/** The lockfile it was read from, as found. */
|
|
31
|
+
readonly file: string;
|
|
32
|
+
}
|
|
33
|
+
export interface LockfileRead {
|
|
34
|
+
readonly file: string;
|
|
35
|
+
readonly packages: readonly Pinned[];
|
|
36
|
+
/** What was in the file but not checked, each with the reason. */
|
|
37
|
+
readonly skipped: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
export interface Advisory {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly summary: string;
|
|
42
|
+
readonly aliases: readonly string[];
|
|
43
|
+
/** OSV's own rating where the source gives one (critical/high/medium/low), else "unrated". */
|
|
44
|
+
readonly severity: string;
|
|
45
|
+
/** Versions the advisory says fix it, for this package. Empty when none is published. */
|
|
46
|
+
readonly fixed: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
export interface DependencyHit {
|
|
49
|
+
readonly pkg: Pinned;
|
|
50
|
+
readonly advisories: readonly Advisory[];
|
|
51
|
+
}
|
|
52
|
+
/** Recognised by name, not read yet. Listed in the output, never ignored in silence. */
|
|
53
|
+
export declare const UNREAD_LOCKFILES: ReadonlySet<string>;
|
|
54
|
+
/** Parse one lockfile by its file name. Null when the name is not one this reads. */
|
|
55
|
+
export declare function readLockfile(file: string, text: string): LockfileRead | null;
|
|
56
|
+
/** Every lockfile under `target`, read; plus the recognised ones that are not. `exclude` means what scan --exclude means. */
|
|
57
|
+
export declare function findLockfiles(target: string, exclude?: readonly string[]): {
|
|
58
|
+
read: LockfileRead[];
|
|
59
|
+
unread: string[];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Ask OSV about every pinned package. Sends name, ecosystem and version only.
|
|
63
|
+
* `fetchImpl` is injectable so the tests can run without a network.
|
|
64
|
+
*/
|
|
65
|
+
export declare function queryOsv(pinned: readonly Pinned[], fetchImpl?: typeof fetch): Promise<{
|
|
66
|
+
hits: DependencyHit[];
|
|
67
|
+
checked: number;
|
|
68
|
+
incomplete: string[];
|
|
69
|
+
}>;
|
|
70
|
+
/**
|
|
71
|
+
* The whole check for `scan --deps`: find lockfiles, ask OSV, and return one
|
|
72
|
+
* signature-based finding per affected package, located on its lockfile line.
|
|
73
|
+
* `display` turns an absolute path into the one the report shows.
|
|
74
|
+
*/
|
|
75
|
+
export declare function checkDependencies(target: string, display: (file: string) => string, exclude?: readonly string[], fetchImpl?: typeof fetch): Promise<{
|
|
76
|
+
findings: SignatureFinding[];
|
|
77
|
+
report: DependencyReport;
|
|
78
|
+
}>;
|
|
79
|
+
export declare function dependencyFinding(hit: DependencyHit, lines: readonly string[], file: string): SignatureFinding;
|
|
80
|
+
/**
|
|
81
|
+
* One flaw, one entry. OSV holds the same CVE once per database (GHSA-, GO-,
|
|
82
|
+
* PYSEC-...), so a project would otherwise see one bug listed two or three
|
|
83
|
+
* times. Entries naming each other, or sharing an alias, are folded into one;
|
|
84
|
+
* a rated entry wins over an unrated one, and every id is kept in `aliases`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function mergeSame(advisories: readonly Advisory[]): Advisory[];
|