@telora/daemon 0.15.40 → 0.15.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-info.json +2 -2
- package/dist/feeds/ghsa.d.ts +9 -0
- package/dist/feeds/ghsa.d.ts.map +1 -1
- package/dist/feeds/ghsa.js +7 -0
- package/dist/feeds/ghsa.js.map +1 -1
- package/dist/feeds/osv.d.ts +9 -0
- package/dist/feeds/osv.d.ts.map +1 -1
- package/dist/feeds/osv.js +12 -1
- package/dist/feeds/osv.js.map +1 -1
- package/dist/focus-completion.d.ts.map +1 -1
- package/dist/focus-completion.js +21 -6
- package/dist/focus-completion.js.map +1 -1
- package/dist/focus-engine.d.ts.map +1 -1
- package/dist/focus-engine.js +19 -12
- package/dist/focus-engine.js.map +1 -1
- package/dist/focus-merge.d.ts.map +1 -1
- package/dist/focus-merge.js +2 -0
- package/dist/focus-merge.js.map +1 -1
- package/dist/scanners/workflow.d.ts +8 -0
- package/dist/scanners/workflow.d.ts.map +1 -1
- package/dist/scanners/workflow.js +113 -27
- package/dist/scanners/workflow.js.map +1 -1
- package/dist/security-auto-inject.d.ts +17 -32
- package/dist/security-auto-inject.d.ts.map +1 -1
- package/dist/security-auto-inject.js +13 -49
- package/dist/security-auto-inject.js.map +1 -1
- package/dist/security-finding-gate.d.ts +74 -0
- package/dist/security-finding-gate.d.ts.map +1 -0
- package/dist/security-finding-gate.js +82 -0
- package/dist/security-finding-gate.js.map +1 -0
- package/dist/security-rescan-resolution.d.ts +1 -27
- package/dist/security-rescan-resolution.d.ts.map +1 -1
- package/dist/security-rescan-resolution.js +1 -38
- package/dist/security-rescan-resolution.js.map +1 -1
- package/dist/security-scan-engine.d.ts +43 -23
- package/dist/security-scan-engine.d.ts.map +1 -1
- package/dist/security-scan-engine.js +54 -72
- package/dist/security-scan-engine.js.map +1 -1
- package/dist/spawner-lifecycle.d.ts +2 -0
- package/dist/spawner-lifecycle.d.ts.map +1 -1
- package/dist/spawner-lifecycle.js +3 -2
- package/dist/spawner-lifecycle.js.map +1 -1
- package/dist/verification-engine.d.ts +9 -0
- package/dist/verification-engine.d.ts.map +1 -1
- package/dist/verification-engine.js +29 -3
- package/dist/verification-engine.js.map +1 -1
- package/package.json +1 -1
|
@@ -23,11 +23,19 @@
|
|
|
23
23
|
* @module scanners/workflow
|
|
24
24
|
*/
|
|
25
25
|
import type { Scanner } from '../security-scan-engine.js';
|
|
26
|
+
import { type IocFeedEntry } from '../feeds/local.js';
|
|
26
27
|
export interface WorkflowScannerDeps {
|
|
27
28
|
/** List workflow files in the repo's `.github/workflows/` directory. */
|
|
28
29
|
readWorkflowsDir: (repoPath: string) => Promise<string[]>;
|
|
29
30
|
/** Read a workflow file's contents as UTF-8. */
|
|
30
31
|
readFile: (path: string) => Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Load org-local IOC feed entries with `ioc_class='workflow'`. Each
|
|
34
|
+
* entry's pattern is tested against the workflow file's full text and
|
|
35
|
+
* against every step's `uses`, `run`, and `name` value. Default loads
|
|
36
|
+
* from the daemon edge function; tests inject a stub.
|
|
37
|
+
*/
|
|
38
|
+
loadLocalIocFeed?: (organizationId: string, iocClass?: string) => Promise<IocFeedEntry[]>;
|
|
31
39
|
}
|
|
32
40
|
export declare function createWorkflowScanner(deps?: WorkflowScannerDeps): Scanner;
|
|
33
41
|
export declare const workflowScanner: Scanner;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/scanners/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAyC,OAAO,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/scanners/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAyC,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACjG,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAMxF,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,gDAAgD;IAChD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CAC3F;AAoMD,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,mBAAiC,GAAG,OAAO,CAkHtF;AAWD,eAAO,MAAM,eAAe,EAAE,OAAiC,CAAC"}
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { readdir, readFile } from 'node:fs/promises';
|
|
26
26
|
import { join } from 'node:path';
|
|
27
27
|
import { parse as parseYaml } from 'yaml';
|
|
28
|
+
import { loadLocalIocFeed, matchesPattern } from '../feeds/local.js';
|
|
28
29
|
const WORKFLOW_EXTENSIONS = ['.yml', '.yaml'];
|
|
29
30
|
const CHECKOUT_REF_HEAD_SHA = '${{ github.event.pull_request.head.sha }}';
|
|
30
31
|
const CHECKOUT_REF_HEAD_REF = '${{ github.event.pull_request.head.ref }}';
|
|
@@ -46,6 +47,7 @@ const defaultDeps = {
|
|
|
46
47
|
.map((name) => join(dir, name));
|
|
47
48
|
},
|
|
48
49
|
readFile: (path) => readFile(path, 'utf8'),
|
|
50
|
+
loadLocalIocFeed,
|
|
49
51
|
};
|
|
50
52
|
// ---------------------------------------------------------------------------
|
|
51
53
|
// Type guards -- narrow `unknown` parsed YAML
|
|
@@ -168,13 +170,58 @@ function analyzeJob(job) {
|
|
|
168
170
|
// Factory
|
|
169
171
|
// ---------------------------------------------------------------------------
|
|
170
172
|
const PATTERN_LABEL = 'pull_request_target + checkout-head + cache-write';
|
|
173
|
+
/**
|
|
174
|
+
* Collect every string from a parsed workflow tree that an IOC pattern
|
|
175
|
+
* could meaningfully match: the full file text, every step's `uses`,
|
|
176
|
+
* `run`, and `name`. Each entry carries a label so emitted findings can
|
|
177
|
+
* cite where the match occurred.
|
|
178
|
+
*/
|
|
179
|
+
function collectIocCandidates(workflowFileName, rawText, parsed) {
|
|
180
|
+
const out = [];
|
|
181
|
+
out.push({ label: `${workflowFileName} (full text)`, value: rawText });
|
|
182
|
+
const jobs = parsed.jobs;
|
|
183
|
+
if (!isRecord(jobs))
|
|
184
|
+
return out;
|
|
185
|
+
for (const [jobName, jobValue] of Object.entries(jobs)) {
|
|
186
|
+
if (!isRecord(jobValue))
|
|
187
|
+
continue;
|
|
188
|
+
const steps = jobValue.steps;
|
|
189
|
+
if (!Array.isArray(steps))
|
|
190
|
+
continue;
|
|
191
|
+
steps.forEach((raw, idx) => {
|
|
192
|
+
const step = asStep(raw);
|
|
193
|
+
if (!step)
|
|
194
|
+
return;
|
|
195
|
+
const stepLabel = `${workflowFileName}:${jobName}#${idx}`;
|
|
196
|
+
if (step.uses)
|
|
197
|
+
out.push({ label: `${stepLabel}.uses`, value: step.uses });
|
|
198
|
+
if (step.run)
|
|
199
|
+
out.push({ label: `${stepLabel}.run`, value: step.run });
|
|
200
|
+
if (step.name)
|
|
201
|
+
out.push({ label: `${stepLabel}.name`, value: step.name });
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
171
206
|
export function createWorkflowScanner(deps = defaultDeps) {
|
|
207
|
+
const loadFeed = deps.loadLocalIocFeed ?? loadLocalIocFeed;
|
|
172
208
|
return {
|
|
173
209
|
iocClass: 'workflow',
|
|
174
210
|
async scan(ctx) {
|
|
175
211
|
const files = await deps.readWorkflowsDir(ctx.repoPath);
|
|
176
212
|
const findings = [];
|
|
177
213
|
let parseErrors = 0;
|
|
214
|
+
const warnings = [];
|
|
215
|
+
// Org-local custom workflow IOCs. Resilient: a feed error is captured
|
|
216
|
+
// as a warning, never an exception — a missing feed must not abort
|
|
217
|
+
// the built-in pull_request_target check.
|
|
218
|
+
const iocEntries = await loadFeed(ctx.config.organizationId, 'workflow').catch((err) => {
|
|
219
|
+
warnings.push(`local-ioc-feed: ${err.message}`);
|
|
220
|
+
return [];
|
|
221
|
+
});
|
|
222
|
+
// Dedupe findings across the file's pattern match and the IOC scan
|
|
223
|
+
// when both fire for the same identifier shape.
|
|
224
|
+
const seen = new Set();
|
|
178
225
|
for (const file of files) {
|
|
179
226
|
let raw;
|
|
180
227
|
try {
|
|
@@ -194,37 +241,76 @@ export function createWorkflowScanner(deps = defaultDeps) {
|
|
|
194
241
|
}
|
|
195
242
|
if (!isRecord(parsed))
|
|
196
243
|
continue;
|
|
197
|
-
if (!hasPullRequestTargetTrigger(parsed.on))
|
|
198
|
-
continue;
|
|
199
|
-
const jobs = parsed.jobs;
|
|
200
|
-
if (!isRecord(jobs))
|
|
201
|
-
continue;
|
|
202
244
|
const workflowFileName = baseName(file);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
245
|
+
// Built-in pull_request_target + checkout-head + cache-write check.
|
|
246
|
+
if (hasPullRequestTargetTrigger(parsed.on)) {
|
|
247
|
+
const jobs = parsed.jobs;
|
|
248
|
+
if (isRecord(jobs)) {
|
|
249
|
+
for (const [jobName, jobValue] of Object.entries(jobs)) {
|
|
250
|
+
const analysis = analyzeJob(jobValue);
|
|
251
|
+
if (!analysis.hasCheckoutOfForkHead || !analysis.cacheWriteAfterCheckout)
|
|
252
|
+
continue;
|
|
253
|
+
const identifier = `${workflowFileName}:${jobName}`;
|
|
254
|
+
if (seen.has(identifier))
|
|
255
|
+
continue;
|
|
256
|
+
seen.add(identifier);
|
|
257
|
+
findings.push({
|
|
258
|
+
iocClass: 'workflow',
|
|
259
|
+
severity: 'high',
|
|
260
|
+
identifier,
|
|
261
|
+
payload: {
|
|
262
|
+
workflow: workflowFileName,
|
|
263
|
+
job: jobName,
|
|
264
|
+
pattern: PATTERN_LABEL,
|
|
265
|
+
matched_lines: analysis.matchedLines,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Local IOC feed pass: test every entry's pattern against the file
|
|
272
|
+
// text and every step `uses`/`run`/`name` value. One finding per
|
|
273
|
+
// (entry, file) so multiple matches inside one file collapse into a
|
|
274
|
+
// single finding referencing the strongest match location.
|
|
275
|
+
if (iocEntries.length > 0) {
|
|
276
|
+
const candidates = collectIocCandidates(workflowFileName, raw, parsed);
|
|
277
|
+
for (const entry of iocEntries) {
|
|
278
|
+
if (entry.iocClass !== 'workflow')
|
|
279
|
+
continue;
|
|
280
|
+
const match = candidates.find((c) => matchesPattern(c.value, entry));
|
|
281
|
+
if (!match)
|
|
282
|
+
continue;
|
|
283
|
+
const identifier = `${workflowFileName}#ioc:${entry.id}`;
|
|
284
|
+
if (seen.has(identifier))
|
|
285
|
+
continue;
|
|
286
|
+
seen.add(identifier);
|
|
287
|
+
findings.push({
|
|
288
|
+
iocClass: 'workflow',
|
|
289
|
+
severity: entry.severity,
|
|
290
|
+
identifier,
|
|
291
|
+
payload: {
|
|
292
|
+
source: 'local_ioc_feed',
|
|
293
|
+
entry_id: entry.id,
|
|
294
|
+
pattern_type: entry.patternType,
|
|
295
|
+
pattern: entry.pattern,
|
|
296
|
+
advisory_text: entry.advisoryText,
|
|
297
|
+
workflow: workflowFileName,
|
|
298
|
+
matched_at: match.label,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
}
|
|
218
302
|
}
|
|
219
303
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
parse_errors: parseErrors,
|
|
226
|
-
},
|
|
304
|
+
const coverage = {
|
|
305
|
+
workflows_scanned: files.length,
|
|
306
|
+
risky_patterns: findings.length,
|
|
307
|
+
parse_errors: parseErrors,
|
|
308
|
+
local_ioc_entries: iocEntries.length,
|
|
227
309
|
};
|
|
310
|
+
if (warnings.length > 0) {
|
|
311
|
+
coverage.warnings = warnings;
|
|
312
|
+
}
|
|
313
|
+
return { findings, coverage };
|
|
228
314
|
},
|
|
229
315
|
};
|
|
230
316
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/scanners/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/scanners/workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Auto-injection for security findings.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 1.
|
|
4
|
+
* Every open finding that the engine sees -- newly observed or with no
|
|
5
|
+
* linked remediation -- is handed to processNewFinding. The function:
|
|
6
|
+
* 1. Finds-or-creates the product's active Security focus + tree.
|
|
7
7
|
* 2. Creates or reuses an entity node representing the vulnerable surface.
|
|
8
8
|
* 3. Creates an injection node on the focus's reality tree with
|
|
9
9
|
* statement = advisory summary; targets the entity node via a
|
|
@@ -13,12 +13,9 @@
|
|
|
13
13
|
* 5. Sets finding.linked_injection_id.
|
|
14
14
|
* 6. Writes a security_finding_audit row with action='auto_injected'.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* Suppressed findings (suppression jsonb non-null and not expired) are
|
|
21
|
-
* skipped entirely.
|
|
16
|
+
* No severity gate -- AI attempts every finding. The only short-circuit
|
|
17
|
+
* is `skipped_already_linked` when the finding already carries a
|
|
18
|
+
* remediation injection.
|
|
22
19
|
*
|
|
23
20
|
* @module security-auto-inject
|
|
24
21
|
*/
|
|
@@ -31,28 +28,19 @@ export interface FindingForInjection {
|
|
|
31
28
|
severity: Severity;
|
|
32
29
|
identifier: string;
|
|
33
30
|
payload: Record<string, unknown>;
|
|
34
|
-
status: 'open' | 'resolved' | '
|
|
35
|
-
suppression: Record<string, unknown> | null;
|
|
31
|
+
status: 'open' | 'remediating' | 'resolved' | 'escalated';
|
|
36
32
|
linkedInjectionId: string | null;
|
|
37
33
|
}
|
|
38
|
-
export
|
|
39
|
-
|
|
40
|
-
autoInjectThreshold: Severity;
|
|
34
|
+
export type AutoInjectOptions = Record<string, never>;
|
|
35
|
+
export interface AutoInjectDeps {
|
|
41
36
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* suppression and already-linked checks still apply.
|
|
37
|
+
* Find an open product_focus carrying the Security workflow for this
|
|
38
|
+
* product, or create one. Always returns a focus + its reality tree.
|
|
45
39
|
*/
|
|
46
|
-
|
|
47
|
-
/** Actor user id, if the action came from a UI button rather than the daemon. */
|
|
48
|
-
actorUserId?: string;
|
|
49
|
-
}
|
|
50
|
-
export interface AutoInjectDeps {
|
|
51
|
-
/** Look up the Security focus id + tree id for a product. */
|
|
52
|
-
resolveSecurityFocus: (productId: string) => Promise<{
|
|
40
|
+
findOrCreateActiveSecurityFocus: (productId: string, organizationId: string) => Promise<{
|
|
53
41
|
focusId: string;
|
|
54
42
|
treeId: string;
|
|
55
|
-
}
|
|
43
|
+
}>;
|
|
56
44
|
/**
|
|
57
45
|
* Create or find an entity node representing the vulnerable surface
|
|
58
46
|
* (one per identifier per focus, deduplicated server-side).
|
|
@@ -96,19 +84,16 @@ export interface AutoInjectDeps {
|
|
|
96
84
|
writeAudit: (input: {
|
|
97
85
|
findingId: string;
|
|
98
86
|
organizationId: string;
|
|
99
|
-
action: 'auto_injected'
|
|
100
|
-
actorUserId?: string;
|
|
87
|
+
action: 'auto_injected';
|
|
101
88
|
reason?: string;
|
|
102
89
|
payload?: Record<string, unknown>;
|
|
103
90
|
}) => Promise<void>;
|
|
104
91
|
}
|
|
105
|
-
/** Returns true when `severity` is at or above `threshold`. */
|
|
106
|
-
export declare function meetsThreshold(severity: Severity, threshold: Severity): boolean;
|
|
107
92
|
export interface ProcessNewFindingResult {
|
|
108
|
-
status: 'injected' | '
|
|
93
|
+
status: 'injected' | 'skipped_already_linked';
|
|
109
94
|
injectionNodeId?: string;
|
|
110
95
|
deliveryId?: string;
|
|
111
96
|
}
|
|
112
|
-
export declare function processNewFinding(finding: FindingForInjection,
|
|
97
|
+
export declare function processNewFinding(finding: FindingForInjection, _options: AutoInjectOptions, deps: AutoInjectDeps): Promise<ProcessNewFindingResult>;
|
|
113
98
|
export declare function buildDefaultAutoInjectDeps(): AutoInjectDeps;
|
|
114
99
|
//# sourceMappingURL=security-auto-inject.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-auto-inject.d.ts","sourceRoot":"","sources":["../src/security-auto-inject.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"security-auto-inject.d.ts","sourceRoot":"","sources":["../src/security-auto-inject.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Auto-injection for security findings.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 1.
|
|
4
|
+
* Every open finding that the engine sees -- newly observed or with no
|
|
5
|
+
* linked remediation -- is handed to processNewFinding. The function:
|
|
6
|
+
* 1. Finds-or-creates the product's active Security focus + tree.
|
|
7
7
|
* 2. Creates or reuses an entity node representing the vulnerable surface.
|
|
8
8
|
* 3. Creates an injection node on the focus's reality tree with
|
|
9
9
|
* statement = advisory summary; targets the entity node via a
|
|
@@ -13,40 +13,14 @@
|
|
|
13
13
|
* 5. Sets finding.linked_injection_id.
|
|
14
14
|
* 6. Writes a security_finding_audit row with action='auto_injected'.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* Suppressed findings (suppression jsonb non-null and not expired) are
|
|
21
|
-
* skipped entirely.
|
|
16
|
+
* No severity gate -- AI attempts every finding. The only short-circuit
|
|
17
|
+
* is `skipped_already_linked` when the finding already carries a
|
|
18
|
+
* remediation injection.
|
|
22
19
|
*
|
|
23
20
|
* @module security-auto-inject
|
|
24
21
|
*/
|
|
25
22
|
import { callApi } from './queries/shared.js';
|
|
26
23
|
// ---------------------------------------------------------------------------
|
|
27
|
-
// Severity comparison
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
const SEVERITY_RANK = {
|
|
30
|
-
low: 1,
|
|
31
|
-
medium: 2,
|
|
32
|
-
high: 3,
|
|
33
|
-
critical: 4,
|
|
34
|
-
};
|
|
35
|
-
/** Returns true when `severity` is at or above `threshold`. */
|
|
36
|
-
export function meetsThreshold(severity, threshold) {
|
|
37
|
-
return SEVERITY_RANK[severity] >= SEVERITY_RANK[threshold];
|
|
38
|
-
}
|
|
39
|
-
function isSuppressedAndActive(finding, now = new Date()) {
|
|
40
|
-
if (finding.status !== 'suppressed')
|
|
41
|
-
return false;
|
|
42
|
-
if (!finding.suppression)
|
|
43
|
-
return false;
|
|
44
|
-
const s = finding.suppression;
|
|
45
|
-
if (!s.expires_at)
|
|
46
|
-
return true;
|
|
47
|
-
return new Date(s.expires_at).getTime() > now.getTime();
|
|
48
|
-
}
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
24
|
// Statement + label builders
|
|
51
25
|
// ---------------------------------------------------------------------------
|
|
52
26
|
function buildAdvisorySummary(finding) {
|
|
@@ -62,17 +36,10 @@ function buildAdvisorySummary(finding) {
|
|
|
62
36
|
function buildEntityLabel(finding) {
|
|
63
37
|
return `${finding.iocClass}:${finding.identifier}`;
|
|
64
38
|
}
|
|
65
|
-
export async function processNewFinding(finding,
|
|
39
|
+
export async function processNewFinding(finding, _options, deps) {
|
|
66
40
|
if (finding.linkedInjectionId)
|
|
67
41
|
return { status: 'skipped_already_linked' };
|
|
68
|
-
|
|
69
|
-
return { status: 'skipped_suppressed' };
|
|
70
|
-
if (!options.force && !meetsThreshold(finding.severity, options.autoInjectThreshold)) {
|
|
71
|
-
return { status: 'skipped_below_threshold' };
|
|
72
|
-
}
|
|
73
|
-
const focus = await deps.resolveSecurityFocus(finding.productId);
|
|
74
|
-
if (!focus)
|
|
75
|
-
return { status: 'skipped_no_security_focus' };
|
|
42
|
+
const focus = await deps.findOrCreateActiveSecurityFocus(finding.productId, finding.organizationId);
|
|
76
43
|
const entity = await deps.upsertEntityNode({
|
|
77
44
|
treeId: focus.treeId,
|
|
78
45
|
organizationId: finding.organizationId,
|
|
@@ -103,12 +70,10 @@ export async function processNewFinding(finding, options, deps) {
|
|
|
103
70
|
await deps.writeAudit({
|
|
104
71
|
findingId: finding.id,
|
|
105
72
|
organizationId: finding.organizationId,
|
|
106
|
-
action:
|
|
107
|
-
|
|
108
|
-
reason: options.force ? 'click_to_remediate' : 'severity_threshold_met',
|
|
73
|
+
action: 'auto_injected',
|
|
74
|
+
reason: 'finding_observed',
|
|
109
75
|
payload: {
|
|
110
76
|
severity: finding.severity,
|
|
111
|
-
threshold: options.autoInjectThreshold,
|
|
112
77
|
delivery_id: delivery.deliveryId,
|
|
113
78
|
injection_node_id: injection.nodeId,
|
|
114
79
|
},
|
|
@@ -124,9 +89,8 @@ export async function processNewFinding(finding, options, deps) {
|
|
|
124
89
|
// ---------------------------------------------------------------------------
|
|
125
90
|
export function buildDefaultAutoInjectDeps() {
|
|
126
91
|
return {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return res?.focus ?? null;
|
|
92
|
+
findOrCreateActiveSecurityFocus: async (productId, organizationId) => {
|
|
93
|
+
return callApi('daemon_find_or_create_active_security_focus', { productId, organizationId });
|
|
130
94
|
},
|
|
131
95
|
upsertEntityNode: async (input) => {
|
|
132
96
|
return callApi('daemon_upsert_security_entity_node', input);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-auto-inject.js","sourceRoot":"","sources":["../src/security-auto-inject.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"security-auto-inject.js","sourceRoot":"","sources":["../src/security-auto-inject.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic check evaluator for the Security workflow verify-closed gate.
|
|
3
|
+
*
|
|
4
|
+
* The Security workflow declares its verify-closed stage with
|
|
5
|
+
* `agent_directive.gate = { kind: 'deterministic', check:
|
|
6
|
+
* 'security.finding_no_longer_present' }`. The gate's contract: a security
|
|
7
|
+
* remediation injection cannot be verified (retired) unless the linked
|
|
8
|
+
* security_findings row has flipped to status='resolved' (the re-scan
|
|
9
|
+
* resolution sweep clears the indicator).
|
|
10
|
+
*
|
|
11
|
+
* If the gate fails, the linked finding is flipped to status='escalated'
|
|
12
|
+
* with escalation_reason='verify_gate_failed'. After the D1 schema
|
|
13
|
+
* collapse, the Security workflow no longer carries a 'Reopen (failed)'
|
|
14
|
+
* transition — a failed verify-closed escalates and stays.
|
|
15
|
+
*
|
|
16
|
+
* Wired into verification-engine.ts: before calling verifyInjectionOnPass
|
|
17
|
+
* on a 'passed' outcome, the engine consults this gate. A 'passed' outcome
|
|
18
|
+
* is demoted to 'failed' when the finding is still open/remediating.
|
|
19
|
+
*
|
|
20
|
+
* @module security-finding-gate
|
|
21
|
+
*/
|
|
22
|
+
export type FindingStatusForGate = 'open' | 'remediating' | 'resolved' | 'escalated';
|
|
23
|
+
export interface FindingLookupResult {
|
|
24
|
+
findingId: string;
|
|
25
|
+
status: FindingStatusForGate;
|
|
26
|
+
}
|
|
27
|
+
export interface SecurityFindingGateDeps {
|
|
28
|
+
/**
|
|
29
|
+
* Look up the security_findings row whose linked_injection_id matches the
|
|
30
|
+
* given injection node id. Returns null when no finding is linked
|
|
31
|
+
* (non-security injection -- gate is a no-op).
|
|
32
|
+
*/
|
|
33
|
+
lookupFindingByInjection: (injectionNodeId: string) => Promise<FindingLookupResult | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Flip the finding to status='escalated' and append a security_finding_audit
|
|
36
|
+
* row with action='escalated'. Idempotent at the DB layer.
|
|
37
|
+
*/
|
|
38
|
+
escalateFinding: (findingId: string, reason: string) => Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
export type SecurityFindingGateResult = {
|
|
41
|
+
passed: true;
|
|
42
|
+
findingId: null;
|
|
43
|
+
reason?: string;
|
|
44
|
+
} | {
|
|
45
|
+
passed: true;
|
|
46
|
+
findingId: string;
|
|
47
|
+
reason?: string;
|
|
48
|
+
} | {
|
|
49
|
+
passed: false;
|
|
50
|
+
findingId: string;
|
|
51
|
+
reason: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Evaluate the `security.finding_no_longer_present` deterministic check for
|
|
55
|
+
* a given injection node.
|
|
56
|
+
*
|
|
57
|
+
* Outcomes:
|
|
58
|
+
* - No finding linked to this injection -> passes as a no-op.
|
|
59
|
+
* - Linked finding has status 'resolved' -> passes.
|
|
60
|
+
* - Linked finding has any other status -> fails, returns the findingId
|
|
61
|
+
* so the caller can escalate.
|
|
62
|
+
*
|
|
63
|
+
* The function does NOT mutate any state -- escalation is the caller's
|
|
64
|
+
* responsibility via `escalateOnGateFail`.
|
|
65
|
+
*/
|
|
66
|
+
export declare function evaluateFindingNoLongerPresent(injectionNodeId: string, deps: SecurityFindingGateDeps): Promise<SecurityFindingGateResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Best-effort escalation helper: when the gate fails, flip the linked
|
|
69
|
+
* finding to status='escalated'. Logs and swallows on error so a failed
|
|
70
|
+
* escalation does not throw out of the verification path.
|
|
71
|
+
*/
|
|
72
|
+
export declare function escalateOnGateFail(findingId: string, reason: string, deps: SecurityFindingGateDeps): Promise<void>;
|
|
73
|
+
export declare function buildDefaultSecurityFindingGateDeps(): SecurityFindingGateDeps;
|
|
74
|
+
//# sourceMappingURL=security-finding-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-finding-gate.d.ts","sourceRoot":"","sources":["../src/security-finding-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAQH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,WAAW,CAAC;AAErF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,wBAAwB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IAC3F;;;OAGG;IACH,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED,MAAM,MAAM,yBAAyB,GACjC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAMzD;;;;;;;;;;;;GAYG;AACH,wBAAsB,8BAA8B,CAClD,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,yBAAyB,CAAC,CAapC;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAQf;AAMD,wBAAgB,mCAAmC,IAAI,uBAAuB,CAc7E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic check evaluator for the Security workflow verify-closed gate.
|
|
3
|
+
*
|
|
4
|
+
* The Security workflow declares its verify-closed stage with
|
|
5
|
+
* `agent_directive.gate = { kind: 'deterministic', check:
|
|
6
|
+
* 'security.finding_no_longer_present' }`. The gate's contract: a security
|
|
7
|
+
* remediation injection cannot be verified (retired) unless the linked
|
|
8
|
+
* security_findings row has flipped to status='resolved' (the re-scan
|
|
9
|
+
* resolution sweep clears the indicator).
|
|
10
|
+
*
|
|
11
|
+
* If the gate fails, the linked finding is flipped to status='escalated'
|
|
12
|
+
* with escalation_reason='verify_gate_failed'. After the D1 schema
|
|
13
|
+
* collapse, the Security workflow no longer carries a 'Reopen (failed)'
|
|
14
|
+
* transition — a failed verify-closed escalates and stays.
|
|
15
|
+
*
|
|
16
|
+
* Wired into verification-engine.ts: before calling verifyInjectionOnPass
|
|
17
|
+
* on a 'passed' outcome, the engine consults this gate. A 'passed' outcome
|
|
18
|
+
* is demoted to 'failed' when the finding is still open/remediating.
|
|
19
|
+
*
|
|
20
|
+
* @module security-finding-gate
|
|
21
|
+
*/
|
|
22
|
+
import { callApi } from './queries/shared.js';
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Pure evaluator
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Evaluate the `security.finding_no_longer_present` deterministic check for
|
|
28
|
+
* a given injection node.
|
|
29
|
+
*
|
|
30
|
+
* Outcomes:
|
|
31
|
+
* - No finding linked to this injection -> passes as a no-op.
|
|
32
|
+
* - Linked finding has status 'resolved' -> passes.
|
|
33
|
+
* - Linked finding has any other status -> fails, returns the findingId
|
|
34
|
+
* so the caller can escalate.
|
|
35
|
+
*
|
|
36
|
+
* The function does NOT mutate any state -- escalation is the caller's
|
|
37
|
+
* responsibility via `escalateOnGateFail`.
|
|
38
|
+
*/
|
|
39
|
+
export async function evaluateFindingNoLongerPresent(injectionNodeId, deps) {
|
|
40
|
+
const lookup = await deps.lookupFindingByInjection(injectionNodeId);
|
|
41
|
+
if (!lookup) {
|
|
42
|
+
return { passed: true, findingId: null, reason: 'no_linked_finding' };
|
|
43
|
+
}
|
|
44
|
+
if (lookup.status === 'resolved') {
|
|
45
|
+
return { passed: true, findingId: lookup.findingId, reason: 'finding_resolved' };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
passed: false,
|
|
49
|
+
findingId: lookup.findingId,
|
|
50
|
+
reason: `finding_status_is_${lookup.status}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Best-effort escalation helper: when the gate fails, flip the linked
|
|
55
|
+
* finding to status='escalated'. Logs and swallows on error so a failed
|
|
56
|
+
* escalation does not throw out of the verification path.
|
|
57
|
+
*/
|
|
58
|
+
export async function escalateOnGateFail(findingId, reason, deps) {
|
|
59
|
+
try {
|
|
60
|
+
await deps.escalateFinding(findingId, reason);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.warn(`[security-finding-gate] escalation failed for finding ${findingId}: ${err.message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Default deps -- daemon-side wiring via callApi
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
export function buildDefaultSecurityFindingGateDeps() {
|
|
70
|
+
return {
|
|
71
|
+
lookupFindingByInjection: async (injectionNodeId) => {
|
|
72
|
+
const res = await callApi('daemon_lookup_finding_by_injection', { injectionNodeId });
|
|
73
|
+
if (!res?.findingId || !res.status)
|
|
74
|
+
return null;
|
|
75
|
+
return { findingId: res.findingId, status: res.status };
|
|
76
|
+
},
|
|
77
|
+
escalateFinding: async (findingId, reason) => {
|
|
78
|
+
await callApi('daemon_escalate_security_finding', { findingId, reason });
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=security-finding-gate.js.map
|