@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.
Files changed (236) hide show
  1. package/LICENSE +235 -0
  2. package/README.md +561 -2
  3. package/dist/src/cli.d.ts +20 -0
  4. package/dist/src/cli.js +619 -0
  5. package/dist/src/cli.js.map +1 -0
  6. package/dist/src/core/analyze.d.ts +178 -0
  7. package/dist/src/core/analyze.js +834 -0
  8. package/dist/src/core/analyze.js.map +1 -0
  9. package/dist/src/core/coverage.d.ts +87 -0
  10. package/dist/src/core/coverage.js +181 -0
  11. package/dist/src/core/coverage.js.map +1 -0
  12. package/dist/src/core/finding.d.ts +196 -0
  13. package/dist/src/core/finding.js +178 -0
  14. package/dist/src/core/finding.js.map +1 -0
  15. package/dist/src/core/licence.d.ts +50 -0
  16. package/dist/src/core/licence.js +56 -0
  17. package/dist/src/core/licence.js.map +1 -0
  18. package/dist/src/core/paths.d.ts +21 -0
  19. package/dist/src/core/paths.js +58 -0
  20. package/dist/src/core/paths.js.map +1 -0
  21. package/dist/src/core/score.d.ts +137 -0
  22. package/dist/src/core/score.js +232 -0
  23. package/dist/src/core/score.js.map +1 -0
  24. package/dist/src/dast/targets.d.ts +30 -0
  25. package/dist/src/dast/targets.js +79 -0
  26. package/dist/src/dast/targets.js.map +1 -0
  27. package/dist/src/dast/zap.d.ts +42 -0
  28. package/dist/src/dast/zap.js +184 -0
  29. package/dist/src/dast/zap.js.map +1 -0
  30. package/dist/src/engine/scan.d.ts +80 -0
  31. package/dist/src/engine/scan.js +129 -0
  32. package/dist/src/engine/scan.js.map +1 -0
  33. package/dist/src/engine/shapes.d.ts +47 -0
  34. package/dist/src/engine/shapes.js +328 -0
  35. package/dist/src/engine/shapes.js.map +1 -0
  36. package/dist/src/engine/walk.d.ts +60 -0
  37. package/dist/src/engine/walk.js +156 -0
  38. package/dist/src/engine/walk.js.map +1 -0
  39. package/dist/src/parse/astDump.d.ts +31 -0
  40. package/dist/src/parse/astDump.js +66 -0
  41. package/dist/src/parse/astDump.js.map +1 -0
  42. package/dist/src/parse/languages.d.ts +70 -0
  43. package/dist/src/parse/languages.js +174 -0
  44. package/dist/src/parse/languages.js.map +1 -0
  45. package/dist/src/parse/location.d.ts +17 -0
  46. package/dist/src/parse/location.js +38 -0
  47. package/dist/src/parse/location.js.map +1 -0
  48. package/dist/src/parse/node-grammars.d.ts +12 -0
  49. package/dist/src/parse/node-grammars.js +35 -0
  50. package/dist/src/parse/node-grammars.js.map +1 -0
  51. package/dist/src/parse/parser.d.ts +126 -0
  52. package/dist/src/parse/parser.js +202 -0
  53. package/dist/src/parse/parser.js.map +1 -0
  54. package/dist/src/parse/tree-store.d.ts +139 -0
  55. package/dist/src/parse/tree-store.js +236 -0
  56. package/dist/src/parse/tree-store.js.map +1 -0
  57. package/dist/src/platform/dependency-rule.d.ts +27 -0
  58. package/dist/src/platform/dependency-rule.js +21 -0
  59. package/dist/src/platform/dependency-rule.js.map +1 -0
  60. package/dist/src/platform/deps.d.ts +86 -0
  61. package/dist/src/platform/deps.js +304 -0
  62. package/dist/src/platform/deps.js.map +1 -0
  63. package/dist/src/platform/php-facts.d.ts +49 -0
  64. package/dist/src/platform/php-facts.js +67 -0
  65. package/dist/src/platform/php-facts.js.map +1 -0
  66. package/dist/src/platform/runtime-version.d.ts +78 -0
  67. package/dist/src/platform/runtime-version.js +573 -0
  68. package/dist/src/platform/runtime-version.js.map +1 -0
  69. package/dist/src/report/colors.d.ts +50 -0
  70. package/dist/src/report/colors.js +97 -0
  71. package/dist/src/report/colors.js.map +1 -0
  72. package/dist/src/report/coverage-table.d.ts +90 -0
  73. package/dist/src/report/coverage-table.js +291 -0
  74. package/dist/src/report/coverage-table.js.map +1 -0
  75. package/dist/src/report/html-report.d.ts +66 -0
  76. package/dist/src/report/html-report.js +489 -0
  77. package/dist/src/report/html-report.js.map +1 -0
  78. package/dist/src/report/human.d.ts +44 -0
  79. package/dist/src/report/human.js +575 -0
  80. package/dist/src/report/human.js.map +1 -0
  81. package/dist/src/report/json.d.ts +24 -0
  82. package/dist/src/report/json.js +178 -0
  83. package/dist/src/report/json.js.map +1 -0
  84. package/dist/src/report/mark.d.ts +17 -0
  85. package/dist/src/report/mark.js +39 -0
  86. package/dist/src/report/mark.js.map +1 -0
  87. package/dist/src/report/pricing.d.ts +33 -0
  88. package/dist/src/report/pricing.js +343 -0
  89. package/dist/src/report/pricing.js.map +1 -0
  90. package/dist/src/report/sarif.d.ts +39 -0
  91. package/dist/src/report/sarif.js +219 -0
  92. package/dist/src/report/sarif.js.map +1 -0
  93. package/dist/src/report/site.d.ts +87 -0
  94. package/dist/src/report/site.js +742 -0
  95. package/dist/src/report/site.js.map +1 -0
  96. package/dist/src/rules/code-injection.d.ts +47 -0
  97. package/dist/src/rules/code-injection.js +326 -0
  98. package/dist/src/rules/code-injection.js.map +1 -0
  99. package/dist/src/rules/command-injection.d.ts +30 -0
  100. package/dist/src/rules/command-injection.js +286 -0
  101. package/dist/src/rules/command-injection.js.map +1 -0
  102. package/dist/src/rules/contract.d.ts +198 -0
  103. package/dist/src/rules/contract.js +39 -0
  104. package/dist/src/rules/contract.js.map +1 -0
  105. package/dist/src/rules/format-string.d.ts +46 -0
  106. package/dist/src/rules/format-string.js +146 -0
  107. package/dist/src/rules/format-string.js.map +1 -0
  108. package/dist/src/rules/hardcoded-secret.d.ts +38 -0
  109. package/dist/src/rules/hardcoded-secret.js +440 -0
  110. package/dist/src/rules/hardcoded-secret.js.map +1 -0
  111. package/dist/src/rules/lib/guards.d.ts +168 -0
  112. package/dist/src/rules/lib/guards.js +1179 -0
  113. package/dist/src/rules/lib/guards.js.map +1 -0
  114. package/dist/src/rules/lib/html-safety.d.ts +75 -0
  115. package/dist/src/rules/lib/html-safety.js +755 -0
  116. package/dist/src/rules/lib/html-safety.js.map +1 -0
  117. package/dist/src/rules/lib/local-build.d.ts +59 -0
  118. package/dist/src/rules/lib/local-build.js +320 -0
  119. package/dist/src/rules/lib/local-build.js.map +1 -0
  120. package/dist/src/rules/lib/strings.d.ts +208 -0
  121. package/dist/src/rules/lib/strings.js +1970 -0
  122. package/dist/src/rules/lib/strings.js.map +1 -0
  123. package/dist/src/rules/path-traversal.d.ts +33 -0
  124. package/dist/src/rules/path-traversal.js +96 -0
  125. package/dist/src/rules/path-traversal.js.map +1 -0
  126. package/dist/src/rules/registry.d.ts +19 -0
  127. package/dist/src/rules/registry.js +77 -0
  128. package/dist/src/rules/registry.js.map +1 -0
  129. package/dist/src/rules/sql-injection.d.ts +36 -0
  130. package/dist/src/rules/sql-injection.js +299 -0
  131. package/dist/src/rules/sql-injection.js.map +1 -0
  132. package/dist/src/rules/ssrf.d.ts +39 -0
  133. package/dist/src/rules/ssrf.js +111 -0
  134. package/dist/src/rules/ssrf.js.map +1 -0
  135. package/dist/src/rules/unbounded-copy.d.ts +37 -0
  136. package/dist/src/rules/unbounded-copy.js +147 -0
  137. package/dist/src/rules/unbounded-copy.js.map +1 -0
  138. package/dist/src/rules/unsafe-deserialization.d.ts +43 -0
  139. package/dist/src/rules/unsafe-deserialization.js +276 -0
  140. package/dist/src/rules/unsafe-deserialization.js.map +1 -0
  141. package/dist/src/rules/weak-hash.d.ts +37 -0
  142. package/dist/src/rules/weak-hash.js +197 -0
  143. package/dist/src/rules/weak-hash.js.map +1 -0
  144. package/dist/src/rules/xss.d.ts +38 -0
  145. package/dist/src/rules/xss.js +527 -0
  146. package/dist/src/rules/xss.js.map +1 -0
  147. package/dist/src/taint/content-type.d.ts +19 -0
  148. package/dist/src/taint/content-type.js +20 -0
  149. package/dist/src/taint/content-type.js.map +1 -0
  150. package/dist/src/taint/dictionaries.d.ts +23 -0
  151. package/dist/src/taint/dictionaries.js +1778 -0
  152. package/dist/src/taint/dictionaries.js.map +1 -0
  153. package/dist/src/taint/fold.d.ts +59 -0
  154. package/dist/src/taint/fold.js +1177 -0
  155. package/dist/src/taint/fold.js.map +1 -0
  156. package/dist/src/taint/java-scope.d.ts +61 -0
  157. package/dist/src/taint/java-scope.js +212 -0
  158. package/dist/src/taint/java-scope.js.map +1 -0
  159. package/dist/src/taint/local-map.d.ts +79 -0
  160. package/dist/src/taint/local-map.js +603 -0
  161. package/dist/src/taint/local-map.js.map +1 -0
  162. package/dist/src/taint/project.d.ts +172 -0
  163. package/dist/src/taint/project.js +617 -0
  164. package/dist/src/taint/project.js.map +1 -0
  165. package/dist/src/taint/tracer.d.ts +265 -0
  166. package/dist/src/taint/tracer.js +3817 -0
  167. package/dist/src/taint/tracer.js.map +1 -0
  168. package/dist/src/taint/types.d.ts +484 -0
  169. package/dist/src/taint/types.js +47 -0
  170. package/dist/src/taint/types.js.map +1 -0
  171. package/dist/src/taint/visibility.d.ts +25 -0
  172. package/dist/src/taint/visibility.js +96 -0
  173. package/dist/src/taint/visibility.js.map +1 -0
  174. package/docs/examples/defuse.json +20 -0
  175. package/docs/examples/dvwa-urls.json +14 -0
  176. package/docs/examples/github-code-scanning.yml +54 -0
  177. package/docs/examples/tiny.js +4 -0
  178. package/package.json +73 -4
  179. package/src/cli.ts +747 -0
  180. package/src/core/analyze.ts +1072 -0
  181. package/src/core/coverage.ts +269 -0
  182. package/src/core/finding.ts +311 -0
  183. package/src/core/licence.ts +61 -0
  184. package/src/core/paths.ts +59 -0
  185. package/src/core/score.ts +353 -0
  186. package/src/dast/targets.ts +121 -0
  187. package/src/dast/zap.ts +246 -0
  188. package/src/engine/scan.ts +200 -0
  189. package/src/engine/shapes.ts +348 -0
  190. package/src/engine/walk.ts +196 -0
  191. package/src/parse/astDump.ts +85 -0
  192. package/src/parse/languages.ts +232 -0
  193. package/src/parse/location.ts +44 -0
  194. package/src/parse/node-grammars.ts +41 -0
  195. package/src/parse/parser.ts +262 -0
  196. package/src/parse/tree-store.ts +301 -0
  197. package/src/platform/dependency-rule.ts +33 -0
  198. package/src/platform/deps.ts +374 -0
  199. package/src/platform/php-facts.ts +85 -0
  200. package/src/platform/runtime-version.ts +710 -0
  201. package/src/report/colors.ts +107 -0
  202. package/src/report/coverage-table.ts +359 -0
  203. package/src/report/html-report.ts +601 -0
  204. package/src/report/human.ts +811 -0
  205. package/src/report/json.ts +189 -0
  206. package/src/report/mark.ts +47 -0
  207. package/src/report/pricing.ts +374 -0
  208. package/src/report/sarif.ts +235 -0
  209. package/src/report/site.ts +760 -0
  210. package/src/rules/code-injection.ts +346 -0
  211. package/src/rules/command-injection.ts +296 -0
  212. package/src/rules/contract.ts +220 -0
  213. package/src/rules/format-string.ts +158 -0
  214. package/src/rules/hardcoded-secret.ts +485 -0
  215. package/src/rules/lib/guards.ts +1235 -0
  216. package/src/rules/lib/html-safety.ts +793 -0
  217. package/src/rules/lib/local-build.ts +329 -0
  218. package/src/rules/lib/strings.ts +2034 -0
  219. package/src/rules/path-traversal.ts +101 -0
  220. package/src/rules/registry.ts +90 -0
  221. package/src/rules/sql-injection.ts +311 -0
  222. package/src/rules/ssrf.ts +118 -0
  223. package/src/rules/unbounded-copy.ts +162 -0
  224. package/src/rules/unsafe-deserialization.ts +291 -0
  225. package/src/rules/weak-hash.ts +226 -0
  226. package/src/rules/xss.ts +572 -0
  227. package/src/taint/content-type.ts +20 -0
  228. package/src/taint/dictionaries.ts +1855 -0
  229. package/src/taint/fold.ts +1206 -0
  230. package/src/taint/java-scope.ts +200 -0
  231. package/src/taint/local-map.ts +594 -0
  232. package/src/taint/project.ts +774 -0
  233. package/src/taint/tracer.ts +4298 -0
  234. package/src/taint/types.ts +511 -0
  235. package/src/taint/visibility.ts +100 -0
  236. package/tsconfig.json +61 -0
@@ -0,0 +1,304 @@
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 { readdirSync, readFileSync, statSync } from 'node:fs';
23
+ import path from 'node:path';
24
+ import { signatureFinding } from '../core/finding.js';
25
+ import { DEPENDENCY_RULE } from './dependency-rule.js';
26
+ import { globToRegExp } from '../engine/walk.js';
27
+ export { DEPENDENCY_RULE };
28
+ /** A version that is a release, not a path, URL or branch. */
29
+ const RELEASE = /^v?\d/;
30
+ const READERS = {
31
+ 'package-lock.json': (text, file) => {
32
+ const lock = JSON.parse(text);
33
+ const found = [];
34
+ if (lock.packages) {
35
+ for (const [key, p] of Object.entries(lock.packages)) {
36
+ if (key === '' || p.link || !p.version)
37
+ continue; // the project itself, or a workspace link
38
+ found.push([p.name ?? key.slice(key.lastIndexOf('node_modules/') + 'node_modules/'.length), p.version]);
39
+ }
40
+ }
41
+ else {
42
+ const walk = (deps) => {
43
+ for (const [name, d] of Object.entries(deps ?? {})) {
44
+ if (d.version)
45
+ found.push([name, d.version]);
46
+ walk(d.dependencies);
47
+ }
48
+ };
49
+ walk(lock.dependencies);
50
+ }
51
+ return split(found, 'npm', file);
52
+ },
53
+ 'composer.lock': (text, file) => {
54
+ const lock = JSON.parse(text);
55
+ const all = [...(lock['packages'] ?? []), ...(lock['packages-dev'] ?? [])];
56
+ return split(all.map((p) => [p.name, p.version]), 'Packagist', file);
57
+ },
58
+ 'requirements.txt': (text, file) => {
59
+ const packages = [];
60
+ const skipped = [];
61
+ for (const raw of text.split(/\r?\n/)) {
62
+ const line = raw.replace(/\s+#.*$/, '').trim();
63
+ if (!line || line.startsWith('#'))
64
+ continue;
65
+ if (line.startsWith('-')) {
66
+ skipped.push(`${line} - an option line (-r, -e, --index-url...), not followed`);
67
+ continue;
68
+ }
69
+ const m = /^([A-Za-z0-9._-]+)(?:\[[^\]]*\])?\s*==\s*([^\s;,]+)$/.exec(line.split(';')[0].trim());
70
+ if (m)
71
+ packages.push({ name: m[1], version: m[2], ecosystem: 'PyPI', file });
72
+ else
73
+ skipped.push(`${line} - not pinned with ==, so the installed version is unknown`);
74
+ }
75
+ return { packages, skipped };
76
+ },
77
+ 'go.mod': (text, file) => {
78
+ const found = [];
79
+ let inRequire = false;
80
+ for (const raw of text.split(/\r?\n/)) {
81
+ const line = raw.replace(/\/\/.*$/, '').trim();
82
+ if (/^require\s*\($/.test(line))
83
+ inRequire = true;
84
+ else if (inRequire && line === ')')
85
+ inRequire = false;
86
+ else {
87
+ const m = (inRequire ? /^(\S+)\s+(\S+)$/ : /^require\s+(\S+)\s+(\S+)$/).exec(line);
88
+ if (m)
89
+ found.push([m[1], m[2]]);
90
+ }
91
+ }
92
+ const result = split(found, 'Go', file);
93
+ if (/^replace\b/m.test(text)) {
94
+ result.skipped.push('replace directives - not applied, so a replaced module is checked at its required version');
95
+ }
96
+ return result;
97
+ },
98
+ };
99
+ function split(found, ecosystem, file) {
100
+ const packages = [];
101
+ const skipped = [];
102
+ for (const [name, version] of found) {
103
+ if (RELEASE.test(version))
104
+ packages.push({ name, version, ecosystem, file });
105
+ else
106
+ skipped.push(`${name} ${version} - not a release version (a path, URL or branch)`);
107
+ }
108
+ return { packages, skipped };
109
+ }
110
+ /** Recognised by name, not read yet. Listed in the output, never ignored in silence. */
111
+ export const UNREAD_LOCKFILES = new Set([
112
+ 'yarn.lock', 'pnpm-lock.yaml', 'npm-shrinkwrap.json', 'Pipfile.lock', 'poetry.lock', 'uv.lock',
113
+ 'Cargo.lock', 'Gemfile.lock', 'pom.xml', 'build.gradle', 'build.gradle.kts', 'gradle.lockfile',
114
+ 'packages.lock.json',
115
+ ]);
116
+ const SKIP_DIRS = new Set(['node_modules', 'vendor', '.git', '.venv', 'venv', '__pycache__']);
117
+ /** Parse one lockfile by its file name. Null when the name is not one this reads. */
118
+ export function readLockfile(file, text) {
119
+ const reader = READERS[path.basename(file)];
120
+ if (!reader)
121
+ return null;
122
+ try {
123
+ return { file, ...reader(text, file) };
124
+ }
125
+ catch (error) {
126
+ return { file, packages: [], skipped: [`the whole file - it did not parse: ${error.message}`] };
127
+ }
128
+ }
129
+ /** Every lockfile under `target`, read; plus the recognised ones that are not. `exclude` means what scan --exclude means. */
130
+ export function findLockfiles(target, exclude = []) {
131
+ const read = [];
132
+ const unread = [];
133
+ const patterns = exclude.map(globToRegExp);
134
+ const visit = (p) => {
135
+ const relative = path.relative(target, p).split(path.sep).join('/');
136
+ if (relative && patterns.some((re) => re.test(relative)))
137
+ return;
138
+ if (statSync(p).isDirectory()) {
139
+ for (const entry of readdirSync(p))
140
+ if (!SKIP_DIRS.has(entry))
141
+ visit(path.join(p, entry));
142
+ return;
143
+ }
144
+ const name = path.basename(p);
145
+ if (UNREAD_LOCKFILES.has(name))
146
+ unread.push(p);
147
+ else if (READERS[name])
148
+ read.push(readLockfile(p, readFileSync(p, 'utf8')));
149
+ };
150
+ visit(target);
151
+ return { read, unread };
152
+ }
153
+ const OSV = 'https://api.osv.dev/v1';
154
+ const BATCH = 1000; // OSV's own limit per querybatch call
155
+ /**
156
+ * Ask OSV about every pinned package. Sends name, ecosystem and version only.
157
+ * `fetchImpl` is injectable so the tests can run without a network.
158
+ */
159
+ export async function queryOsv(pinned, fetchImpl = fetch) {
160
+ const unique = [...new Map(pinned.map((p) => [`${p.ecosystem}|${p.name}|${p.version}`, p])).values()];
161
+ const call = async (url, body) => {
162
+ const signal = AbortSignal.timeout(30_000);
163
+ const response = await fetchImpl(url, body
164
+ ? { method: 'POST', body: JSON.stringify(body), headers: { 'content-type': 'application/json' }, signal }
165
+ : { signal });
166
+ if (!response.ok)
167
+ throw new Error(`OSV answered ${response.status} for ${url}`);
168
+ return response.json();
169
+ };
170
+ const idsPer = [];
171
+ const incomplete = [];
172
+ for (let i = 0; i < unique.length; i += BATCH) {
173
+ const chunk = unique.slice(i, i + BATCH);
174
+ const answer = (await call(`${OSV}/querybatch`, {
175
+ queries: chunk.map((p) => ({ package: { name: p.name, ecosystem: p.ecosystem }, version: p.version })),
176
+ }));
177
+ if (!Array.isArray(answer.results) || answer.results.length !== chunk.length) {
178
+ throw new Error('OSV returned a batch answer of the wrong shape - nothing was checked');
179
+ }
180
+ answer.results.forEach((r, j) => {
181
+ idsPer.push((r.vulns ?? []).map((v) => v.id));
182
+ if (r.next_page_token)
183
+ incomplete.push(`${chunk[j].name} ${chunk[j].version} - more advisories exist than one page`);
184
+ });
185
+ }
186
+ // ponytail: one GET per distinct advisory, all at once; add a concurrency cap if OSV starts refusing.
187
+ const details = new Map();
188
+ await Promise.all([...new Set(idsPer.flat())].map(async (id) => details.set(id, (await call(`${OSV}/vulns/${encodeURIComponent(id)}`)))));
189
+ const hits = unique.flatMap((pkg, i) => {
190
+ const ids = idsPer[i];
191
+ return ids.length ? [{ pkg, advisories: mergeSame(ids.map((id) => toAdvisory(details.get(id), pkg))) }] : [];
192
+ });
193
+ return { hits, checked: unique.length, incomplete };
194
+ }
195
+ const RANK = { critical: 4, high: 3, medium: 2, low: 1 };
196
+ /**
197
+ * The whole check for `scan --deps`: find lockfiles, ask OSV, and return one
198
+ * signature-based finding per affected package, located on its lockfile line.
199
+ * `display` turns an absolute path into the one the report shows.
200
+ */
201
+ export async function checkDependencies(target, display, exclude = [], fetchImpl = fetch) {
202
+ const { read, unread } = findLockfiles(target, exclude);
203
+ const pinned = read.flatMap((r) => r.packages);
204
+ const answer = pinned.length ? await queryOsv(pinned, fetchImpl) : { hits: [], checked: 0, incomplete: [] };
205
+ const texts = new Map();
206
+ const linesOf = (file) => {
207
+ if (!texts.has(file))
208
+ texts.set(file, readFileSync(file, 'utf8').split(/\r?\n/));
209
+ return texts.get(file);
210
+ };
211
+ return {
212
+ findings: answer.hits.map((h) => dependencyFinding(h, linesOf(h.pkg.file), display(h.pkg.file))),
213
+ report: {
214
+ source: 'api.osv.dev',
215
+ checkedAt: new Date().toISOString(),
216
+ read: read.map((r) => ({ file: display(r.file), packages: r.packages.length, skipped: r.skipped })),
217
+ notRead: unread.map(display),
218
+ checked: answer.checked,
219
+ incomplete: answer.incomplete,
220
+ },
221
+ };
222
+ }
223
+ export function dependencyFinding(hit, lines, file) {
224
+ const { pkg, advisories } = hit;
225
+ // ponytail: first line naming the package (with its version, if one does); a lockfile has no AST to point at.
226
+ const named = (needle) => lines.findIndex((l) => l.includes(needle));
227
+ const at = [`node_modules/${pkg.name}"`, `"${pkg.name}"`, pkg.name]
228
+ .map((n) => {
229
+ const withVersion = lines.findIndex((l) => l.includes(n) && l.includes(pkg.version));
230
+ return withVersion >= 0 ? withVersion : named(n);
231
+ })
232
+ .find((i) => i >= 0) ?? 0;
233
+ const text = (lines[at] ?? '').trim();
234
+ const rated = advisories.filter((a) => a.severity !== 'unrated');
235
+ const top = rated.reduce((best, a) => (RANK[a.severity] > RANK[best] ? a.severity : best), 'low');
236
+ const severity = (rated.length ? top : 'medium');
237
+ const ids = advisories.map((a) => [a.id, ...a.aliases.filter((x) => x.startsWith('CVE-'))].join('/'));
238
+ const fixed = [...new Set(advisories.flatMap((a) => a.fixed))];
239
+ return signatureFinding({
240
+ ruleId: DEPENDENCY_RULE.id,
241
+ ruleName: DEPENDENCY_RULE.name,
242
+ cwe: 'CWE-1395',
243
+ owasp: DEPENDENCY_RULE.owasp,
244
+ severity,
245
+ message: `${pkg.name} ${pkg.version} has ${advisories.length} published advisor${advisories.length === 1 ? 'y' : 'ies'}` +
246
+ (fixed.length ? ` - fixed versions: ${fixed.join(', ')}` : ' - no fixed version published'),
247
+ location: {
248
+ file,
249
+ startLine: at + 1,
250
+ startColumn: 1,
251
+ endLine: at + 1,
252
+ endColumn: (lines[at]?.length ?? 0) + 1,
253
+ astNodeType: 'lockfile-entry',
254
+ astNodePath: `${pkg.ecosystem} lockfile`,
255
+ snippet: text.length > 160 ? `${text.slice(0, 159)}…` : text,
256
+ },
257
+ reasoning: `osv.dev lists ${ids.join(', ')} for ${pkg.ecosystem} ${pkg.name} ${pkg.version}: ` +
258
+ advisories.map((a) => `${a.summary} (${a.severity})`).join('; ') +
259
+ '.' +
260
+ (rated.length
261
+ ? ''
262
+ : ' None of these advisories carries a severity rating, so medium is a placeholder, not an assessment.'),
263
+ limitations: DEPENDENCY_RULE.limitations,
264
+ });
265
+ }
266
+ /**
267
+ * One flaw, one entry. OSV holds the same CVE once per database (GHSA-, GO-,
268
+ * PYSEC-...), so a project would otherwise see one bug listed two or three
269
+ * times. Entries naming each other, or sharing an alias, are folded into one;
270
+ * a rated entry wins over an unrated one, and every id is kept in `aliases`.
271
+ */
272
+ export function mergeSame(advisories) {
273
+ const groups = [];
274
+ for (const a of advisories) {
275
+ const names = new Set([a.id, ...a.aliases]);
276
+ const group = groups.find((g) => g.some((b) => b.id === a.id || names.has(b.id) || b.aliases.some((x) => names.has(x))));
277
+ if (group)
278
+ group.push(a);
279
+ else
280
+ groups.push([a]);
281
+ }
282
+ return groups.map((g) => {
283
+ const lead = g.find((a) => a.severity !== 'unrated') ?? g[0];
284
+ const ids = new Set(g.flatMap((a) => [a.id, ...a.aliases]));
285
+ ids.delete(lead.id);
286
+ return { ...lead, aliases: [...ids], fixed: [...new Set(g.flatMap((a) => a.fixed))] };
287
+ });
288
+ }
289
+ function toAdvisory(v, pkg) {
290
+ const fixed = (v.affected ?? [])
291
+ .filter((a) => a.package?.name === pkg.name && a.package?.ecosystem === pkg.ecosystem)
292
+ // GIT ranges name commits, not versions anyone can install.
293
+ .flatMap((a) => (a.ranges ?? []).filter((r) => r.type !== 'GIT'))
294
+ .flatMap((r) => (r.events ?? []).flatMap((e) => (e.fixed ? [e.fixed] : [])));
295
+ const rated = v.database_specific?.severity?.toLowerCase().replace('moderate', 'medium');
296
+ return {
297
+ id: v.id,
298
+ summary: v.summary ?? v.details?.split('\n')[0]?.slice(0, 160) ?? '(no summary published)',
299
+ aliases: v.aliases ?? [],
300
+ severity: rated && ['critical', 'high', 'medium', 'low'].includes(rated) ? rated : 'unrated',
301
+ fixed: [...new Set(fixed)],
302
+ };
303
+ }
304
+ //# sourceMappingURL=deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.js","sourceRoot":"","sources":["../../../src/platform/deps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * WHAT DEFUSE KNOWS ABOUT PHP RELEASES - AND WHEN IT LAST CHECKED.
3
+ * ================================================================
4
+ *
5
+ * Every other check in Defuse reads the code it is given and needs nothing
6
+ * else. This one compares a version number with facts about the world, and
7
+ * facts about the world go stale: a release that is current today is missing a
8
+ * fix next month. So the table carries the date it was checked, and every
9
+ * finding and report built on it prints that date. A stale table is then a
10
+ * visible fact rather than a quiet wrong answer.
11
+ *
12
+ * Every entry was read from php.net on the date below, not from news reports:
13
+ * https://www.php.net/supported-versions.php support dates, 8.2 - 8.5
14
+ * https://www.php.net/eol.php end-of-life dates
15
+ * https://www.php.net/archive/2026 the 21 Oct 2026 security releases
16
+ *
17
+ * TO UPDATE: change the facts, change CHECKED, run `npm test`. The tests pin
18
+ * the table's shape, not today's numbers, so an update is a data change only.
19
+ */
20
+ /** The day these facts were read from php.net. ISO date. */
21
+ export declare const PHP_FACTS_CHECKED = "2026-10-28";
22
+ /**
23
+ * Branches still receiving security fixes, and until when. A branch past its
24
+ * date on the day of the scan is treated as end of life - the scan date
25
+ * decides that, not the table date, so 8.2 turns out of support on 1 Jan 2027
26
+ * without anyone editing this file.
27
+ */
28
+ export declare const PHP_SECURITY_UNTIL: Readonly<Record<string, string>>;
29
+ /** Branches php.net lists as end of life, with the date support ended. */
30
+ export declare const PHP_END_OF_LIFE: Readonly<Record<string, string>>;
31
+ export interface PhpAdvisory {
32
+ /** The name it is known by, for a reader who has seen the news. */
33
+ readonly name: string;
34
+ readonly cves: readonly string[];
35
+ /** ISO date of the fixing releases. */
36
+ readonly published: string;
37
+ /** One plain sentence: what an attacker gets. */
38
+ readonly impact: string;
39
+ /** Per supported branch, the first release with the fix. */
40
+ readonly fixedIn: Readonly<Record<string, string>>;
41
+ /** The oldest branch reported affected. Older branches are end of life anyway. */
42
+ readonly oldestAffected: string;
43
+ }
44
+ /**
45
+ * Only advisories severe enough to change what a reader should do today. This
46
+ * is not a vulnerability database and does not pretend to be one - see the
47
+ * limitations text in runtime-version.ts. One entry is a deliberate start.
48
+ */
49
+ export declare const PHP_ADVISORIES: readonly PhpAdvisory[];
@@ -0,0 +1,67 @@
1
+ /**
2
+ * WHAT DEFUSE KNOWS ABOUT PHP RELEASES - AND WHEN IT LAST CHECKED.
3
+ * ================================================================
4
+ *
5
+ * Every other check in Defuse reads the code it is given and needs nothing
6
+ * else. This one compares a version number with facts about the world, and
7
+ * facts about the world go stale: a release that is current today is missing a
8
+ * fix next month. So the table carries the date it was checked, and every
9
+ * finding and report built on it prints that date. A stale table is then a
10
+ * visible fact rather than a quiet wrong answer.
11
+ *
12
+ * Every entry was read from php.net on the date below, not from news reports:
13
+ * https://www.php.net/supported-versions.php support dates, 8.2 - 8.5
14
+ * https://www.php.net/eol.php end-of-life dates
15
+ * https://www.php.net/archive/2026 the 21 Oct 2026 security releases
16
+ *
17
+ * TO UPDATE: change the facts, change CHECKED, run `npm test`. The tests pin
18
+ * the table's shape, not today's numbers, so an update is a data change only.
19
+ */
20
+ /** The day these facts were read from php.net. ISO date. */
21
+ export const PHP_FACTS_CHECKED = '2026-10-28';
22
+ /**
23
+ * Branches still receiving security fixes, and until when. A branch past its
24
+ * date on the day of the scan is treated as end of life - the scan date
25
+ * decides that, not the table date, so 8.2 turns out of support on 1 Jan 2027
26
+ * without anyone editing this file.
27
+ */
28
+ export const PHP_SECURITY_UNTIL = {
29
+ '8.2': '2026-12-31',
30
+ '8.3': '2027-12-31',
31
+ '8.4': '2028-12-31',
32
+ '8.5': '2029-12-31',
33
+ };
34
+ /** Branches php.net lists as end of life, with the date support ended. */
35
+ export const PHP_END_OF_LIFE = {
36
+ '8.1': '2025-12-31',
37
+ '8.0': '2023-11-26',
38
+ '7.4': '2022-11-28',
39
+ '7.3': '2021-12-06',
40
+ '7.2': '2020-11-30',
41
+ '7.1': '2019-12-01',
42
+ '7.0': '2019-01-10',
43
+ '5.6': '2018-12-31',
44
+ '5.5': '2016-07-21',
45
+ '5.4': '2015-09-03',
46
+ '5.3': '2014-08-14',
47
+ '5.2': '2011-01-06',
48
+ '5.1': '2006-08-24',
49
+ '5.0': '2005-09-05',
50
+ };
51
+ /**
52
+ * Only advisories severe enough to change what a reader should do today. This
53
+ * is not a vulnerability database and does not pretend to be one - see the
54
+ * limitations text in runtime-version.ts. One entry is a deliberate start.
55
+ */
56
+ export const PHP_ADVISORIES = [
57
+ {
58
+ name: 'PHP2Shell',
59
+ cves: ['CVE-2026-109618', 'CVE-2026-109619'],
60
+ published: '2026-10-21',
61
+ impact: 'two critical flaws in PHP itself that can let an attacker run code on the server ' +
62
+ 'without logging in, reported exploited in the wild',
63
+ fixedIn: { '8.2': '8.2.35', '8.3': '8.3.36', '8.4': '8.4.27', '8.5': '8.5.12' },
64
+ oldestAffected: '5.4',
65
+ },
66
+ ];
67
+ //# sourceMappingURL=php-facts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"php-facts.js","sourceRoot":"","sources":["../../../src/platform/php-facts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAqC;IAClE,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;CACpB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,eAAe,GAAqC;IAC/D,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;CACpB,CAAC;AAgBF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;QAC5C,SAAS,EAAE,YAAY;QACvB,MAAM,EACJ,mFAAmF;YACnF,oDAAoD;QACtD,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;QAC/E,cAAc,EAAE,KAAK;KACtB;CACF,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * THE RUNTIME VERSION CHECK: WHICH PHP DOES THIS PROJECT SAY IT RUNS ON?
3
+ * =====================================================================
4
+ *
5
+ * Every other check in Defuse reads the code a project contains. PHP2Shell
6
+ * (CVE-2026-109618 / CVE-2026-109619, October 2026) was a reminder that perfect
7
+ * code on PHP 8.2.20 is still a server an attacker can take over: the bug was
8
+ * in PHP itself. Reading code cannot find that. Reading the version the
9
+ * project declares can - Dockerfiles, compose files, composer.json and
10
+ * `.php-version` all say which PHP the project expects.
11
+ *
12
+ * WHAT IT CAN AND CANNOT CLAIM. A declaration is not the server. A Dockerfile
13
+ * that says 8.2.20 may have been replaced, and one that says `php:8.2-apache`
14
+ * gets whatever 8.2 release was current when the image was built. So every
15
+ * result here is signature-based - a match against a declaration, never a
16
+ * proof about what runs - and every message says which file it read and how
17
+ * old the facts behind it are (php-facts.ts).
18
+ *
19
+ * Platform-free like the rest of the analysis: it takes text and returns
20
+ * findings, and the walker in engine/ decides which files to hand it.
21
+ */
22
+ import { type SignatureFinding } from '../core/finding.js';
23
+ export declare const RUNTIME_VERSION_RULE: {
24
+ readonly id: "runtime-version";
25
+ readonly name: "PHP version with a known critical flaw or no security support";
26
+ readonly owasp: "A03:2025 Software Supply Chain Failures";
27
+ readonly summary: "The project runs on a PHP version with a known serious flaw, or one that no longer gets security fixes.";
28
+ readonly fix: "Upgrade PHP to a supported release and update the version declared in the Dockerfile or composer.json.";
29
+ readonly explanation: string;
30
+ readonly limitations: string;
31
+ };
32
+ /** One file the walker handed over. `path` is the path a reader should see. */
33
+ export interface RuntimeInputFile {
34
+ readonly path: string;
35
+ readonly text: string;
36
+ }
37
+ /** A version a project declares, with what the facts table says about it. */
38
+ export interface DeclaredRuntime {
39
+ readonly file: string;
40
+ readonly line: number;
41
+ /** Where it came from, in words: "Dockerfile FROM", "composer.json require". */
42
+ readonly source: string;
43
+ /** Exactly as written: `php:8.2-apache`, `^7.4 || ^8.0`, `8.2.20`. */
44
+ readonly declared: string;
45
+ /** What Defuse concluded, in one plain sentence. */
46
+ readonly status: string;
47
+ }
48
+ export interface RuntimeCheckResult {
49
+ readonly findings: readonly SignatureFinding[];
50
+ readonly declared: readonly DeclaredRuntime[];
51
+ /** Things a reader must know that are not findings - a gap is never silent. */
52
+ readonly notes: readonly string[];
53
+ /** The date the facts table was checked against php.net. */
54
+ readonly factsChecked: string;
55
+ }
56
+ export interface RuntimeCheckOptions {
57
+ /** ISO date of the scan. End of support is judged against this, not the table date. */
58
+ readonly today: string;
59
+ /** How many PHP source files the scan read - a project with none needs no PHP version. */
60
+ readonly phpFilesScanned: number;
61
+ }
62
+ type Version = readonly [number, number, number];
63
+ /** `2026-12-31` -> `31 Dec 2026`. */
64
+ export declare function readableDate(iso: string): string;
65
+ /** Which reader a file needs, by its name alone. Exported for the walker. */
66
+ export declare function runtimeFileKind(fileName: string): 'dockerfile' | 'compose' | 'composer' | 'php-version' | null;
67
+ type Comparison = {
68
+ readonly op: '>=' | '>' | '<' | '<=' | '=' | '!=';
69
+ readonly v: Version;
70
+ };
71
+ /**
72
+ * A composer version constraint, as alternatives of conditions - `^7.4 || ^8.0`
73
+ * is two alternatives. Null when any part cannot be read: an unreadable range
74
+ * is reported as unreadable, never guessed at.
75
+ */
76
+ export declare function parseComposerRange(constraint: string): Comparison[][] | null;
77
+ export declare function checkRuntimeVersions(files: readonly RuntimeInputFile[], options: RuntimeCheckOptions): RuntimeCheckResult;
78
+ export {};