@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,50 @@
1
+ /**
2
+ * Terminal colour, in about forty lines and zero dependencies.
3
+ *
4
+ * ANSI escape codes are just special character sequences: printing "\x1b[31m"
5
+ * tells the terminal "red from here", and "\x1b[0m" means "back to normal".
6
+ * Every colour library is a wrapper around that idea.
7
+ *
8
+ * We turn colour OFF when output is being piped into a file or another program
9
+ * (`process.stdout.isTTY` is false), and we honour the NO_COLOR convention,
10
+ * because escape codes in a log file are unreadable garbage.
11
+ */
12
+ export declare const color: {
13
+ enabled: boolean;
14
+ bold: (text: string) => string;
15
+ dim: (text: string) => string;
16
+ italic: (text: string) => string;
17
+ underline: (text: string) => string;
18
+ red: (text: string) => string;
19
+ green: (text: string) => string;
20
+ yellow: (text: string) => string;
21
+ blue: (text: string) => string;
22
+ magenta: (text: string) => string;
23
+ cyan: (text: string) => string;
24
+ white: (text: string) => string;
25
+ gray: (text: string) => string;
26
+ bgRed: (text: string) => string;
27
+ bgYellow: (text: string) => string;
28
+ };
29
+ /** Word-wrap a paragraph to `width`, indenting every line after the first. */
30
+ export declare function wrapText(text: string, width: number, indent: string): string;
31
+ export declare function setAsciiMode(enabled: boolean): void;
32
+ declare const GLYPHS: {
33
+ readonly rule: readonly ["─", "-"];
34
+ readonly vertical: readonly ["│", "|"];
35
+ readonly cornerTop: readonly ["┌", "+"];
36
+ readonly cornerBottom: readonly ["└", "+"];
37
+ readonly tick: readonly ["✓", "ok"];
38
+ readonly cross: readonly ["✗", "X"];
39
+ readonly dotFull: readonly ["●", "*"];
40
+ readonly dotHalf: readonly ["◐", "~"];
41
+ readonly dotEmpty: readonly ["○", "o"];
42
+ readonly middot: readonly ["·", "-"];
43
+ readonly arrow: readonly ["→", "->"];
44
+ readonly ellipsis: readonly ["…", "..."];
45
+ readonly warn: readonly ["⚠", "!"];
46
+ };
47
+ export type GlyphName = keyof typeof GLYPHS;
48
+ /** The right spelling of a glyph for wherever this output is going. */
49
+ export declare function g(name: GlyphName): string;
50
+ export {};
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Terminal colour, in about forty lines and zero dependencies.
3
+ *
4
+ * ANSI escape codes are just special character sequences: printing "\x1b[31m"
5
+ * tells the terminal "red from here", and "\x1b[0m" means "back to normal".
6
+ * Every colour library is a wrapper around that idea.
7
+ *
8
+ * We turn colour OFF when output is being piped into a file or another program
9
+ * (`process.stdout.isTTY` is false), and we honour the NO_COLOR convention,
10
+ * because escape codes in a log file are unreadable garbage.
11
+ */
12
+ const enabled = process.env['NO_COLOR'] === undefined &&
13
+ process.env['TERM'] !== 'dumb' &&
14
+ process.stdout.isTTY === true;
15
+ function wrap(open, close) {
16
+ return (text) => (enabled ? `\x1b[${open}m${text}\x1b[${close}m` : text);
17
+ }
18
+ export const color = {
19
+ enabled,
20
+ bold: wrap(1, 22),
21
+ dim: wrap(2, 22),
22
+ italic: wrap(3, 23),
23
+ underline: wrap(4, 24),
24
+ red: wrap(31, 39),
25
+ green: wrap(32, 39),
26
+ yellow: wrap(33, 39),
27
+ blue: wrap(34, 39),
28
+ magenta: wrap(35, 39),
29
+ cyan: wrap(36, 39),
30
+ white: wrap(37, 39),
31
+ gray: wrap(90, 39),
32
+ bgRed: wrap(41, 49),
33
+ bgYellow: wrap(43, 49),
34
+ };
35
+ /** Word-wrap a paragraph to `width`, indenting every line after the first. */
36
+ export function wrapText(text, width, indent) {
37
+ const words = text.split(/\s+/).filter(Boolean);
38
+ const lines = [];
39
+ let current = '';
40
+ for (const word of words) {
41
+ if (current.length === 0) {
42
+ current = word;
43
+ }
44
+ else if (current.length + 1 + word.length <= width) {
45
+ current += ` ${word}`;
46
+ }
47
+ else {
48
+ lines.push(current);
49
+ current = word;
50
+ }
51
+ }
52
+ if (current)
53
+ lines.push(current);
54
+ return lines.join(`\n${indent}`);
55
+ }
56
+ /**
57
+ * BOX-DRAWING GLYPHS, AND WHY THEY NEED A FALLBACK
58
+ *
59
+ * The report uses characters like ─ ✓ ● because they read well in a terminal.
60
+ * They are also a portability hazard: they are multi-byte UTF-8, and the moment
61
+ * output leaves a UTF-8 terminal the bytes can be re-interpreted by something
62
+ * that assumes a legacy code page. Redirecting a scan to a file on Windows and
63
+ * opening it in Notepad turns every `──` into `ÔöÇÔöÇ` - the report becomes
64
+ * unreadable while the analysis behind it was perfectly correct.
65
+ *
66
+ * So: one table, two spellings, and a switch. ASCII is chosen automatically
67
+ * when output is being redirected on Windows (the case that actually breaks),
68
+ * and can be forced anywhere with --ascii or NS1_ASCII=1.
69
+ */
70
+ let asciiMode = process.env['NS1_ASCII'] === '1' ||
71
+ (process.platform === 'win32' && process.stdout.isTTY !== true);
72
+ export function setAsciiMode(enabled) {
73
+ asciiMode = enabled;
74
+ }
75
+ const GLYPHS = {
76
+ rule: ['\u2500', '-'],
77
+ vertical: ['\u2502', '|'],
78
+ cornerTop: ['\u250c', '+'],
79
+ cornerBottom: ['\u2514', '+'],
80
+ tick: ['\u2713', 'ok'],
81
+ cross: ['\u2717', 'X'],
82
+ dotFull: ['\u25cf', '*'],
83
+ dotHalf: ['\u25d0', '~'],
84
+ dotEmpty: ['\u25cb', 'o'],
85
+ middot: ['\u00b7', '-'],
86
+ arrow: ['\u2192', '->'],
87
+ ellipsis: ['\u2026', '...'],
88
+ // Windows PowerShell renders U+26A0 inconsistently; the ASCII fallback keeps
89
+ // the warning legible everywhere, which for a warning is the whole point.
90
+ warn: ['\u26a0', '!'],
91
+ };
92
+ /** The right spelling of a glyph for wherever this output is going. */
93
+ export function g(name) {
94
+ const pair = GLYPHS[name];
95
+ return asciiMode ? pair[1] : pair[0];
96
+ }
97
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../../../src/report/colors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,SAAS;IACrC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,MAAM;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC;AAEhC,SAAS,IAAI,CAAC,IAAY,EAAE,KAAa;IACvC,OAAO,CAAC,IAAY,EAAU,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,IAAI,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC3F,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,OAAO;IACP,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;IACjB,GAAG,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;IAChB,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;IACtB,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IACjB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IACnB,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IAClB,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IAClB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IAClB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IACnB,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;CACvB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,KAAa,EAAE,MAAc;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,EAAE,CAAC;IAEjB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;aAAM,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;YACrD,OAAO,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;IACD,IAAI,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;AACnC,CAAC;AAGD;;;;;;;;;;;;;GAaG;AACH,IAAI,SAAS,GACX,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,GAAG;IAChC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;AAElE,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,SAAS,GAAG,OAAO,CAAC;AACtB,CAAC;AAED,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IACrB,QAAQ,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IACzB,SAAS,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IAC1B,YAAY,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IAC7B,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC;IACtB,KAAK,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IACtB,OAAO,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IACxB,OAAO,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IACxB,QAAQ,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IACzB,MAAM,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;IACvB,KAAK,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC;IACvB,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;IAC3B,6EAA6E;IAC7E,0EAA0E;IAC1E,IAAI,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC;CACb,CAAC;AAIX,uEAAuE;AACvE,MAAM,UAAU,CAAC,CAAC,IAAe;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,90 @@
1
+ /** Opening and closing markers for a generated block in a markdown file. */
2
+ export declare const BLOCK_START: (name: string) => string;
3
+ export declare const BLOCK_END = "<!-- /derived -->";
4
+ /**
5
+ * The rule × language coverage matrix.
6
+ *
7
+ * `Rule.support` is already a per-language honesty declaration with a REQUIRED
8
+ * note on every entry, printed at the end of every scan. The README's table was
9
+ * a second copy of it, kept by hand, and it is the copy that went stale.
10
+ */
11
+ export declare function renderRuleMatrix(): string;
12
+ /** One sentence naming the current totals, for use in running prose. */
13
+ export declare function renderCounts(): string;
14
+ export interface BenchmarkResult {
15
+ readonly scored: number;
16
+ readonly tp: number;
17
+ readonly fp: number;
18
+ readonly fn: number;
19
+ readonly tn: number;
20
+ readonly precision: string;
21
+ readonly recall: string;
22
+ readonly missedByCategory: Record<string, number>;
23
+ readonly filesParsed: number;
24
+ readonly parseErrors: number;
25
+ readonly durationMs: number;
26
+ /** False positives carrying BenchmarkJava's constant-branch decoy. */
27
+ readonly decoyFalsePositives: number;
28
+ readonly decoyShare: string;
29
+ readonly engineVersion: string;
30
+ readonly measuredAt: string;
31
+ }
32
+ /**
33
+ * The OWASP BenchmarkJava table.
34
+ *
35
+ * This one cannot be derived from the registry, because it is a MEASUREMENT
36
+ * rather than a declaration - so it is derived from the measurement instead.
37
+ * `npm run benchmark` writes docs/benchmark-result.json, this renders it, and
38
+ * the test compares the README against it. The loop closes: you cannot change
39
+ * the engine's score without the README either following or failing the build.
40
+ *
41
+ * The stale figures this replaced were 64.5% precision / 13.8% recall. They
42
+ * were honestly measured and then left behind by the Spring and JAX-RS source
43
+ * bindings and by declaredTypes(), which took recall to 76.4% and cost
44
+ * precision on the way - a real trade the old table could not show.
45
+ */
46
+ export declare function renderBenchmark(result: BenchmarkResult): string;
47
+ /** Replace the contents of one `<!-- derived:name -->` block. */
48
+ export interface LabelSplitTier {
49
+ readonly label: string;
50
+ readonly tp: number;
51
+ readonly fp: number;
52
+ readonly fn: number;
53
+ readonly precision: string;
54
+ readonly recall: string;
55
+ readonly decoyFalsePositives: number;
56
+ readonly decoyShare: string;
57
+ readonly nonDecoyFalsePositives: number;
58
+ }
59
+ export interface LabelSplitResult {
60
+ readonly scored: number;
61
+ readonly findingsByConfidence: Record<string, number>;
62
+ readonly tiers: readonly LabelSplitTier[];
63
+ readonly engineVersion: string;
64
+ readonly measuredAt: string;
65
+ }
66
+ /**
67
+ * THE BLOCK THAT SCORES THE LABELS SEPARATELY.
68
+ *
69
+ * `renderBenchmark` reports one precision figure covering both labels mixed
70
+ * together. That figure is an average across two populations this whole tool
71
+ * exists to keep apart - the same blending it refuses to do inside a single
72
+ * finding, done one level up in the report about itself.
73
+ *
74
+ * So this renders the split, and like the benchmark block it derives its own
75
+ * interpretation rather than leaving a hand-written sentence to go stale
76
+ * underneath it. Two sentences are computed:
77
+ *
78
+ * - whether the green label is actually worth more than the amber one, and
79
+ * by how much. If a change ever made them equal, this block would say so
80
+ * in the README rather than waiting for someone to notice.
81
+ *
82
+ * - how much of the green tier's error is BenchmarkJava's synthetic decoy
83
+ * rather than a real mistake. Quoting the decoy-excluded figure alone
84
+ * would be picking the flattering number; quoting only the blended one
85
+ * hides that the benchmark's traps dominate what is left.
86
+ */
87
+ export declare function renderLabelSplit(result: LabelSplitResult): string;
88
+ export declare function replaceBlock(markdown: string, name: string, body: string): string;
89
+ /** Read the current contents of one block, for comparison. */
90
+ export declare function readBlock(markdown: string, name: string): string | null;
@@ -0,0 +1,291 @@
1
+ /**
2
+ * THE README, DERIVED FROM THE REGISTRY.
3
+ * =====================================
4
+ *
5
+ * WHY THIS FILE EXISTS.
6
+ *
7
+ * Pointing a fresh reader at this repository produced a list of things the
8
+ * README asserts that the code contradicts:
9
+ *
10
+ * README says 5 rules registry ships 8
11
+ * README says 5 languages languages.ts 6 (PHP absent entirely)
12
+ * README says "Not SARIF" src/report/sarif.ts is a SARIF 2.1.0 exporter
13
+ * README says 13.8% recall the scorer measures 76.4%
14
+ *
15
+ * Every one of those was true when it was written. The project simply moved and
16
+ * the prose did not, because the prose was a hand-maintained restatement of
17
+ * facts the registry already knew.
18
+ *
19
+ * THIS IS THE FOURTH TIME THIS EXACT CLASS HAS BITTEN, and the previous three
20
+ * are recorded elsewhere in this codebase: sources built from the frameworks we
21
+ * had been shown (elasticsearch), escapers likewise (Jenkins), test-path
22
+ * conventions likewise (cal.com). The pattern is always a list maintained by
23
+ * hand that looks complete until reality moves.
24
+ *
25
+ * What makes this instance the sharpest is where the stale list was. There IS a
26
+ * documentation-drift check in the suite, and it passed the whole time - because
27
+ * it greps the README for exactly one regex, the `npm test - N checks` line.
28
+ * A narrow instrument was trusted as a broad one. The checker itself was the
29
+ * hand-maintained list.
30
+ *
31
+ * So: the counts and the matrix are now GENERATED from ALL_RULES and LANGUAGES,
32
+ * the benchmark table is generated from the scorer's own output file, and
33
+ * `npm test` fails when the README disagrees with either. Prose that restates a
34
+ * fact the program knows is a bug waiting for a quiet afternoon.
35
+ */
36
+ import { ALL_RULES } from '../rules/registry.js';
37
+ import { LANGUAGES } from '../parse/languages.js';
38
+ /** Opening and closing markers for a generated block in a markdown file. */
39
+ export const BLOCK_START = (name) => `<!-- derived:${name} -->`;
40
+ export const BLOCK_END = '<!-- /derived -->';
41
+ const MARK = {
42
+ implemented: '●',
43
+ partial: '◐',
44
+ 'not-implemented': '○',
45
+ };
46
+ const SHORT = {
47
+ javascript: 'JS',
48
+ typescript: 'TS',
49
+ python: 'PY',
50
+ java: 'JAVA',
51
+ php: 'PHP',
52
+ go: 'GO',
53
+ };
54
+ const columnLabel = (id) => SHORT[id] ?? id.toUpperCase();
55
+ /**
56
+ * The rule × language coverage matrix.
57
+ *
58
+ * `Rule.support` is already a per-language honesty declaration with a REQUIRED
59
+ * note on every entry, printed at the end of every scan. The README's table was
60
+ * a second copy of it, kept by hand, and it is the copy that went stale.
61
+ */
62
+ export function renderRuleMatrix() {
63
+ const languages = LANGUAGES.map((l) => l.id);
64
+ const header = `| Rule | CWE | OWASP | ${languages.map(columnLabel).join(' | ')} |`;
65
+ const divider = `|---|---|---|${languages.map(() => ':--:').join('|')}|`;
66
+ const rows = ALL_RULES.map((rule) => {
67
+ const cells = languages.map((id) => MARK[rule.support[id]?.status ?? 'not-implemented']);
68
+ return `| \`${rule.id}\` | ${rule.cwe} | ${rule.owasp} | ${cells.join(' | ')} |`;
69
+ });
70
+ return [
71
+ header,
72
+ divider,
73
+ ...rows,
74
+ '',
75
+ '● implemented ◐ partial ○ not implemented',
76
+ '',
77
+ `${ALL_RULES.length} rules across ${languages.length} languages. This table is generated from`,
78
+ '`ALL_RULES` and `LANGUAGES` by `npm run sync:readme`, and `npm test` fails if it',
79
+ 'drifts - the counts in it are not maintained by hand.',
80
+ ].join('\n');
81
+ }
82
+ /** One sentence naming the current totals, for use in running prose. */
83
+ export function renderCounts() {
84
+ const names = LANGUAGES.map((l) => l.displayName);
85
+ return (`**${ALL_RULES.length} rules** across **${names.length} languages** ` +
86
+ `(${names.join(', ')}).`);
87
+ }
88
+ /**
89
+ * The OWASP BenchmarkJava table.
90
+ *
91
+ * This one cannot be derived from the registry, because it is a MEASUREMENT
92
+ * rather than a declaration - so it is derived from the measurement instead.
93
+ * `npm run benchmark` writes docs/benchmark-result.json, this renders it, and
94
+ * the test compares the README against it. The loop closes: you cannot change
95
+ * the engine's score without the README either following or failing the build.
96
+ *
97
+ * The stale figures this replaced were 64.5% precision / 13.8% recall. They
98
+ * were honestly measured and then left behind by the Spring and JAX-RS source
99
+ * bindings and by declaredTypes(), which took recall to 76.4% and cost
100
+ * precision on the way - a real trade the old table could not show.
101
+ */
102
+ export function renderBenchmark(result) {
103
+ /*
104
+ * THE HEADLINE IS DERIVED TOO, and that is the point of this function.
105
+ *
106
+ * The first version of this block generated the TABLE and left the sentence
107
+ * underneath it hand-written. That sentence read "Recall is low and that is
108
+ * the honest headline" - an interpretation of the OLD numbers, which went
109
+ * stale in the very commit that fixed the table it sat beneath. Deriving the
110
+ * facts and hand-keeping their meaning just moves the drift one line down.
111
+ *
112
+ * So the weak side is computed. More false positives than false negatives
113
+ * means precision is where the tool is worst; the reverse means recall is.
114
+ * Whichever it is, the README says it because the arithmetic said it.
115
+ */
116
+ const precisionIsWeaker = result.fp > result.fn;
117
+ const ratio = (Math.max(result.fp, result.fn) / Math.max(1, Math.min(result.fp, result.fn)))
118
+ .toFixed(1);
119
+ /*
120
+ * A ZERO IS NOT A RATIO. When the hunt for BenchmarkJava's 38 misses took
121
+ * false negatives to 0, this sentence printed "271 false positives against 0
122
+ * false negatives - 271.0x as many": arithmetic on a divide-by-one guard,
123
+ * dressed up as a measurement. A zero is said in words, and so is its limit:
124
+ * no misses HERE is a fact about these 1,210 test cases, not about your code.
125
+ */
126
+ const scope = 'That is a statement about these scored test cases, not about code in general - ' +
127
+ 'the limitations list is where the misses this benchmark cannot show are written down.';
128
+ const headline = result.fn === 0 && result.fp > 0
129
+ ? `**Precision is the weak side.** ${result.fp} false positives and no false ` +
130
+ `negatives, so on this benchmark the cost of this engine is triage time, not ` +
131
+ `missed bugs. ${scope}`
132
+ : result.fp === 0 && result.fn > 0
133
+ ? `**Recall is the weak side.** ${result.fn} false negatives and no false ` +
134
+ `positives, so on this benchmark the cost of this engine is missed bugs, not ` +
135
+ `triage time. ${scope}`
136
+ : precisionIsWeaker
137
+ ? `**Precision is the weaker side.** ${result.fp} false positives against ` +
138
+ `${result.fn} false negatives - ${ratio}x as many - so the cost of this engine ` +
139
+ `is triage time, not missed bugs.`
140
+ : `**Recall is the weaker side.** ${result.fn} false negatives against ` +
141
+ `${result.fp} false positives - ${ratio}x as many - so the cost of this engine ` +
142
+ `is missed bugs, not triage time.`;
143
+ const missed = Object.entries(result.missedByCategory);
144
+ return [
145
+ '| | cases | TP | FP | FN | TN | precision | recall |',
146
+ '|---|--:|--:|--:|--:|--:|--:|--:|',
147
+ `| **overall** | **${result.scored}** | **${result.tp}** | **${result.fp}** | ` +
148
+ `**${result.fn}** | **${result.tn}** | **${result.precision}** | **${result.recall}** |`,
149
+ '',
150
+ `${result.filesParsed} files, ${result.parseErrors} parse errors, ` +
151
+ `${(result.durationMs / 1000).toFixed(1)} seconds, on engine ${result.engineVersion} ` +
152
+ `(${result.measuredAt}).`,
153
+ '',
154
+ headline,
155
+ '',
156
+ (missed.length > 0
157
+ ? `Missed by category: ${missed.map(([k, v]) => `${k} ${v}`).join(', ')}. `
158
+ : 'Nothing was missed in the scored categories. ') +
159
+ `Of the ${result.fp} false positives, **${result.decoyFalsePositives} ` +
160
+ `(${result.decoyShare})** carry the shape of BenchmarkJava's constant-branch decoy - ` +
161
+ `a condition like \`if ((7 * 42) - num > 200)\`. The data-flow pass, the SQL rule ` +
162
+ `and the command-injection rule all decide such a condition, so the shape alone ` +
163
+ `explains none of them: every false positive here is ours.`,
164
+ '',
165
+ 'Every number above is generated by `npm run benchmark` into',
166
+ '`docs/benchmark-result.json` - including the sentence naming the weaker side,',
167
+ 'because an interpretation kept by hand goes stale exactly like a figure does.',
168
+ ].join('\n');
169
+ }
170
+ /**
171
+ * THE BLOCK THAT SCORES THE LABELS SEPARATELY.
172
+ *
173
+ * `renderBenchmark` reports one precision figure covering both labels mixed
174
+ * together. That figure is an average across two populations this whole tool
175
+ * exists to keep apart - the same blending it refuses to do inside a single
176
+ * finding, done one level up in the report about itself.
177
+ *
178
+ * So this renders the split, and like the benchmark block it derives its own
179
+ * interpretation rather than leaving a hand-written sentence to go stale
180
+ * underneath it. Two sentences are computed:
181
+ *
182
+ * - whether the green label is actually worth more than the amber one, and
183
+ * by how much. If a change ever made them equal, this block would say so
184
+ * in the README rather than waiting for someone to notice.
185
+ *
186
+ * - how much of the green tier's error is BenchmarkJava's synthetic decoy
187
+ * rather than a real mistake. Quoting the decoy-excluded figure alone
188
+ * would be picking the flattering number; quoting only the blended one
189
+ * hides that the benchmark's traps dominate what is left.
190
+ */
191
+ export function renderLabelSplit(result) {
192
+ const pct = (s) => Number.parseFloat(s);
193
+ const verified = result.tiers.find((t) => t.label.startsWith('flow-verified'));
194
+ const signature = result.tiers.find((t) => t.label.startsWith('signature-only'));
195
+ const rows = result.tiers.map((t) => `| \`${t.label}\` | ${t.tp} | ${t.fp} | ${t.fn} | **${t.precision}** | ${t.recall} | ` +
196
+ `${t.decoyFalsePositives} (${t.decoyShare}) |`);
197
+ const lines = [
198
+ '| tier | TP | FP | FN | precision | recall | FPs that are decoys |',
199
+ '|---|--:|--:|--:|--:|--:|--:|',
200
+ ...rows,
201
+ '',
202
+ `${Object.entries(result.findingsByConfidence)
203
+ .map(([k, v]) => `${v} \`${k}\``)
204
+ .join(', ')} findings, on engine ${result.engineVersion} (${result.measuredAt}).`,
205
+ '',
206
+ ];
207
+ if (verified && signature) {
208
+ const gap = pct(verified.precision) - pct(signature.precision);
209
+ lines.push(gap > 0
210
+ ? `**The green label is worth ${gap.toFixed(1)} points.** \`flow-verified\` runs ` +
211
+ `${verified.precision} against \`signature-based\` at ${signature.precision}. The ` +
212
+ `gap is the whole claim this tool makes; it is measured here rather than asserted.`
213
+ : `**The labels are not separating.** \`flow-verified\` runs ${verified.precision} ` +
214
+ `against \`signature-based\` at ${signature.precision}, so the green label is ` +
215
+ `currently buying nothing. That is a defect, not a footnote.`);
216
+ lines.push('');
217
+ const real = verified.tp + verified.nonDecoyFalsePositives;
218
+ const excl = real === 0 ? 0 : (verified.tp / real) * 100;
219
+ /*
220
+ * TWO SENTENCES, AND WHICH ONE IS TRUE IS NOT OURS TO CHOOSE.
221
+ *
222
+ * For a long time the green label carried a large pile of the benchmark's
223
+ * constant-branch decoys, so the honest thing was to print the headline AND
224
+ * the decoy-excluded figure, and to say plainly that neither alone was the
225
+ * truth.
226
+ *
227
+ * Then constant-condition evaluation landed and the decoys went to zero.
228
+ * Keeping the old paragraph would have had the page arguing for a
229
+ * distinction between two numbers that are now identical - drift caused by
230
+ * an improvement, which is the sneakiest kind, because nothing failed and
231
+ * the prose simply stopped being about anything.
232
+ *
233
+ * So the branch is derived from the measurement, like every other sentence
234
+ * in this file. If decoys ever come back, so does the caveat.
235
+ */
236
+ if (verified.fp === 0) {
237
+ /*
238
+ * Zero is said in words, and so is its limit - the same rule the benchmark
239
+ * block follows for zero false negatives. "0 ordinary mistakes out of 456"
240
+ * would be arithmetic dressed as a boast.
241
+ */
242
+ lines.push(`**No false positive carries the green label here.** All ${verified.tp} ` +
243
+ `\`flow-verified\` claims on this benchmark are real vulnerabilities. That is a ` +
244
+ `statement about these scored test cases, not a promise about your code: a proof ` +
245
+ `means the path exists in the source, and the ways that can still be wrong are ` +
246
+ `the ones in the limitations list, which a synthetic benchmark does not exercise.`);
247
+ }
248
+ else if (verified.decoyFalsePositives === 0) {
249
+ lines.push(`**There is no footnote any more.** Not one of the ${verified.fp} false positives ` +
250
+ `still carrying the green label is a constant-branch decoy. Until constant ` +
251
+ `conditions were evaluated, that pile was most of them, and this paragraph had to ` +
252
+ `print two precision figures and explain why neither alone was the truth. ` +
253
+ `\`flow-verified\` now reads **${excl.toFixed(1)}%** with nothing set aside and ` +
254
+ `nothing excluded - ${verified.nonDecoyFalsePositives} ordinary mistakes out of ` +
255
+ `${verified.tp + verified.fp} claims.`);
256
+ }
257
+ else {
258
+ lines.push(`Of the ${verified.fp} false positives still carrying the green label, ` +
259
+ `**${verified.decoyFalsePositives} (${verified.decoyShare})** are BenchmarkJava's ` +
260
+ `constant-branch decoy - a path that genuinely exists inside a branch that cannot ` +
261
+ `run - leaving **${verified.nonDecoyFalsePositives}** that are ordinary mistakes. ` +
262
+ `Set the decoys aside and \`flow-verified\` precision is **${excl.toFixed(1)}%**. ` +
263
+ `Both figures are printed because neither alone is the truth: the first is ` +
264
+ `contaminated by synthetic traps, the second requires excluding cases, and a ` +
265
+ `reader deserves to see the size of that choice rather than inherit it.`);
266
+ }
267
+ }
268
+ return lines.join('\n');
269
+ }
270
+ export function replaceBlock(markdown, name, body) {
271
+ const start = BLOCK_START(name);
272
+ const from = markdown.indexOf(start);
273
+ if (from === -1)
274
+ throw new Error(`README has no block named "${name}"`);
275
+ const to = markdown.indexOf(BLOCK_END, from);
276
+ if (to === -1)
277
+ throw new Error(`block "${name}" is not closed with ${BLOCK_END}`);
278
+ return (markdown.slice(0, from + start.length) + '\n' + body + '\n' + markdown.slice(to));
279
+ }
280
+ /** Read the current contents of one block, for comparison. */
281
+ export function readBlock(markdown, name) {
282
+ const start = BLOCK_START(name);
283
+ const from = markdown.indexOf(start);
284
+ if (from === -1)
285
+ return null;
286
+ const to = markdown.indexOf(BLOCK_END, from);
287
+ if (to === -1)
288
+ return null;
289
+ return markdown.slice(from + start.length, to).trim();
290
+ }
291
+ //# sourceMappingURL=coverage-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage-table.js","sourceRoot":"","sources":["../../../src/report/coverage-table.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * THE CLIENT REPORT
3
+ * =================
4
+ *
5
+ * The terminal report is for the person who ran the scan. This one is for the
6
+ * person who did not: a project owner who will read it once, possibly printed,
7
+ * and decide what to do. It is one self-contained HTML file - no script, no
8
+ * stylesheet or font fetched from anywhere, the mark drawn inline - so it can
9
+ * be emailed, opened offline and printed to PDF from any browser.
10
+ *
11
+ * It says the same things the terminal does, in the same order of trust:
12
+ *
13
+ * 1. what was PROVEN, each with the path that proves it;
14
+ * 2. what was only MATCHED, said plainly to be unproven;
15
+ * 3. what was checked and CLEARED, with the reason;
16
+ * 4. what was NOT CHECKED at all.
17
+ *
18
+ * The fourth section is not an appendix to skip. A report that lists problems
19
+ * and stays silent about what it never looked at invites the reader to treat
20
+ * "not listed" as "fine" - the exact failure this tool exists to avoid - and a
21
+ * client report is where that misreading costs the most.
22
+ *
23
+ * Two labels, never blended: a proven finding is a filled dot, a guess a
24
+ * hollow ring, so the difference survives a black-and-white printer. The
25
+ * mark's purple is used on the mark and the name only.
26
+ *
27
+ * PLATFORM-FREE ON PURPOSE: string in, string out, no Node imports, so the
28
+ * browser UI can produce the same file from the same engine.
29
+ *
30
+ * EVERY PIECE OF TEXT FROM THE SCANNED CODE IS ESCAPED. A snippet is whatever
31
+ * the scanned project contains, and a report that rendered it as markup would
32
+ * be an XSS vector aimed at the person reading a security report. The test
33
+ * suite feeds it a hostile snippet to make sure.
34
+ */
35
+ import type { ScanResult } from '../engine/scan.js';
36
+ export interface HtmlReportOptions {
37
+ /** Shown as the project name. Defaults to the last segment of the target path. */
38
+ readonly projectName?: string;
39
+ /** Optional: the person or firm who ran the scan and reviewed the report. */
40
+ readonly preparedBy?: string;
41
+ /**
42
+ * Optional: one line printed under the header - that this is a sample, a
43
+ * draft, or which branch was scanned. Escaped like every other string here.
44
+ */
45
+ readonly note?: string;
46
+ /** When the scan ran. Passed in so the output is reproducible in tests. */
47
+ readonly generatedAt: Date;
48
+ /**
49
+ * The scanned folder as the finding paths spell it. The CLI writes paths
50
+ * relative to where it was run (`corpus/app/views.py`) while the target is
51
+ * absolute, so both prefixes are stripped to show `views.py`'s place in the
52
+ * project rather than on the scanning machine.
53
+ */
54
+ readonly root?: string;
55
+ /**
56
+ * Anything the shell that gathered the files left out on its own terms -
57
+ * the browser reads at most 4,000 files and none over 2 MB. Printed in
58
+ * section 4, because a file that never reached the engine is a gap the
59
+ * engine cannot report by itself.
60
+ */
61
+ readonly intake?: string;
62
+ }
63
+ /** Escape for HTML text and attribute values. Everything from the scan goes through this. */
64
+ export declare function escapeHtml(text: string): string;
65
+ export declare function maskSecretLiterals(snippet: string): string;
66
+ export declare function renderHtmlReport(result: ScanResult, options: HtmlReportOptions): string;