insa-check-core 0.1.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 (258) hide show
  1. package/LICENSE +24 -0
  2. package/dist/annex-b.d.ts +6 -0
  3. package/dist/annex-b.d.ts.map +1 -0
  4. package/dist/annex-b.js +152 -0
  5. package/dist/annex-b.js.map +1 -0
  6. package/dist/checks/content/page.d.ts +7 -0
  7. package/dist/checks/content/page.d.ts.map +1 -0
  8. package/dist/checks/content/page.js +133 -0
  9. package/dist/checks/content/page.js.map +1 -0
  10. package/dist/checks/cookies/cookie-flags.d.ts +3 -0
  11. package/dist/checks/cookies/cookie-flags.d.ts.map +1 -0
  12. package/dist/checks/cookies/cookie-flags.js +41 -0
  13. package/dist/checks/cookies/cookie-flags.js.map +1 -0
  14. package/dist/checks/cookies/cookie-scope.d.ts +6 -0
  15. package/dist/checks/cookies/cookie-scope.d.ts.map +1 -0
  16. package/dist/checks/cookies/cookie-scope.js +60 -0
  17. package/dist/checks/cookies/cookie-scope.js.map +1 -0
  18. package/dist/checks/deployment/server-banner.d.ts +4 -0
  19. package/dist/checks/deployment/server-banner.d.ts.map +1 -0
  20. package/dist/checks/deployment/server-banner.js +60 -0
  21. package/dist/checks/deployment/server-banner.js.map +1 -0
  22. package/dist/checks/deployment/trace.d.ts +3 -0
  23. package/dist/checks/deployment/trace.d.ts.map +1 -0
  24. package/dist/checks/deployment/trace.js +30 -0
  25. package/dist/checks/deployment/trace.js.map +1 -0
  26. package/dist/checks/headers/clickjacking.d.ts +5 -0
  27. package/dist/checks/headers/clickjacking.d.ts.map +1 -0
  28. package/dist/checks/headers/clickjacking.js +57 -0
  29. package/dist/checks/headers/clickjacking.js.map +1 -0
  30. package/dist/checks/headers/content-type-options.d.ts +3 -0
  31. package/dist/checks/headers/content-type-options.d.ts.map +1 -0
  32. package/dist/checks/headers/content-type-options.js +17 -0
  33. package/dist/checks/headers/content-type-options.js.map +1 -0
  34. package/dist/checks/headers/cors.d.ts +4 -0
  35. package/dist/checks/headers/cors.d.ts.map +1 -0
  36. package/dist/checks/headers/cors.js +38 -0
  37. package/dist/checks/headers/cors.js.map +1 -0
  38. package/dist/checks/html.d.ts +11 -0
  39. package/dist/checks/html.d.ts.map +1 -0
  40. package/dist/checks/html.js +30 -0
  41. package/dist/checks/html.js.map +1 -0
  42. package/dist/checks/index.d.ts +7 -0
  43. package/dist/checks/index.d.ts.map +1 -0
  44. package/dist/checks/index.js +48 -0
  45. package/dist/checks/index.js.map +1 -0
  46. package/dist/checks/info-disclosure/directory-listing.d.ts +6 -0
  47. package/dist/checks/info-disclosure/directory-listing.d.ts.map +1 -0
  48. package/dist/checks/info-disclosure/directory-listing.js +60 -0
  49. package/dist/checks/info-disclosure/directory-listing.js.map +1 -0
  50. package/dist/checks/info-disclosure/exposed-files.d.ts +4 -0
  51. package/dist/checks/info-disclosure/exposed-files.d.ts.map +1 -0
  52. package/dist/checks/info-disclosure/exposed-files.js +56 -0
  53. package/dist/checks/info-disclosure/exposed-files.js.map +1 -0
  54. package/dist/checks/info-disclosure/more.d.ts +8 -0
  55. package/dist/checks/info-disclosure/more.d.ts.map +1 -0
  56. package/dist/checks/info-disclosure/more.js +186 -0
  57. package/dist/checks/info-disclosure/more.js.map +1 -0
  58. package/dist/checks/tls/certificate.d.ts +3 -0
  59. package/dist/checks/tls/certificate.d.ts.map +1 -0
  60. package/dist/checks/tls/certificate.js +49 -0
  61. package/dist/checks/tls/certificate.js.map +1 -0
  62. package/dist/checks/tls/hsts.d.ts +5 -0
  63. package/dist/checks/tls/hsts.d.ts.map +1 -0
  64. package/dist/checks/tls/hsts.js +35 -0
  65. package/dist/checks/tls/hsts.js.map +1 -0
  66. package/dist/checks/tls/https-redirect.d.ts +3 -0
  67. package/dist/checks/tls/https-redirect.d.ts.map +1 -0
  68. package/dist/checks/tls/https-redirect.js +46 -0
  69. package/dist/checks/tls/https-redirect.js.map +1 -0
  70. package/dist/checks/tls/legacy-protocols.d.ts +3 -0
  71. package/dist/checks/tls/legacy-protocols.d.ts.map +1 -0
  72. package/dist/checks/tls/legacy-protocols.js +27 -0
  73. package/dist/checks/tls/legacy-protocols.js.map +1 -0
  74. package/dist/checks/util.d.ts +22 -0
  75. package/dist/checks/util.d.ts.map +1 -0
  76. package/dist/checks/util.js +63 -0
  77. package/dist/checks/util.js.map +1 -0
  78. package/dist/code/checks/crypto.d.ts +5 -0
  79. package/dist/code/checks/crypto.d.ts.map +1 -0
  80. package/dist/code/checks/crypto.js +100 -0
  81. package/dist/code/checks/crypto.js.map +1 -0
  82. package/dist/code/checks/django.d.ts +14 -0
  83. package/dist/code/checks/django.d.ts.map +1 -0
  84. package/dist/code/checks/django.js +313 -0
  85. package/dist/code/checks/django.js.map +1 -0
  86. package/dist/code/checks/flask.d.ts +6 -0
  87. package/dist/code/checks/flask.d.ts.map +1 -0
  88. package/dist/code/checks/flask.js +100 -0
  89. package/dist/code/checks/flask.js.map +1 -0
  90. package/dist/code/checks/index.d.ts +9 -0
  91. package/dist/code/checks/index.d.ts.map +1 -0
  92. package/dist/code/checks/index.js +126 -0
  93. package/dist/code/checks/index.js.map +1 -0
  94. package/dist/code/checks/injection.d.ts +27 -0
  95. package/dist/code/checks/injection.d.ts.map +1 -0
  96. package/dist/code/checks/injection.js +189 -0
  97. package/dist/code/checks/injection.js.map +1 -0
  98. package/dist/code/checks/javascript-config.d.ts +14 -0
  99. package/dist/code/checks/javascript-config.d.ts.map +1 -0
  100. package/dist/code/checks/javascript-config.js +251 -0
  101. package/dist/code/checks/javascript-config.js.map +1 -0
  102. package/dist/code/checks/javascript.d.ts +34 -0
  103. package/dist/code/checks/javascript.d.ts.map +1 -0
  104. package/dist/code/checks/javascript.js +333 -0
  105. package/dist/code/checks/javascript.js.map +1 -0
  106. package/dist/code/checks/php.d.ts +27 -0
  107. package/dist/code/checks/php.d.ts.map +1 -0
  108. package/dist/code/checks/php.js +433 -0
  109. package/dist/code/checks/php.js.map +1 -0
  110. package/dist/code/checks/secrets.d.ts +10 -0
  111. package/dist/code/checks/secrets.d.ts.map +1 -0
  112. package/dist/code/checks/secrets.js +140 -0
  113. package/dist/code/checks/secrets.js.map +1 -0
  114. package/dist/code/checks/shared.d.ts +25 -0
  115. package/dist/code/checks/shared.d.ts.map +1 -0
  116. package/dist/code/checks/shared.js +57 -0
  117. package/dist/code/checks/shared.js.map +1 -0
  118. package/dist/code/checks/treesitter.d.ts +15 -0
  119. package/dist/code/checks/treesitter.d.ts.map +1 -0
  120. package/dist/code/checks/treesitter.js +322 -0
  121. package/dist/code/checks/treesitter.js.map +1 -0
  122. package/dist/code/checks/xml.d.ts +9 -0
  123. package/dist/code/checks/xml.d.ts.map +1 -0
  124. package/dist/code/checks/xml.js +78 -0
  125. package/dist/code/checks/xml.js.map +1 -0
  126. package/dist/code/context.d.ts +6 -0
  127. package/dist/code/context.d.ts.map +1 -0
  128. package/dist/code/context.js +92 -0
  129. package/dist/code/context.js.map +1 -0
  130. package/dist/code/detect.d.ts +3 -0
  131. package/dist/code/detect.d.ts.map +1 -0
  132. package/dist/code/detect.js +118 -0
  133. package/dist/code/detect.js.map +1 -0
  134. package/dist/code/django/settings.d.ts +46 -0
  135. package/dist/code/django/settings.d.ts.map +1 -0
  136. package/dist/code/django/settings.js +154 -0
  137. package/dist/code/django/settings.js.map +1 -0
  138. package/dist/code/findings.d.ts +18 -0
  139. package/dist/code/findings.d.ts.map +1 -0
  140. package/dist/code/findings.js +63 -0
  141. package/dist/code/findings.js.map +1 -0
  142. package/dist/code/javascript/ast.d.ts +46 -0
  143. package/dist/code/javascript/ast.d.ts.map +1 -0
  144. package/dist/code/javascript/ast.js +166 -0
  145. package/dist/code/javascript/ast.js.map +1 -0
  146. package/dist/code/javascript/index.d.ts +3 -0
  147. package/dist/code/javascript/index.d.ts.map +1 -0
  148. package/dist/code/javascript/index.js +3 -0
  149. package/dist/code/javascript/index.js.map +1 -0
  150. package/dist/code/javascript/module.d.ts +59 -0
  151. package/dist/code/javascript/module.d.ts.map +1 -0
  152. package/dist/code/javascript/module.js +250 -0
  153. package/dist/code/javascript/module.js.map +1 -0
  154. package/dist/code/php/index.d.ts +92 -0
  155. package/dist/code/php/index.d.ts.map +1 -0
  156. package/dist/code/php/index.js +316 -0
  157. package/dist/code/php/index.js.map +1 -0
  158. package/dist/code/project.d.ts +13 -0
  159. package/dist/code/project.d.ts.map +1 -0
  160. package/dist/code/project.js +69 -0
  161. package/dist/code/project.js.map +1 -0
  162. package/dist/code/python/ast.d.ts +47 -0
  163. package/dist/code/python/ast.d.ts.map +1 -0
  164. package/dist/code/python/ast.js +193 -0
  165. package/dist/code/python/ast.js.map +1 -0
  166. package/dist/code/python/index.d.ts +3 -0
  167. package/dist/code/python/index.d.ts.map +1 -0
  168. package/dist/code/python/index.js +3 -0
  169. package/dist/code/python/index.js.map +1 -0
  170. package/dist/code/python/module.d.ts +55 -0
  171. package/dist/code/python/module.d.ts.map +1 -0
  172. package/dist/code/python/module.js +215 -0
  173. package/dist/code/python/module.js.map +1 -0
  174. package/dist/code/scan.d.ts +24 -0
  175. package/dist/code/scan.d.ts.map +1 -0
  176. package/dist/code/scan.js +159 -0
  177. package/dist/code/scan.js.map +1 -0
  178. package/dist/code/treesitter/index.d.ts +83 -0
  179. package/dist/code/treesitter/index.d.ts.map +1 -0
  180. package/dist/code/treesitter/index.js +243 -0
  181. package/dist/code/treesitter/index.js.map +1 -0
  182. package/dist/code/types.d.ts +48 -0
  183. package/dist/code/types.d.ts.map +1 -0
  184. package/dist/code/types.js +2 -0
  185. package/dist/code/types.js.map +1 -0
  186. package/dist/content/en.d.ts +3 -0
  187. package/dist/content/en.d.ts.map +1 -0
  188. package/dist/content/en.js +722 -0
  189. package/dist/content/en.js.map +1 -0
  190. package/dist/content/index.d.ts +15 -0
  191. package/dist/content/index.d.ts.map +1 -0
  192. package/dist/content/index.js +21 -0
  193. package/dist/content/index.js.map +1 -0
  194. package/dist/content/types.d.ts +87 -0
  195. package/dist/content/types.d.ts.map +1 -0
  196. package/dist/content/types.js +2 -0
  197. package/dist/content/types.js.map +1 -0
  198. package/dist/engine.d.ts +52 -0
  199. package/dist/engine.d.ts.map +1 -0
  200. package/dist/engine.js +120 -0
  201. package/dist/engine.js.map +1 -0
  202. package/dist/index.d.ts +12 -0
  203. package/dist/index.d.ts.map +1 -0
  204. package/dist/index.js +11 -0
  205. package/dist/index.js.map +1 -0
  206. package/dist/meta.d.ts +4 -0
  207. package/dist/meta.d.ts.map +1 -0
  208. package/dist/meta.js +4 -0
  209. package/dist/meta.js.map +1 -0
  210. package/dist/net/context.d.ts +9 -0
  211. package/dist/net/context.d.ts.map +1 -0
  212. package/dist/net/context.js +62 -0
  213. package/dist/net/context.js.map +1 -0
  214. package/dist/net/guard.d.ts +16 -0
  215. package/dist/net/guard.d.ts.map +1 -0
  216. package/dist/net/guard.js +70 -0
  217. package/dist/net/guard.js.map +1 -0
  218. package/dist/net/http.d.ts +22 -0
  219. package/dist/net/http.d.ts.map +1 -0
  220. package/dist/net/http.js +114 -0
  221. package/dist/net/http.js.map +1 -0
  222. package/dist/net/tls.d.ts +15 -0
  223. package/dist/net/tls.d.ts.map +1 -0
  224. package/dist/net/tls.js +103 -0
  225. package/dist/net/tls.js.map +1 -0
  226. package/dist/report/html.d.ts +14 -0
  227. package/dist/report/html.d.ts.map +1 -0
  228. package/dist/report/html.js +210 -0
  229. package/dist/report/html.js.map +1 -0
  230. package/dist/report/index.d.ts +10 -0
  231. package/dist/report/index.d.ts.map +1 -0
  232. package/dist/report/index.js +10 -0
  233. package/dist/report/index.js.map +1 -0
  234. package/dist/report/manual.d.ts +62 -0
  235. package/dist/report/manual.d.ts.map +1 -0
  236. package/dist/report/manual.js +190 -0
  237. package/dist/report/manual.js.map +1 -0
  238. package/dist/report/merge.d.ts +8 -0
  239. package/dist/report/merge.d.ts.map +1 -0
  240. package/dist/report/merge.js +135 -0
  241. package/dist/report/merge.js.map +1 -0
  242. package/dist/report/summary.d.ts +10 -0
  243. package/dist/report/summary.d.ts.map +1 -0
  244. package/dist/report/summary.js +27 -0
  245. package/dist/report/summary.js.map +1 -0
  246. package/dist/report/validate.d.ts +3 -0
  247. package/dist/report/validate.d.ts.map +1 -0
  248. package/dist/report/validate.js +78 -0
  249. package/dist/report/validate.js.map +1 -0
  250. package/dist/types.d.ts +244 -0
  251. package/dist/types.d.ts.map +1 -0
  252. package/dist/types.js +2 -0
  253. package/dist/types.js.map +1 -0
  254. package/dist/validate.d.ts +4 -0
  255. package/dist/validate.d.ts.map +1 -0
  256. package/dist/validate.js +24 -0
  257. package/dist/validate.js.map +1 -0
  258. package/package.json +40 -0
@@ -0,0 +1,190 @@
1
+ import { ANNEX_B, STANDARD } from "../annex-b.js";
2
+ import { TOOL_NAME, TOOL_VERSION } from "../meta.js";
3
+ import { summarize } from "./summary.js";
4
+ /** Browser-safe Tier 3 helpers: guided manual review recorded on the same report rows. */
5
+ export const MANUAL_VERDICTS = ["pass", "fail", "not-applicable", "untested"];
6
+ /** Untested stays "manual" (pending), so an unreviewed item can never count as a pass. */
7
+ export function verdictStatus(verdict) {
8
+ return verdict === "untested" ? "manual" : verdict;
9
+ }
10
+ /** A row needs (or received) a human verdict when automation left it undecided. */
11
+ export function isReviewable(row) {
12
+ return row.manual !== undefined || row.status === "manual";
13
+ }
14
+ /** Attaches an "untested" review to every undecided row. Idempotent; existing verdicts are kept. */
15
+ export function startManualReview(report) {
16
+ const results = report.results.map((r) => r.manual || r.status !== "manual" ? r : { ...r, evaluatedBy: "manual", manual: { verdict: "untested" } });
17
+ return { ...report, results, summary: summarize(results) };
18
+ }
19
+ export class ReviewError extends Error {
20
+ name = "ReviewError";
21
+ }
22
+ /** Note/evidence edits closer together than this are folded into one history entry. */
23
+ const HISTORY_GAP_MS = 10 * 60 * 1000;
24
+ function snapshot(m) {
25
+ const s = { verdict: m.verdict, at: m.reviewedAt };
26
+ if (m.note)
27
+ s.note = m.note;
28
+ if (m.evidenceText)
29
+ s.evidenceText = m.evidenceText;
30
+ if (m.reviewer)
31
+ s.reviewer = m.reviewer;
32
+ if (m.attachments?.length)
33
+ s.attachments = m.attachments;
34
+ if (m.carriedFrom)
35
+ s.carriedFrom = m.carriedFrom;
36
+ return s;
37
+ }
38
+ /** Fail and Not applicable must be justified: a verdict without a note keeps the report incomplete. */
39
+ export function needsNote(row) {
40
+ const m = row.manual;
41
+ return !!m && (m.verdict === "fail" || m.verdict === "not-applicable") && !m.note?.trim();
42
+ }
43
+ /**
44
+ * Records (part of) a verdict for one item and recomputes the summary.
45
+ * The previous state goes into `history` when the verdict, reviewer or screenshots change, when a
46
+ * carried verdict is re-confirmed, or when note/evidence change more than 10 minutes after the last change.
47
+ * A carried verdict counts as re-confirmed only when a verdict is set; editing just the note keeps the mark.
48
+ */
49
+ export function recordVerdict(report, id, patch, now = new Date()) {
50
+ const started = startManualReview(report);
51
+ const index = started.results.findIndex((r) => r.id === id);
52
+ const row = started.results[index];
53
+ if (!row)
54
+ throw new ReviewError(`No item ${id} in this report.`);
55
+ if (!row.manual)
56
+ throw new ReviewError(`Item ${id} was decided by an automated check and cannot take a manual verdict.`);
57
+ if (patch.verdict && !MANUAL_VERDICTS.includes(patch.verdict))
58
+ throw new ReviewError(`Unknown verdict "${patch.verdict}".`);
59
+ // History and carry-over state are maintained here, never set by callers.
60
+ const { history: _h, carriedFrom: _c, ...changes } = patch;
61
+ const prev = row.manual;
62
+ const review = { ...prev, ...changes, reviewedAt: now.toISOString() };
63
+ const reconfirmed = !!prev.carriedFrom && changes.verdict !== undefined;
64
+ if (reconfirmed)
65
+ delete review.carriedFrom;
66
+ if (prev.reviewedAt) {
67
+ const textChanged = (review.note ?? "") !== (prev.note ?? "") || (review.evidenceText ?? "") !== (prev.evidenceText ?? "");
68
+ const ids = (m) => (m.attachments ?? []).map((a) => a.id).sort().join(",");
69
+ const significant = review.verdict !== prev.verdict ||
70
+ (review.reviewer ?? "") !== (prev.reviewer ?? "") ||
71
+ ids(review) !== ids(prev) ||
72
+ reconfirmed ||
73
+ (textChanged && now.getTime() - Date.parse(prev.reviewedAt) > HISTORY_GAP_MS);
74
+ if (significant)
75
+ review.history = [...(prev.history ?? []), snapshot(prev)];
76
+ }
77
+ for (const key of ["note", "evidenceText", "reviewer"]) {
78
+ if (typeof review[key] === "string" && !review[key].trim())
79
+ delete review[key];
80
+ }
81
+ if (review.attachments?.length === 0)
82
+ delete review.attachments;
83
+ const results = [...started.results];
84
+ results[index] = { ...row, evaluatedBy: "manual", status: verdictStatus(review.verdict), manual: review };
85
+ return { ...started, results, summary: summarize(results) };
86
+ }
87
+ export function reviewProgress(report) {
88
+ const rows = report.results.filter(isReviewable);
89
+ return { total: rows.length, reviewed: rows.filter((r) => r.manual && r.manual.verdict !== "untested").length };
90
+ }
91
+ /** Overall readiness across all three tiers. Untested items are counted as pending, never as pass. */
92
+ export function readiness(report) {
93
+ const count = (s) => report.results.filter((r) => r.status === s).length;
94
+ const r = {
95
+ total: report.results.length,
96
+ pass: count("pass"),
97
+ fail: count("fail"),
98
+ notApplicable: count("not-applicable"),
99
+ pending: count("manual"),
100
+ error: count("error"),
101
+ needsNote: report.results.filter(needsNote).length,
102
+ carried: report.results.filter((x) => x.manual?.carriedFrom).length,
103
+ };
104
+ const complete = r.pending === 0 && r.error === 0 && r.needsNote === 0;
105
+ return { ...r, complete, state: !complete ? "incomplete" : r.fail > 0 ? "failures" : "clean" };
106
+ }
107
+ /** A report with every Annex B item pending manual review, for reviewing without any automated scan. */
108
+ export function createManualReport(name, now = new Date()) {
109
+ const results = ANNEX_B.map((item) => ({
110
+ id: item.id,
111
+ section: item.section,
112
+ title: item.title,
113
+ tier: item.tier,
114
+ status: "manual",
115
+ evaluatedBy: "manual",
116
+ detail: item.manualNote,
117
+ manual: { verdict: "untested" },
118
+ }));
119
+ return {
120
+ tool: { name: TOOL_NAME, version: TOOL_VERSION },
121
+ standard: STANDARD,
122
+ target: name,
123
+ sources: {},
124
+ startedAt: now.toISOString(),
125
+ finishedAt: now.toISOString(),
126
+ summary: summarize(results),
127
+ results,
128
+ };
129
+ }
130
+ const sameTarget = (a, b) => {
131
+ const urlA = a.sources?.url;
132
+ const urlB = b.sources?.url;
133
+ const codeA = a.sources?.code?.name;
134
+ const codeB = b.sources?.code?.name;
135
+ const shared = (urlA && urlB ? [urlA === urlB] : []).concat(codeA && codeB ? [codeA === codeB] : []);
136
+ return shared.length > 0 && shared.every(Boolean);
137
+ };
138
+ /**
139
+ * Copies recorded manual verdicts from an earlier report of the same target into a newer one.
140
+ * Only rows still pending in the new report are filled; existing verdicts and automated results
141
+ * are never overridden. Carried verdicts are marked (`carriedFrom`) until someone re-records them.
142
+ */
143
+ export function carryForwardReviews(previous, next) {
144
+ if (!sameTarget(previous, next)) {
145
+ throw new ReviewError("Verdicts can only be carried between reports of the same website or project.");
146
+ }
147
+ const old = new Map(previous.results.map((r) => [r.id, r]));
148
+ const carried = [];
149
+ const skippedFails = [];
150
+ const results = next.results.map((row) => {
151
+ const prev = old.get(row.id)?.manual;
152
+ if (!prev || prev.verdict === "untested")
153
+ return row;
154
+ const pending = row.status === "manual" && (!row.manual || row.manual.verdict === "untested");
155
+ if (!pending) {
156
+ if (prev.verdict === "fail" && !row.manual)
157
+ skippedFails.push(row.id);
158
+ return row;
159
+ }
160
+ carried.push(row.id);
161
+ return {
162
+ ...row,
163
+ evaluatedBy: "manual",
164
+ status: verdictStatus(prev.verdict),
165
+ manual: { ...prev, carriedFrom: prev.carriedFrom ?? previous.finishedAt },
166
+ };
167
+ });
168
+ const day = previous.finishedAt.slice(0, 10);
169
+ const notes = [
170
+ ...(next.notes ?? []),
171
+ ...(carried.length ? [`Manual verdicts for ${carried.length} item(s) were carried from the report of ${day}; re-confirm them.`] : []),
172
+ ...skippedFails.map((id) => `Item ${id}: an earlier manual Fail (${day}) was not carried because an automated check now decides this item; re-check it.`),
173
+ ];
174
+ // Bring along embedded screenshots referenced by carried verdicts (CLI / exported JSON reports).
175
+ const attachments = { ...(next.attachments ?? {}) };
176
+ for (const row of results) {
177
+ if (!row.manual?.carriedFrom)
178
+ continue;
179
+ for (const ref of row.manual.attachments ?? []) {
180
+ const file = previous.attachments?.[ref.id];
181
+ if (file && !attachments[ref.id])
182
+ attachments[ref.id] = file;
183
+ }
184
+ }
185
+ return {
186
+ report: { ...next, results, notes, summary: summarize(results), ...(Object.keys(attachments).length ? { attachments } : {}) },
187
+ carried,
188
+ };
189
+ }
190
+ //# sourceMappingURL=manual.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual.js","sourceRoot":"","sources":["../../src/report/manual.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Report } from "../types.js";
2
+ /** Browser-safe. Combines a URL (Tier 1) report and a code (Tier 2) report into one 102-row report. */
3
+ export declare class MergeError extends Error {
4
+ name: string;
5
+ }
6
+ export declare function reportKind(report: Report): "url" | "code" | "combined";
7
+ export declare function mergeReports(a: Report, b: Report): Report;
8
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/report/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA0C,MAAM,EAAU,MAAM,aAAa,CAAC;AAI1F,uGAAuG;AAEvG,qBAAa,UAAW,SAAQ,KAAK;IAC1B,IAAI,SAAgB;CAC9B;AA8CD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,UAAU,CAKtE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA8EzD"}
@@ -0,0 +1,135 @@
1
+ import { verdictStatus } from "./manual.js";
2
+ import { summarize } from "./summary.js";
3
+ /** Browser-safe. Combines a URL (Tier 1) report and a code (Tier 2) report into one 102-row report. */
4
+ export class MergeError extends Error {
5
+ name = "MergeError";
6
+ }
7
+ const evaluatedByOf = (r) => r.evaluatedBy ?? (r.status === "manual" ? "manual" : "url");
8
+ /** fail > error > pass > not-applicable > manual: a failure from either source wins; an error means the row is incomplete. */
9
+ const PRIORITY = ["fail", "error", "pass", "not-applicable", "manual"];
10
+ const STATUS_TEXT = {
11
+ pass: "Pass",
12
+ fail: "Fail",
13
+ manual: "Manual review",
14
+ error: "Error",
15
+ "not-applicable": "Not applicable",
16
+ };
17
+ /** Decided by an automated check (not pending, not a human verdict). */
18
+ const automated = (r) => evaluatedByOf(r) !== "manual" && !r.manual && r.status !== "manual";
19
+ const recorded = (m) => (m && m.verdict !== "untested" ? m : undefined);
20
+ const newer = (a, b) => ((a.reviewedAt ?? "") >= (b.reviewedAt ?? "") ? a : b);
21
+ /**
22
+ * A recorded verdict beats "untested". Between two recorded verdicts a Fail always wins
23
+ * (as it does against automated results); otherwise the newer one wins.
24
+ */
25
+ function pickReview(a, b) {
26
+ const ra = recorded(a);
27
+ const rb = recorded(b);
28
+ if (ra && rb) {
29
+ const failA = ra.verdict === "fail";
30
+ const failB = rb.verdict === "fail";
31
+ const kept = failA !== failB ? (failA ? ra : rb) : newer(ra, rb);
32
+ const other = kept === ra ? rb : ra;
33
+ return { review: kept, ...(other.verdict !== kept.verdict ? { overridden: other } : {}) };
34
+ }
35
+ return { review: ra ?? rb ?? a ?? b };
36
+ }
37
+ const VERDICT_LABEL = {
38
+ pass: "Pass",
39
+ fail: "Fail",
40
+ "not-applicable": "Not applicable",
41
+ untested: "Untested",
42
+ };
43
+ export function reportKind(report) {
44
+ const s = report.sources;
45
+ if (s?.url && s.code)
46
+ return "combined";
47
+ if (s?.code)
48
+ return "code";
49
+ return "url";
50
+ }
51
+ export function mergeReports(a, b) {
52
+ const kinds = [reportKind(a), reportKind(b)];
53
+ if (!(kinds.includes("url") && kinds.includes("code"))) {
54
+ throw new MergeError("Merge needs one URL report and one code report.");
55
+ }
56
+ const [urlReport, codeReport] = reportKind(a) === "url" ? [a, b] : [b, a];
57
+ const codeById = new Map(codeReport.results.map((r) => [r.id, r]));
58
+ const replacedReviews = [];
59
+ const conflicts = [];
60
+ const results = urlReport.results.map((u) => {
61
+ const c = codeById.get(u.id);
62
+ if (!c)
63
+ return { ...u, evaluatedBy: evaluatedByOf(u) };
64
+ const uAuto = automated(u);
65
+ const cAuto = automated(c);
66
+ if (!uAuto && !cAuto) {
67
+ // Still undecided by automation: keep the more informative detail and the best human verdict.
68
+ const base = evaluatedByOf(c) !== "manual" || c.manual ? c : u;
69
+ const { review, overridden } = pickReview(u.manual, c.manual);
70
+ if (review && overridden) {
71
+ conflicts.push(`Item ${u.id}: manual verdicts differed; ${VERDICT_LABEL[review.verdict]} kept over ${VERDICT_LABEL[overridden.verdict]}.`);
72
+ }
73
+ const { manual: _drop, ...rest } = base;
74
+ return review
75
+ ? { ...rest, evaluatedBy: "manual", status: verdictStatus(review.verdict), manual: review }
76
+ : { ...rest, evaluatedBy: evaluatedByOf(base), status: "manual" };
77
+ }
78
+ if (!uAuto || !cAuto) {
79
+ // Automation decides the row, except that a human Fail recorded on the other side wins;
80
+ // any other human verdict is superseded.
81
+ const [winner, other] = uAuto ? [u, c] : [c, u];
82
+ if (other.manual?.verdict === "fail") {
83
+ // Keep the automated detail, evidence and findings; the manual Fail decides the status.
84
+ const source = winner === u ? "URL" : "Code";
85
+ if (winner.status !== "fail") {
86
+ conflicts.push(`Item ${u.id}: manual Fail kept over automated ${STATUS_TEXT[winner.status]} (${source}).`);
87
+ }
88
+ return {
89
+ ...winner,
90
+ status: "fail",
91
+ evaluatedBy: winner.status === "fail" ? evaluatedByOf(winner) : "manual",
92
+ detail: `${source}: ${winner.detail} | Manual: Fail`,
93
+ manual: other.manual,
94
+ };
95
+ }
96
+ if (recorded(other.manual))
97
+ replacedReviews.push(u.id);
98
+ return { ...winner, evaluatedBy: evaluatedByOf(winner) };
99
+ }
100
+ const status = PRIORITY.find((s) => s === u.status || s === c.status);
101
+ return {
102
+ ...u,
103
+ status,
104
+ evaluatedBy: "url+code",
105
+ detail: `URL: ${u.detail} | Code: ${c.detail}`,
106
+ ...(u.evidence || c.evidence ? { evidence: [...(u.evidence ?? []), ...(c.evidence ?? [])] } : {}),
107
+ ...(c.findings ? { findings: c.findings } : {}),
108
+ };
109
+ });
110
+ return {
111
+ tool: urlReport.tool,
112
+ standard: urlReport.standard,
113
+ target: `${urlReport.target} + ${codeReport.target}`,
114
+ sources: { url: urlReport.sources?.url ?? urlReport.target, code: codeReport.sources?.code },
115
+ notes: [
116
+ ...(urlReport.notes ?? []),
117
+ ...(codeReport.notes ?? []),
118
+ ...(replacedReviews.length
119
+ ? [`Manual verdicts for item(s) ${replacedReviews.join(", ")} were replaced by automated results from the merged report.`]
120
+ : []),
121
+ ...conflicts,
122
+ ],
123
+ ...mergeAttachments(urlReport, codeReport),
124
+ startedAt: [urlReport.startedAt, codeReport.startedAt].sort()[0],
125
+ finishedAt: [urlReport.finishedAt, codeReport.finishedAt].sort().at(-1),
126
+ summary: summarize(results),
127
+ results,
128
+ };
129
+ }
130
+ /** Embedded evidence files from both reports (ids are unique per attachment). */
131
+ function mergeAttachments(a, b) {
132
+ const all = { ...(a.attachments ?? {}), ...(b.attachments ?? {}) };
133
+ return Object.keys(all).length ? { attachments: all } : {};
134
+ }
135
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/report/merge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { CheckResult, ReportSummary, Status } from "../types.js";
2
+ export declare function summarize(results: readonly CheckResult[]): ReportSummary;
3
+ export interface SectionGroup {
4
+ section: string;
5
+ results: CheckResult[];
6
+ }
7
+ /** Groups results by Annex B section, preserving the standard's order. */
8
+ export declare function groupBySection(results: readonly CheckResult[]): SectionGroup[];
9
+ export declare const STATUS_LABEL: Record<Status, string>;
10
+ //# sourceMappingURL=summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../../src/report/summary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEtE,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,aAAa,CAIxE;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,YAAY,EAAE,CAW9E;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAM/C,CAAC"}
@@ -0,0 +1,27 @@
1
+ export function summarize(results) {
2
+ const summary = { total: results.length, pass: 0, fail: 0, manual: 0, error: 0, "not-applicable": 0 };
3
+ for (const r of results)
4
+ summary[r.status] = (summary[r.status] ?? 0) + 1;
5
+ return summary;
6
+ }
7
+ /** Groups results by Annex B section, preserving the standard's order. */
8
+ export function groupBySection(results) {
9
+ const groups = [];
10
+ for (const r of results) {
11
+ let group = groups.at(-1);
12
+ if (!group || group.section !== r.section) {
13
+ group = { section: r.section, results: [] };
14
+ groups.push(group);
15
+ }
16
+ group.results.push(r);
17
+ }
18
+ return groups;
19
+ }
20
+ export const STATUS_LABEL = {
21
+ pass: "Pass",
22
+ fail: "Fail",
23
+ manual: "Manual review",
24
+ error: "Error",
25
+ "not-applicable": "Not applicable",
26
+ };
27
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/report/summary.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,OAA+B;IACvD,MAAM,OAAO,GAAkB,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,CAAC;IACrH,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1E,OAAO,OAAO,CAAC;AACjB,CAAC;AAOD,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,OAA+B;IAC5D,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC1C,KAAK,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;YAC5C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAA2B;IAClD,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,eAAe;IACvB,KAAK,EAAE,OAAO;IACd,gBAAgB,EAAE,gBAAgB;CACnC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Report } from "../types.js";
2
+ export declare function isReport(value: unknown): value is Report;
3
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/report/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAiF1C,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAcxD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Browser-safe shape check for reports loaded from files (dashboard import, CLI --merge / --carry-from),
3
+ * so a malformed or hand-edited file is rejected instead of crashing rendering later.
4
+ */
5
+ const VERDICTS = ["pass", "fail", "not-applicable", "untested"];
6
+ const STATUSES = ["pass", "fail", "manual", "error", "not-applicable"];
7
+ const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
8
+ const optString = (v) => v === undefined || typeof v === "string";
9
+ const stringArray = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
10
+ function isAttachmentRef(v) {
11
+ return isObj(v) && typeof v.id === "string" && typeof v.name === "string" && typeof v.type === "string" && typeof v.size === "number";
12
+ }
13
+ function isSnapshot(v) {
14
+ return (isObj(v) &&
15
+ VERDICTS.includes(v.verdict) &&
16
+ typeof v.at === "string" &&
17
+ optString(v.note) &&
18
+ optString(v.evidenceText) &&
19
+ optString(v.reviewer) &&
20
+ optString(v.carriedFrom) &&
21
+ (v.attachments === undefined || (Array.isArray(v.attachments) && v.attachments.every(isAttachmentRef))));
22
+ }
23
+ function isManualReview(v) {
24
+ return (isObj(v) &&
25
+ VERDICTS.includes(v.verdict) &&
26
+ optString(v.note) &&
27
+ optString(v.evidenceText) &&
28
+ optString(v.reviewer) &&
29
+ optString(v.reviewedAt) &&
30
+ optString(v.carriedFrom) &&
31
+ (v.attachments === undefined || (Array.isArray(v.attachments) && v.attachments.every(isAttachmentRef))) &&
32
+ (v.history === undefined || (Array.isArray(v.history) && v.history.every(isSnapshot))));
33
+ }
34
+ function isFinding(v) {
35
+ return isObj(v) && typeof v.file === "string" && typeof v.line === "number" && typeof v.snippet === "string";
36
+ }
37
+ function isRow(v) {
38
+ return (isObj(v) &&
39
+ typeof v.id === "string" &&
40
+ typeof v.section === "string" &&
41
+ typeof v.title === "string" &&
42
+ typeof v.detail === "string" &&
43
+ STATUSES.includes(v.status) &&
44
+ (v.evidence === undefined || stringArray(v.evidence)) &&
45
+ (v.findings === undefined || (Array.isArray(v.findings) && v.findings.every(isFinding))) &&
46
+ (v.manual === undefined || isManualReview(v.manual)));
47
+ }
48
+ function isSources(v) {
49
+ if (v === undefined)
50
+ return true;
51
+ if (!isObj(v) || !optString(v.url))
52
+ return false;
53
+ const c = v.code;
54
+ return (c === undefined ||
55
+ (isObj(c) &&
56
+ typeof c.name === "string" &&
57
+ stringArray(c.frameworks) &&
58
+ typeof c.filesAnalyzed === "number" &&
59
+ Array.isArray(c.parseErrors)));
60
+ }
61
+ function isEmbedded(v) {
62
+ return isObj(v) && typeof v.name === "string" && typeof v.type === "string" && typeof v.size === "number" && typeof v.base64 === "string";
63
+ }
64
+ export function isReport(value) {
65
+ if (!isObj(value))
66
+ return false;
67
+ const r = value;
68
+ return (typeof r.target === "string" &&
69
+ typeof r.startedAt === "string" &&
70
+ typeof r.finishedAt === "string" &&
71
+ isObj(r.summary) &&
72
+ Array.isArray(r.results) &&
73
+ r.results.every(isRow) &&
74
+ isSources(r.sources) &&
75
+ (r.notes === undefined || stringArray(r.notes)) &&
76
+ (r.attachments === undefined || (isObj(r.attachments) && Object.values(r.attachments).every(isEmbedded))));
77
+ }
78
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/report/validate.ts"],"names":[],"mappings":"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"}