vaspera 2.11.0 → 2.13.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 (176) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/__tests__/audit-trail.test.d.ts +7 -0
  3. package/dist/__tests__/audit-trail.test.d.ts.map +1 -0
  4. package/dist/__tests__/audit-trail.test.js +336 -0
  5. package/dist/__tests__/audit-trail.test.js.map +1 -0
  6. package/dist/__tests__/property-test-helpers.d.ts +1 -1
  7. package/dist/__tests__/siem-integration.test.d.ts +7 -0
  8. package/dist/__tests__/siem-integration.test.d.ts.map +1 -0
  9. package/dist/__tests__/siem-integration.test.js +285 -0
  10. package/dist/__tests__/siem-integration.test.js.map +1 -0
  11. package/dist/action/pr-comment.test.js +1 -0
  12. package/dist/action/pr-comment.test.js.map +1 -1
  13. package/dist/action/sarif-upload.test.js +1 -0
  14. package/dist/action/sarif-upload.test.js.map +1 -1
  15. package/dist/autofix/ast/__tests__/typescript.test.d.ts +5 -0
  16. package/dist/autofix/ast/__tests__/typescript.test.d.ts.map +1 -0
  17. package/dist/autofix/ast/__tests__/typescript.test.js +210 -0
  18. package/dist/autofix/ast/__tests__/typescript.test.js.map +1 -0
  19. package/dist/autofix/ast/index.d.ts +11 -0
  20. package/dist/autofix/ast/index.d.ts.map +1 -0
  21. package/dist/autofix/ast/index.js +11 -0
  22. package/dist/autofix/ast/index.js.map +1 -0
  23. package/dist/autofix/ast/types.d.ts +77 -0
  24. package/dist/autofix/ast/types.d.ts.map +1 -0
  25. package/dist/autofix/ast/types.js +9 -0
  26. package/dist/autofix/ast/types.js.map +1 -0
  27. package/dist/autofix/ast/typescript.d.ts +17 -0
  28. package/dist/autofix/ast/typescript.d.ts.map +1 -0
  29. package/dist/autofix/ast/typescript.js +427 -0
  30. package/dist/autofix/ast/typescript.js.map +1 -0
  31. package/dist/autofix/constitution.schema.d.ts +21 -21
  32. package/dist/autofix/index.d.ts +1 -0
  33. package/dist/autofix/index.d.ts.map +1 -1
  34. package/dist/autofix/index.js +2 -0
  35. package/dist/autofix/index.js.map +1 -1
  36. package/dist/config/flags.d.ts +6 -6
  37. package/dist/history/store.d.ts +55 -1
  38. package/dist/history/store.d.ts.map +1 -1
  39. package/dist/history/store.js +152 -4
  40. package/dist/history/store.js.map +1 -1
  41. package/dist/history/types.d.ts +9 -5
  42. package/dist/history/types.d.ts.map +1 -1
  43. package/dist/history/verify.d.ts.map +1 -1
  44. package/dist/history/verify.js +5 -3
  45. package/dist/history/verify.js.map +1 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +923 -16
  48. package/dist/index.js.map +1 -1
  49. package/dist/integrations/siem/datadog.d.ts +44 -0
  50. package/dist/integrations/siem/datadog.d.ts.map +1 -0
  51. package/dist/integrations/siem/datadog.js +211 -0
  52. package/dist/integrations/siem/datadog.js.map +1 -0
  53. package/dist/integrations/siem/format.d.ts +59 -0
  54. package/dist/integrations/siem/format.d.ts.map +1 -0
  55. package/dist/integrations/siem/format.js +360 -0
  56. package/dist/integrations/siem/format.js.map +1 -0
  57. package/dist/integrations/siem/index.d.ts +56 -0
  58. package/dist/integrations/siem/index.d.ts.map +1 -0
  59. package/dist/integrations/siem/index.js +117 -0
  60. package/dist/integrations/siem/index.js.map +1 -0
  61. package/dist/integrations/siem/sentinel.d.ts +53 -0
  62. package/dist/integrations/siem/sentinel.d.ts.map +1 -0
  63. package/dist/integrations/siem/sentinel.js +231 -0
  64. package/dist/integrations/siem/sentinel.js.map +1 -0
  65. package/dist/integrations/siem/splunk.d.ts +46 -0
  66. package/dist/integrations/siem/splunk.d.ts.map +1 -0
  67. package/dist/integrations/siem/splunk.js +210 -0
  68. package/dist/integrations/siem/splunk.js.map +1 -0
  69. package/dist/integrations/siem/types.d.ts +210 -0
  70. package/dist/integrations/siem/types.d.ts.map +1 -0
  71. package/dist/integrations/siem/types.js +9 -0
  72. package/dist/integrations/siem/types.js.map +1 -0
  73. package/dist/persistence/__tests__/json-fallback.test.d.ts +5 -0
  74. package/dist/persistence/__tests__/json-fallback.test.d.ts.map +1 -0
  75. package/dist/persistence/__tests__/json-fallback.test.js +249 -0
  76. package/dist/persistence/__tests__/json-fallback.test.js.map +1 -0
  77. package/dist/persistence/__tests__/persistence.test.d.ts +5 -0
  78. package/dist/persistence/__tests__/persistence.test.d.ts.map +1 -0
  79. package/dist/persistence/__tests__/persistence.test.js +369 -0
  80. package/dist/persistence/__tests__/persistence.test.js.map +1 -0
  81. package/dist/persistence/db.d.ts +30 -0
  82. package/dist/persistence/db.d.ts.map +1 -0
  83. package/dist/persistence/db.js +128 -0
  84. package/dist/persistence/db.js.map +1 -0
  85. package/dist/persistence/index.d.ts +75 -0
  86. package/dist/persistence/index.d.ts.map +1 -0
  87. package/dist/persistence/index.js +268 -0
  88. package/dist/persistence/index.js.map +1 -0
  89. package/dist/persistence/json-fallback.d.ts +52 -0
  90. package/dist/persistence/json-fallback.d.ts.map +1 -0
  91. package/dist/persistence/json-fallback.js +283 -0
  92. package/dist/persistence/json-fallback.js.map +1 -0
  93. package/dist/persistence/migrations/index.d.ts +10 -0
  94. package/dist/persistence/migrations/index.d.ts.map +1 -0
  95. package/dist/persistence/migrations/index.js +125 -0
  96. package/dist/persistence/migrations/index.js.map +1 -0
  97. package/dist/persistence/repositories/findings.d.ts +41 -0
  98. package/dist/persistence/repositories/findings.d.ts.map +1 -0
  99. package/dist/persistence/repositories/findings.js +238 -0
  100. package/dist/persistence/repositories/findings.js.map +1 -0
  101. package/dist/persistence/repositories/projects.d.ts +22 -0
  102. package/dist/persistence/repositories/projects.d.ts.map +1 -0
  103. package/dist/persistence/repositories/projects.js +71 -0
  104. package/dist/persistence/repositories/projects.js.map +1 -0
  105. package/dist/persistence/repositories/scans.d.ts +30 -0
  106. package/dist/persistence/repositories/scans.d.ts.map +1 -0
  107. package/dist/persistence/repositories/scans.js +107 -0
  108. package/dist/persistence/repositories/scans.js.map +1 -0
  109. package/dist/persistence/repositories/trends.d.ts +42 -0
  110. package/dist/persistence/repositories/trends.d.ts.map +1 -0
  111. package/dist/persistence/repositories/trends.js +178 -0
  112. package/dist/persistence/repositories/trends.js.map +1 -0
  113. package/dist/persistence/types.d.ts +105 -0
  114. package/dist/persistence/types.d.ts.map +1 -0
  115. package/dist/persistence/types.js +13 -0
  116. package/dist/persistence/types.js.map +1 -0
  117. package/dist/plugins/types.d.ts +2 -2
  118. package/dist/scanners/ai-code/index.d.ts.map +1 -1
  119. package/dist/scanners/ai-code/index.js +90 -2
  120. package/dist/scanners/ai-code/index.js.map +1 -1
  121. package/dist/scanners/ai-code/types.d.ts +24 -12
  122. package/dist/scanners/ai-code/types.d.ts.map +1 -1
  123. package/dist/scanners/cache.d.ts.map +1 -1
  124. package/dist/scanners/cache.js +1 -0
  125. package/dist/scanners/cache.js.map +1 -1
  126. package/dist/scanners/deploy/types.d.ts +13 -13
  127. package/dist/scanners/detection/__tests__/detection.test.d.ts +5 -0
  128. package/dist/scanners/detection/__tests__/detection.test.d.ts.map +1 -0
  129. package/dist/scanners/detection/__tests__/detection.test.js +265 -0
  130. package/dist/scanners/detection/__tests__/detection.test.js.map +1 -0
  131. package/dist/scanners/detection/engines/ast-query.d.ts +23 -0
  132. package/dist/scanners/detection/engines/ast-query.d.ts.map +1 -0
  133. package/dist/scanners/detection/engines/ast-query.js +232 -0
  134. package/dist/scanners/detection/engines/ast-query.js.map +1 -0
  135. package/dist/scanners/detection/engines/data-flow.d.ts +12 -0
  136. package/dist/scanners/detection/engines/data-flow.d.ts.map +1 -0
  137. package/dist/scanners/detection/engines/data-flow.js +269 -0
  138. package/dist/scanners/detection/engines/data-flow.js.map +1 -0
  139. package/dist/scanners/detection/index.d.ts +29 -0
  140. package/dist/scanners/detection/index.d.ts.map +1 -0
  141. package/dist/scanners/detection/index.js +140 -0
  142. package/dist/scanners/detection/index.js.map +1 -0
  143. package/dist/scanners/detection/rules/builtin.d.ts +14 -0
  144. package/dist/scanners/detection/rules/builtin.d.ts.map +1 -0
  145. package/dist/scanners/detection/rules/builtin.js +307 -0
  146. package/dist/scanners/detection/rules/builtin.js.map +1 -0
  147. package/dist/scanners/detection/rules/loader.d.ts +19 -0
  148. package/dist/scanners/detection/rules/loader.d.ts.map +1 -0
  149. package/dist/scanners/detection/rules/loader.js +111 -0
  150. package/dist/scanners/detection/rules/loader.js.map +1 -0
  151. package/dist/scanners/detection/types.d.ts +171 -0
  152. package/dist/scanners/detection/types.d.ts.map +1 -0
  153. package/dist/scanners/detection/types.js +36 -0
  154. package/dist/scanners/detection/types.js.map +1 -0
  155. package/dist/scanners/eslint.d.ts.map +1 -1
  156. package/dist/scanners/eslint.js +45 -3
  157. package/dist/scanners/eslint.js.map +1 -1
  158. package/dist/scanners/index.d.ts +9 -1
  159. package/dist/scanners/index.d.ts.map +1 -1
  160. package/dist/scanners/index.js +64 -0
  161. package/dist/scanners/index.js.map +1 -1
  162. package/dist/scanners/index.test.js +6 -6
  163. package/dist/scanners/index.test.js.map +1 -1
  164. package/dist/scanners/scale/bottleneck-detector.d.ts +13 -2
  165. package/dist/scanners/scale/bottleneck-detector.d.ts.map +1 -1
  166. package/dist/scanners/scale/bottleneck-detector.js +199 -72
  167. package/dist/scanners/scale/bottleneck-detector.js.map +1 -1
  168. package/dist/scanners/scale/types.d.ts +3 -3
  169. package/dist/scanners/types.d.ts +19 -2
  170. package/dist/scanners/types.d.ts.map +1 -1
  171. package/dist/scanners/types.js +1 -0
  172. package/dist/scanners/types.js.map +1 -1
  173. package/dist/scanners/typescript.d.ts.map +1 -1
  174. package/dist/scanners/typescript.js +36 -4
  175. package/dist/scanners/typescript.js.map +1 -1
  176. package/package.json +5 -1
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Findings Repository
3
+ *
4
+ * CRUD operations for security findings with status tracking.
5
+ *
6
+ * @module persistence/repositories/findings
7
+ */
8
+ import { randomUUID } from "crypto";
9
+ export class FindingsRepository {
10
+ db;
11
+ constructor(db) {
12
+ this.db = db;
13
+ }
14
+ create(finding) {
15
+ const id = randomUUID();
16
+ const now = new Date().toISOString();
17
+ this.db
18
+ .prepare(`INSERT INTO findings (
19
+ id, project_id, certification_id, severity, category, file, line, column_num,
20
+ description, scanner_source, rule_id, status, confidence, cwe_ids,
21
+ first_seen_at, last_seen_at, metadata
22
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
23
+ .run(id, finding.projectId, finding.certificationId || null, finding.severity, finding.category, finding.file, finding.line, finding.column || null, finding.description, finding.scannerSource, finding.ruleId || null, finding.status || "open", finding.confidence || 100, finding.cweIds ? JSON.stringify(finding.cweIds) : null, now, now, finding.metadata ? JSON.stringify(finding.metadata) : null);
24
+ return {
25
+ ...finding,
26
+ id,
27
+ firstSeenAt: now,
28
+ lastSeenAt: now,
29
+ status: finding.status || "open",
30
+ };
31
+ }
32
+ upsertFromScan(finding) {
33
+ const existing = this.findBySignature(finding.projectId, finding.file, finding.line, finding.category, finding.scannerSource);
34
+ if (existing) {
35
+ this.db
36
+ .prepare("UPDATE findings SET last_seen_at = datetime('now') WHERE id = ?")
37
+ .run(existing.id);
38
+ return {
39
+ finding: { ...existing, lastSeenAt: new Date().toISOString() },
40
+ isNew: false,
41
+ };
42
+ }
43
+ const created = this.create({ ...finding, status: "open" });
44
+ return { finding: created, isNew: true };
45
+ }
46
+ findById(id) {
47
+ const row = this.db
48
+ .prepare("SELECT * FROM findings WHERE id = ?")
49
+ .get(id);
50
+ return row ? this.mapRow(row) : undefined;
51
+ }
52
+ findBySignature(projectId, file, line, category, scannerSource) {
53
+ const row = this.db
54
+ .prepare(`SELECT * FROM findings
55
+ WHERE project_id = ? AND file = ? AND line = ? AND category = ? AND scanner_source = ?
56
+ AND status != 'fixed'`)
57
+ .get(projectId, file, line, category, scannerSource);
58
+ return row ? this.mapRow(row) : undefined;
59
+ }
60
+ find(filter) {
61
+ let sql = "SELECT * FROM findings WHERE 1=1";
62
+ const params = [];
63
+ if (filter.projectId) {
64
+ sql += " AND project_id = ?";
65
+ params.push(filter.projectId);
66
+ }
67
+ if (filter.certificationId) {
68
+ sql += " AND certification_id = ?";
69
+ params.push(filter.certificationId);
70
+ }
71
+ if (filter.severity) {
72
+ const severities = Array.isArray(filter.severity) ? filter.severity : [filter.severity];
73
+ sql += ` AND severity IN (${severities.map(() => "?").join(", ")})`;
74
+ params.push(...severities);
75
+ }
76
+ if (filter.status) {
77
+ const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
78
+ sql += ` AND status IN (${statuses.map(() => "?").join(", ")})`;
79
+ params.push(...statuses);
80
+ }
81
+ if (filter.category) {
82
+ const categories = Array.isArray(filter.category) ? filter.category : [filter.category];
83
+ sql += ` AND category IN (${categories.map(() => "?").join(", ")})`;
84
+ params.push(...categories);
85
+ }
86
+ if (filter.file) {
87
+ sql += " AND file LIKE ?";
88
+ params.push(`%${filter.file}%`);
89
+ }
90
+ if (filter.scannerSource) {
91
+ sql += " AND scanner_source = ?";
92
+ params.push(filter.scannerSource);
93
+ }
94
+ if (filter.sinceDate) {
95
+ sql += " AND first_seen_at >= ?";
96
+ params.push(filter.sinceDate);
97
+ }
98
+ if (filter.untilDate) {
99
+ sql += " AND first_seen_at <= ?";
100
+ params.push(filter.untilDate);
101
+ }
102
+ sql += " ORDER BY first_seen_at DESC";
103
+ if (filter.limit) {
104
+ sql += " LIMIT ?";
105
+ params.push(filter.limit);
106
+ }
107
+ if (filter.offset) {
108
+ sql += " OFFSET ?";
109
+ params.push(filter.offset);
110
+ }
111
+ const rows = this.db.prepare(sql).all(...params);
112
+ return rows.map((r) => this.mapRow(r));
113
+ }
114
+ updateStatus(id, status, fixedBy) {
115
+ const updates = ["status = ?"];
116
+ const params = [status];
117
+ if (status === "fixed") {
118
+ updates.push("fixed_at = datetime('now')");
119
+ if (fixedBy) {
120
+ updates.push("fixed_by = ?");
121
+ params.push(fixedBy);
122
+ }
123
+ }
124
+ params.push(id);
125
+ const result = this.db
126
+ .prepare(`UPDATE findings SET ${updates.join(", ")} WHERE id = ?`)
127
+ .run(...params);
128
+ if (result.changes > 0) {
129
+ this.db
130
+ .prepare(`INSERT INTO fix_history (finding_id, action, performed_by)
131
+ VALUES (?, ?, ?)`)
132
+ .run(id, status === "fixed" ? "fixed" : status, fixedBy || null);
133
+ }
134
+ return result.changes > 0;
135
+ }
136
+ markFixed(ids, fixedBy) {
137
+ let count = 0;
138
+ this.db.transaction(() => {
139
+ for (const id of ids) {
140
+ if (this.updateStatus(id, "fixed", fixedBy)) {
141
+ count++;
142
+ }
143
+ }
144
+ })();
145
+ return count;
146
+ }
147
+ markFalsePositive(id, reason) {
148
+ const result = this.db
149
+ .prepare(`UPDATE findings
150
+ SET status = 'false_positive', false_positive = 1, false_positive_reason = ?
151
+ WHERE id = ?`)
152
+ .run(reason || null, id);
153
+ if (result.changes > 0) {
154
+ this.db
155
+ .prepare(`INSERT INTO fix_history (finding_id, action, notes)
156
+ VALUES (?, 'marked_false_positive', ?)`)
157
+ .run(id, reason || null);
158
+ }
159
+ return result.changes > 0;
160
+ }
161
+ getStats(projectId) {
162
+ const countsBySeverity = this.db
163
+ .prepare(`SELECT severity, COUNT(*) as count
164
+ FROM findings WHERE project_id = ? AND status = 'open'
165
+ GROUP BY severity`)
166
+ .all(projectId);
167
+ const countsByCategory = this.db
168
+ .prepare(`SELECT category, COUNT(*) as count
169
+ FROM findings WHERE project_id = ? AND status = 'open'
170
+ GROUP BY category`)
171
+ .all(projectId);
172
+ const countsByScanner = this.db
173
+ .prepare(`SELECT scanner_source, COUNT(*) as count
174
+ FROM findings WHERE project_id = ?
175
+ GROUP BY scanner_source`)
176
+ .all(projectId);
177
+ const totals = this.db
178
+ .prepare(`SELECT
179
+ COUNT(*) as total,
180
+ SUM(CASE WHEN status = 'open' THEN 1 ELSE 0 END) as open,
181
+ SUM(CASE WHEN status = 'fixed' THEN 1 ELSE 0 END) as fixed,
182
+ SUM(CASE WHEN status = 'false_positive' THEN 1 ELSE 0 END) as false_positives
183
+ FROM findings WHERE project_id = ?`)
184
+ .get(projectId);
185
+ const avgMttr = this.db
186
+ .prepare(`SELECT AVG((julianday(fixed_at) - julianday(first_seen_at)) * 24) as avg_hours
187
+ FROM findings WHERE project_id = ? AND status = 'fixed' AND fixed_at IS NOT NULL`)
188
+ .get(projectId);
189
+ const fixVelocity = this.db
190
+ .prepare(`SELECT COUNT(*) * 1.0 / MAX(1, (julianday('now') - julianday(MIN(fixed_at))) / 7) as velocity
191
+ FROM findings WHERE project_id = ? AND status = 'fixed'`)
192
+ .get(projectId);
193
+ const scanDates = this.db
194
+ .prepare(`SELECT MIN(started_at) as first_scan, MAX(started_at) as last_scan
195
+ FROM scans WHERE project_id = ?`)
196
+ .get(projectId);
197
+ return {
198
+ projectId,
199
+ totalFindings: totals?.total || 0,
200
+ openFindings: totals?.open || 0,
201
+ fixedFindings: totals?.fixed || 0,
202
+ falsePositives: totals?.false_positives || 0,
203
+ bySeverity: Object.fromEntries(countsBySeverity.map((r) => [r.severity, r.count])),
204
+ byCategory: Object.fromEntries(countsByCategory.map((r) => [r.category, r.count])),
205
+ byScanner: Object.fromEntries(countsByScanner.map((r) => [r.scanner_source, r.count])),
206
+ avgMttrHours: avgMttr?.avg_hours || undefined,
207
+ fixVelocityPerWeek: fixVelocity?.velocity || undefined,
208
+ firstScanAt: scanDates?.first_scan || undefined,
209
+ lastScanAt: scanDates?.last_scan || undefined,
210
+ };
211
+ }
212
+ mapRow(row) {
213
+ return {
214
+ id: row.id,
215
+ projectId: row.project_id,
216
+ certificationId: row.certification_id || undefined,
217
+ severity: row.severity,
218
+ category: row.category,
219
+ file: row.file,
220
+ line: row.line,
221
+ column: row.column_num || undefined,
222
+ description: row.description,
223
+ scannerSource: row.scanner_source,
224
+ ruleId: row.rule_id || undefined,
225
+ status: row.status,
226
+ confidence: row.confidence,
227
+ cweIds: row.cwe_ids ? JSON.parse(row.cwe_ids) : undefined,
228
+ firstSeenAt: row.first_seen_at,
229
+ lastSeenAt: row.last_seen_at,
230
+ fixedAt: row.fixed_at || undefined,
231
+ fixedBy: row.fixed_by || undefined,
232
+ falsePositive: row.false_positive === 1,
233
+ falsePositiveReason: row.false_positive_reason || undefined,
234
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
235
+ };
236
+ }
237
+ }
238
+ //# sourceMappingURL=findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.js","sourceRoot":"","sources":["../../../src/persistence/repositories/findings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Projects Repository
3
+ *
4
+ * CRUD operations for projects.
5
+ *
6
+ * @module persistence/repositories/projects
7
+ */
8
+ import type Database from "better-sqlite3";
9
+ import type { Project } from "../types.js";
10
+ export declare class ProjectsRepository {
11
+ private db;
12
+ constructor(db: Database.Database);
13
+ create(path: string, name?: string): Project;
14
+ findById(id: string): Project | undefined;
15
+ findByPath(path: string): Project | undefined;
16
+ findOrCreate(path: string, name?: string): Project;
17
+ list(): Project[];
18
+ updateLastScan(id: string): void;
19
+ delete(id: string): boolean;
20
+ private mapRow;
21
+ }
22
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../../src/persistence/repositories/projects.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI3C,qBAAa,kBAAkB;IACjB,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAEzC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO;IAe5C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAQzC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAQ7C,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO;IAMlD,IAAI,IAAI,OAAO,EAAE;IAQjB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAMhC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQ3B,OAAO,CAAC,MAAM;CAUf"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Projects Repository
3
+ *
4
+ * CRUD operations for projects.
5
+ *
6
+ * @module persistence/repositories/projects
7
+ */
8
+ import { randomUUID } from "crypto";
9
+ import { basename } from "path";
10
+ export class ProjectsRepository {
11
+ db;
12
+ constructor(db) {
13
+ this.db = db;
14
+ }
15
+ create(path, name) {
16
+ const id = randomUUID();
17
+ const projectName = name || basename(path);
18
+ const createdAt = new Date().toISOString();
19
+ this.db
20
+ .prepare(`INSERT INTO projects (id, path, name, created_at)
21
+ VALUES (?, ?, ?, ?)`)
22
+ .run(id, path, projectName, createdAt);
23
+ return { id, path, name: projectName, createdAt };
24
+ }
25
+ findById(id) {
26
+ const row = this.db
27
+ .prepare("SELECT * FROM projects WHERE id = ?")
28
+ .get(id);
29
+ return row ? this.mapRow(row) : undefined;
30
+ }
31
+ findByPath(path) {
32
+ const row = this.db
33
+ .prepare("SELECT * FROM projects WHERE path = ?")
34
+ .get(path);
35
+ return row ? this.mapRow(row) : undefined;
36
+ }
37
+ findOrCreate(path, name) {
38
+ const existing = this.findByPath(path);
39
+ if (existing)
40
+ return existing;
41
+ return this.create(path, name);
42
+ }
43
+ list() {
44
+ const rows = this.db
45
+ .prepare("SELECT * FROM projects ORDER BY last_scan_at DESC NULLS LAST, created_at DESC")
46
+ .all();
47
+ return rows.map((r) => this.mapRow(r));
48
+ }
49
+ updateLastScan(id) {
50
+ this.db
51
+ .prepare("UPDATE projects SET last_scan_at = datetime('now') WHERE id = ?")
52
+ .run(id);
53
+ }
54
+ delete(id) {
55
+ const result = this.db
56
+ .prepare("DELETE FROM projects WHERE id = ?")
57
+ .run(id);
58
+ return result.changes > 0;
59
+ }
60
+ mapRow(row) {
61
+ return {
62
+ id: row.id,
63
+ path: row.path,
64
+ name: row.name,
65
+ createdAt: row.created_at,
66
+ lastScanAt: row.last_scan_at || undefined,
67
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
68
+ };
69
+ }
70
+ }
71
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../../src/persistence/repositories/projects.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEhC,MAAM,OAAO,kBAAkB;IACT;IAApB,YAAoB,EAAqB;QAArB,OAAE,GAAF,EAAE,CAAmB;IAAG,CAAC;IAE7C,MAAM,CAAC,IAAY,EAAE,IAAa;QAChC,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAE3C,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;6BACqB,CACtB;aACA,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QAEzC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;IACpD,CAAC;IAED,QAAQ,CAAC,EAAU;QACjB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CAAC,qCAAqC,CAAC;aAC9C,GAAG,CAAC,EAAE,CAAQ,CAAC;QAElB,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5C,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CAAC,uCAAuC,CAAC;aAChD,GAAG,CAAC,IAAI,CAAQ,CAAC;QAEpB,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5C,CAAC;IAED,YAAY,CAAC,IAAY,EAAE,IAAa;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,IAAI;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CAAC,+EAA+E,CAAC;aACxF,GAAG,EAAW,CAAC;QAElB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,cAAc,CAAC,EAAU;QACvB,IAAI,CAAC,EAAE;aACJ,OAAO,CAAC,iEAAiE,CAAC;aAC1E,GAAG,CAAC,EAAE,CAAC,CAAC;IACb,CAAC;IAED,MAAM,CAAC,EAAU;QACf,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE;aACnB,OAAO,CAAC,mCAAmC,CAAC;aAC5C,GAAG,CAAC,EAAE,CAAC,CAAC;QAEX,OAAO,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;IAC5B,CAAC;IAEO,MAAM,CAAC,GAAQ;QACrB,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,SAAS,EAAE,GAAG,CAAC,UAAU;YACzB,UAAU,EAAE,GAAG,CAAC,YAAY,IAAI,SAAS;YACzC,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;SAC9D,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Scans Repository
3
+ *
4
+ * CRUD operations for scan history.
5
+ *
6
+ * @module persistence/repositories/scans
7
+ */
8
+ import type Database from "better-sqlite3";
9
+ import type { Scan } from "../types.js";
10
+ import type { Severity } from "../../certification/types.js";
11
+ export declare class ScansRepository {
12
+ private db;
13
+ constructor(db: Database.Database);
14
+ create(projectId: string, certificationId?: string): Scan;
15
+ complete(id: string, results: {
16
+ totalFindings: number;
17
+ newFindings: number;
18
+ fixedFindings: number;
19
+ bySeverity: Record<Severity, number>;
20
+ byScanner: Record<string, number>;
21
+ duration: number;
22
+ }): void;
23
+ fail(id: string, error: string): void;
24
+ findById(id: string): Scan | undefined;
25
+ findByProject(projectId: string, limit?: number): Scan[];
26
+ findRecent(limit?: number): Scan[];
27
+ getCompletedCount(projectId: string, sinceDate?: string): number;
28
+ private mapRow;
29
+ }
30
+ //# sourceMappingURL=scans.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scans.d.ts","sourceRoot":"","sources":["../../../src/persistence/repositories/scans.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAG7D,qBAAa,eAAe;IACd,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAEzC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI;IAyBzD,QAAQ,CACN,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACrC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClC,QAAQ,EAAE,MAAM,CAAC;KAClB,GACA,IAAI;IAyBP,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAYrC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAQtC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,IAAI,EAAE;IAWpD,UAAU,CAAC,KAAK,SAAM,GAAG,IAAI,EAAE;IAU/B,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAahE,OAAO,CAAC,MAAM;CAoBf"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Scans Repository
3
+ *
4
+ * CRUD operations for scan history.
5
+ *
6
+ * @module persistence/repositories/scans
7
+ */
8
+ import { randomUUID } from "crypto";
9
+ export class ScansRepository {
10
+ db;
11
+ constructor(db) {
12
+ this.db = db;
13
+ }
14
+ create(projectId, certificationId) {
15
+ const id = randomUUID();
16
+ const startedAt = new Date().toISOString();
17
+ this.db
18
+ .prepare(`INSERT INTO scans (id, project_id, certification_id, started_at, status)
19
+ VALUES (?, ?, ?, ?, 'running')`)
20
+ .run(id, projectId, certificationId || null, startedAt);
21
+ return {
22
+ id,
23
+ projectId,
24
+ certificationId,
25
+ startedAt,
26
+ status: "running",
27
+ totalFindings: 0,
28
+ newFindings: 0,
29
+ fixedFindings: 0,
30
+ bySeverity: { critical: 0, high: 0, medium: 0, low: 0, info: 0 },
31
+ byScanner: {},
32
+ };
33
+ }
34
+ complete(id, results) {
35
+ this.db
36
+ .prepare(`UPDATE scans SET
37
+ completed_at = datetime('now'),
38
+ status = 'completed',
39
+ total_findings = ?,
40
+ new_findings = ?,
41
+ fixed_findings = ?,
42
+ by_severity = ?,
43
+ by_scanner = ?,
44
+ duration = ?
45
+ WHERE id = ?`)
46
+ .run(results.totalFindings, results.newFindings, results.fixedFindings, JSON.stringify(results.bySeverity), JSON.stringify(results.byScanner), results.duration, id);
47
+ }
48
+ fail(id, error) {
49
+ this.db
50
+ .prepare(`UPDATE scans SET
51
+ completed_at = datetime('now'),
52
+ status = 'failed',
53
+ error = ?
54
+ WHERE id = ?`)
55
+ .run(error, id);
56
+ }
57
+ findById(id) {
58
+ const row = this.db
59
+ .prepare("SELECT * FROM scans WHERE id = ?")
60
+ .get(id);
61
+ return row ? this.mapRow(row) : undefined;
62
+ }
63
+ findByProject(projectId, limit = 50) {
64
+ const rows = this.db
65
+ .prepare(`SELECT * FROM scans WHERE project_id = ?
66
+ ORDER BY started_at DESC LIMIT ?`)
67
+ .all(projectId, limit);
68
+ return rows.map((r) => this.mapRow(r));
69
+ }
70
+ findRecent(limit = 100) {
71
+ const rows = this.db
72
+ .prepare(`SELECT * FROM scans ORDER BY started_at DESC LIMIT ?`)
73
+ .all(limit);
74
+ return rows.map((r) => this.mapRow(r));
75
+ }
76
+ getCompletedCount(projectId, sinceDate) {
77
+ let sql = "SELECT COUNT(*) as count FROM scans WHERE project_id = ? AND status = 'completed'";
78
+ const params = [projectId];
79
+ if (sinceDate) {
80
+ sql += " AND started_at >= ?";
81
+ params.push(sinceDate);
82
+ }
83
+ const result = this.db.prepare(sql).get(...params);
84
+ return result?.count || 0;
85
+ }
86
+ mapRow(row) {
87
+ return {
88
+ id: row.id,
89
+ projectId: row.project_id,
90
+ certificationId: row.certification_id || undefined,
91
+ startedAt: row.started_at,
92
+ completedAt: row.completed_at || undefined,
93
+ status: row.status,
94
+ totalFindings: row.total_findings || 0,
95
+ newFindings: row.new_findings || 0,
96
+ fixedFindings: row.fixed_findings || 0,
97
+ bySeverity: row.by_severity
98
+ ? JSON.parse(row.by_severity)
99
+ : { critical: 0, high: 0, medium: 0, low: 0, info: 0 },
100
+ byScanner: row.by_scanner ? JSON.parse(row.by_scanner) : {},
101
+ duration: row.duration || undefined,
102
+ error: row.error || undefined,
103
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
104
+ };
105
+ }
106
+ }
107
+ //# sourceMappingURL=scans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scans.js","sourceRoot":"","sources":["../../../src/persistence/repositories/scans.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Trends Repository
3
+ *
4
+ * Historical trend calculations and aggregations.
5
+ *
6
+ * @module persistence/repositories/trends
7
+ */
8
+ import type Database from "better-sqlite3";
9
+ import type { TrendData } from "../types.js";
10
+ import type { Severity } from "../../certification/types.js";
11
+ export type TrendPeriodType = "day" | "week" | "month";
12
+ export declare class TrendsRepository {
13
+ private db;
14
+ constructor(db: Database.Database);
15
+ calculateTrends(projectId: string, period: TrendPeriodType, lookbackPeriods?: number): TrendData[];
16
+ getMTTR(projectId: string, period: TrendPeriodType, lookbackPeriods?: number): {
17
+ period: string;
18
+ mttrHours: number;
19
+ }[];
20
+ getFixVelocity(projectId: string, period: TrendPeriodType, lookbackPeriods?: number): {
21
+ period: string;
22
+ fixesCount: number;
23
+ }[];
24
+ getSeverityTrends(projectId: string, period: TrendPeriodType, lookbackPeriods?: number): {
25
+ period: string;
26
+ bySeverity: Record<Severity, number>;
27
+ }[];
28
+ getCategoryBreakdown(projectId: string): {
29
+ category: string;
30
+ count: number;
31
+ percentage: number;
32
+ }[];
33
+ getScannerEffectiveness(projectId: string): {
34
+ scanner: string;
35
+ found: number;
36
+ falsePositives: number;
37
+ accuracy: number;
38
+ }[];
39
+ private getPeriodFormat;
40
+ private getPeriodInterval;
41
+ }
42
+ //# sourceMappingURL=trends.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trends.d.ts","sourceRoot":"","sources":["../../../src/persistence/repositories/trends.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAEvD,qBAAa,gBAAgB;IACf,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ,CAAC,QAAQ;IAEzC,eAAe,CACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,EACvB,eAAe,SAAK,GACnB,SAAS,EAAE;IAyCd,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,SAAK,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE;IAyBlH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,SAAK,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE;IAyB1H,iBAAiB,CACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,EACvB,eAAe,SAAK,GACnB;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;KAAE,EAAE;IAiC7D,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE;IA0BlG,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE;IAsB1H,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,iBAAiB;CAU1B"}