@telora/daemon 0.15.36 → 0.15.40

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 (110) hide show
  1. package/build-info.json +2 -2
  2. package/dist/assembly-resolvers.d.ts +1 -1
  3. package/dist/assembly-resolvers.d.ts.map +1 -1
  4. package/dist/constants.d.ts +1 -1
  5. package/dist/constants.js +1 -1
  6. package/dist/delivery-lifecycle.d.ts +3 -0
  7. package/dist/delivery-lifecycle.d.ts.map +1 -1
  8. package/dist/delivery-lifecycle.js +13 -1
  9. package/dist/delivery-lifecycle.js.map +1 -1
  10. package/dist/drift-eval-loop.d.ts +63 -0
  11. package/dist/drift-eval-loop.d.ts.map +1 -0
  12. package/dist/drift-eval-loop.js +215 -0
  13. package/dist/drift-eval-loop.js.map +1 -0
  14. package/dist/drift-evaluator.d.ts +51 -0
  15. package/dist/drift-evaluator.d.ts.map +1 -0
  16. package/dist/drift-evaluator.js +62 -0
  17. package/dist/drift-evaluator.js.map +1 -0
  18. package/dist/feeds/ghsa.d.ts +88 -0
  19. package/dist/feeds/ghsa.d.ts.map +1 -0
  20. package/dist/feeds/ghsa.js +219 -0
  21. package/dist/feeds/ghsa.js.map +1 -0
  22. package/dist/feeds/local.d.ts +55 -0
  23. package/dist/feeds/local.d.ts.map +1 -0
  24. package/dist/feeds/local.js +196 -0
  25. package/dist/feeds/local.js.map +1 -0
  26. package/dist/feeds/osv.d.ts +89 -0
  27. package/dist/feeds/osv.d.ts.map +1 -0
  28. package/dist/feeds/osv.js +266 -0
  29. package/dist/feeds/osv.js.map +1 -0
  30. package/dist/focus-completion-event.d.ts +2 -0
  31. package/dist/focus-completion-event.d.ts.map +1 -1
  32. package/dist/focus-completion-event.js +51 -13
  33. package/dist/focus-completion-event.js.map +1 -1
  34. package/dist/focus-completion.d.ts +1 -1
  35. package/dist/focus-completion.js +7 -7
  36. package/dist/focus-completion.js.map +1 -1
  37. package/dist/focus-engine.d.ts.map +1 -1
  38. package/dist/focus-engine.js +71 -0
  39. package/dist/focus-engine.js.map +1 -1
  40. package/dist/focus-executor.d.ts +53 -0
  41. package/dist/focus-executor.d.ts.map +1 -1
  42. package/dist/focus-executor.js +41 -26
  43. package/dist/focus-executor.js.map +1 -1
  44. package/dist/focus-loop.d.ts +1 -1
  45. package/dist/focus-loop.js +2 -2
  46. package/dist/focus-loop.js.map +1 -1
  47. package/dist/focus-phase.js +1 -1
  48. package/dist/focus-phase.js.map +1 -1
  49. package/dist/focus-prompt-builder.d.ts +1 -1
  50. package/dist/focus-prompt-builder.js +1 -1
  51. package/dist/listener-auto-advance.d.ts +3 -3
  52. package/dist/listener-auto-advance.js +10 -10
  53. package/dist/listener-auto-advance.js.map +1 -1
  54. package/dist/listener.js +1 -1
  55. package/dist/listener.js.map +1 -1
  56. package/dist/queries/deliveries.d.ts +20 -1
  57. package/dist/queries/deliveries.d.ts.map +1 -1
  58. package/dist/queries/deliveries.js +8 -1
  59. package/dist/queries/deliveries.js.map +1 -1
  60. package/dist/queries/drift.d.ts +50 -0
  61. package/dist/queries/drift.d.ts.map +1 -0
  62. package/dist/queries/drift.js +28 -0
  63. package/dist/queries/drift.js.map +1 -0
  64. package/dist/queries/focuses.d.ts.map +1 -1
  65. package/dist/queries/focuses.js +1 -0
  66. package/dist/queries/focuses.js.map +1 -1
  67. package/dist/queries/index.d.ts +2 -0
  68. package/dist/queries/index.d.ts.map +1 -1
  69. package/dist/queries/index.js +1 -0
  70. package/dist/queries/index.js.map +1 -1
  71. package/dist/queries/schemas.d.ts +2 -0
  72. package/dist/queries/schemas.d.ts.map +1 -1
  73. package/dist/queries/schemas.js +1 -0
  74. package/dist/queries/schemas.js.map +1 -1
  75. package/dist/scanners/deps.d.ts +101 -0
  76. package/dist/scanners/deps.d.ts.map +1 -0
  77. package/dist/scanners/deps.js +242 -0
  78. package/dist/scanners/deps.js.map +1 -0
  79. package/dist/scanners/signatures.d.ts +44 -0
  80. package/dist/scanners/signatures.d.ts.map +1 -0
  81. package/dist/scanners/signatures.js +140 -0
  82. package/dist/scanners/signatures.js.map +1 -0
  83. package/dist/scanners/workflow.d.ts +34 -0
  84. package/dist/scanners/workflow.d.ts.map +1 -0
  85. package/dist/scanners/workflow.js +239 -0
  86. package/dist/scanners/workflow.js.map +1 -0
  87. package/dist/security-auto-inject.d.ts +114 -0
  88. package/dist/security-auto-inject.d.ts.map +1 -0
  89. package/dist/security-auto-inject.js +148 -0
  90. package/dist/security-auto-inject.js.map +1 -0
  91. package/dist/security-rescan-resolution.d.ts +84 -0
  92. package/dist/security-rescan-resolution.d.ts.map +1 -0
  93. package/dist/security-rescan-resolution.js +114 -0
  94. package/dist/security-rescan-resolution.js.map +1 -0
  95. package/dist/security-scan-engine.d.ts +96 -0
  96. package/dist/security-scan-engine.d.ts.map +1 -0
  97. package/dist/security-scan-engine.js +189 -0
  98. package/dist/security-scan-engine.js.map +1 -0
  99. package/dist/stage-classifier.d.ts +2 -2
  100. package/dist/stage-classifier.d.ts.map +1 -1
  101. package/dist/stage-classifier.js +7 -7
  102. package/dist/stage-classifier.js.map +1 -1
  103. package/dist/state-cascade.d.ts +1 -1
  104. package/dist/state-cascade.js +1 -1
  105. package/dist/team-prompt-base.d.ts.map +1 -1
  106. package/dist/team-prompt-base.js +15 -0
  107. package/dist/team-prompt-base.js.map +1 -1
  108. package/dist/types/focus.d.ts +6 -0
  109. package/dist/types/focus.d.ts.map +1 -1
  110. package/package.json +3 -2
@@ -0,0 +1,101 @@
1
+ /**
2
+ * npm-audit deps scanner for the security scan engine.
3
+ *
4
+ * Spawns `npm audit --json` in the product's repo path and maps the v7+
5
+ * audit output into FindingDraft rows for the 'deps' IOC class. npm audit
6
+ * exits non-zero whenever findings exist; that is normal behaviour, not
7
+ * an error. We only treat the run as failed when the JSON itself is
8
+ * unparseable.
9
+ *
10
+ * Severity mapping: npm uses {info, low, moderate, high, critical}; the
11
+ * security_findings schema uses {low, medium, high, critical}. We map
12
+ * `moderate` -> `medium` and skip `info` entries (advisory-only noise).
13
+ *
14
+ * @module scanners/deps
15
+ */
16
+ import type { Scanner, Severity } from '../security-scan-engine.js';
17
+ import { type OsvAdvisory } from '../feeds/osv.js';
18
+ import { type GhsaAdvisory } from '../feeds/ghsa.js';
19
+ import { type IocFeedEntry } from '../feeds/local.js';
20
+ type NpmSeverity = 'info' | 'low' | 'moderate' | 'high' | 'critical';
21
+ interface NpmAuditViaObject {
22
+ source?: number;
23
+ name?: string;
24
+ url?: string;
25
+ title?: string;
26
+ severity?: NpmSeverity;
27
+ cwe?: string[];
28
+ cvss?: {
29
+ score?: number;
30
+ vectorString?: string;
31
+ };
32
+ range?: string;
33
+ }
34
+ type NpmAuditVia = string | NpmAuditViaObject;
35
+ interface NpmAuditVulnerability {
36
+ name?: string;
37
+ severity?: NpmSeverity;
38
+ via?: NpmAuditVia[];
39
+ range?: string;
40
+ nodes?: string[];
41
+ fixAvailable?: boolean | {
42
+ name: string;
43
+ version: string;
44
+ isSemVerMajor: boolean;
45
+ };
46
+ }
47
+ interface NpmAuditOutput {
48
+ vulnerabilities?: Record<string, NpmAuditVulnerability>;
49
+ metadata?: {
50
+ vulnerabilities?: Record<NpmSeverity, number>;
51
+ dependencies?: Record<string, number> | {
52
+ total?: number;
53
+ };
54
+ };
55
+ }
56
+ export interface NpmAuditResult {
57
+ stdout: string;
58
+ exitCode: number;
59
+ }
60
+ export type NpmAuditRunner = (repoPath: string) => Promise<NpmAuditResult>;
61
+ declare function mapSeverity(s: NpmSeverity | undefined): Severity | null;
62
+ declare function parseAuditJson(raw: string): NpmAuditOutput;
63
+ export interface DepsScannerExternalDeps {
64
+ runNpmAudit: NpmAuditRunner;
65
+ /** Optional OSV adapter; default uses the real OSV.dev client. */
66
+ queryOsv?: (input: {
67
+ packages: Array<{
68
+ name: string;
69
+ version: string;
70
+ ecosystem: 'npm';
71
+ }>;
72
+ }) => Promise<{
73
+ advisories: OsvAdvisory[];
74
+ warnings: string[];
75
+ }>;
76
+ /** Optional GHSA adapter; default uses the real GHSA REST client. */
77
+ queryGhsa?: (input: {
78
+ packages: Array<{
79
+ name: string;
80
+ ecosystem: 'npm';
81
+ }>;
82
+ githubToken?: string;
83
+ }) => Promise<{
84
+ advisories: GhsaAdvisory[];
85
+ warnings: string[];
86
+ }>;
87
+ /** Optional local IOC feed loader; default reads from security_ioc_feed_entries. */
88
+ loadLocalIocFeed?: (organizationId: string, iocClass?: string) => Promise<IocFeedEntry[]>;
89
+ }
90
+ export declare function createDepsScanner(opts: DepsScannerExternalDeps): Scanner;
91
+ declare function computePackagesAudited(audit: NpmAuditOutput): number;
92
+ export declare const depsScanner: Scanner;
93
+ /** Test seam re-export for unit tests. */
94
+ export declare const _internal: {
95
+ defaultRunNpmAudit: NpmAuditRunner;
96
+ mapSeverity: typeof mapSeverity;
97
+ parseAuditJson: typeof parseAuditJson;
98
+ computePackagesAudited: typeof computePackagesAudited;
99
+ };
100
+ export {};
101
+ //# sourceMappingURL=deps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/scanners/deps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,242 @@
1
+ /**
2
+ * npm-audit deps scanner for the security scan engine.
3
+ *
4
+ * Spawns `npm audit --json` in the product's repo path and maps the v7+
5
+ * audit output into FindingDraft rows for the 'deps' IOC class. npm audit
6
+ * exits non-zero whenever findings exist; that is normal behaviour, not
7
+ * an error. We only treat the run as failed when the JSON itself is
8
+ * unparseable.
9
+ *
10
+ * Severity mapping: npm uses {info, low, moderate, high, critical}; the
11
+ * security_findings schema uses {low, medium, high, critical}. We map
12
+ * `moderate` -> `medium` and skip `info` entries (advisory-only noise).
13
+ *
14
+ * @module scanners/deps
15
+ */
16
+ import { spawn } from 'node:child_process';
17
+ import { queryOsv } from '../feeds/osv.js';
18
+ import { queryGhsa } from '../feeds/ghsa.js';
19
+ import { loadLocalIocFeed, matchesPattern } from '../feeds/local.js';
20
+ const defaultRunNpmAudit = (repoPath) => new Promise((resolve, reject) => {
21
+ const child = spawn('npm', ['audit', '--json'], {
22
+ cwd: repoPath,
23
+ stdio: ['ignore', 'pipe', 'pipe'],
24
+ });
25
+ let stdout = '';
26
+ child.stdout?.on('data', (chunk) => {
27
+ stdout += chunk.toString('utf8');
28
+ });
29
+ // stderr is discarded -- npm emits progress/warnings there that
30
+ // are not useful for the audit decision.
31
+ child.on('error', (err) => {
32
+ reject(err);
33
+ });
34
+ child.on('close', (code) => {
35
+ resolve({ stdout, exitCode: code ?? 0 });
36
+ });
37
+ });
38
+ // ---------------------------------------------------------------------------
39
+ // Severity mapping
40
+ // ---------------------------------------------------------------------------
41
+ function mapSeverity(s) {
42
+ switch (s) {
43
+ case 'low':
44
+ return 'low';
45
+ case 'moderate':
46
+ return 'medium';
47
+ case 'high':
48
+ return 'high';
49
+ case 'critical':
50
+ return 'critical';
51
+ case 'info':
52
+ case undefined:
53
+ default:
54
+ return null;
55
+ }
56
+ }
57
+ // ---------------------------------------------------------------------------
58
+ // JSON shape guard
59
+ // ---------------------------------------------------------------------------
60
+ function isRecord(value) {
61
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
62
+ }
63
+ function parseAuditJson(raw) {
64
+ const parsed = JSON.parse(raw);
65
+ if (!isRecord(parsed)) {
66
+ throw new Error('npm audit JSON: top-level value is not an object');
67
+ }
68
+ // Shallow validation -- we treat unknown fields as opaque payload data
69
+ // rather than failing the scan on schema drift.
70
+ return parsed;
71
+ }
72
+ function extractPackagesFromVulns(vulns) {
73
+ // npm audit doesn't include a version in vulnerabilities, just a range.
74
+ // We use the range as a coarse 'version' for OSV's query -- OSV's batch
75
+ // tolerates ranges via an empty version when set carefully, but to keep
76
+ // the contract simple we pass the leftmost concrete version when we can
77
+ // extract one, otherwise '0.0.0' so OSV returns no match (and we still
78
+ // surface the npm-audit finding without enrichment).
79
+ const out = [];
80
+ for (const [name, advisory] of Object.entries(vulns)) {
81
+ const range = typeof advisory.range === 'string' ? advisory.range : '';
82
+ const version = range.match(/\d+\.\d+\.\d+/)?.[0] ?? '0.0.0';
83
+ out.push({ name, version });
84
+ }
85
+ return out;
86
+ }
87
+ export function createDepsScanner(opts) {
88
+ const osv = opts.queryOsv ?? ((input) => queryOsv({ packages: input.packages }));
89
+ const ghsa = opts.queryGhsa ?? ((input) => queryGhsa({ packages: input.packages, githubToken: input.githubToken }));
90
+ const local = opts.loadLocalIocFeed ?? loadLocalIocFeed;
91
+ return {
92
+ iocClass: 'deps',
93
+ async scan(ctx) {
94
+ const { stdout, exitCode } = await opts.runNpmAudit(ctx.repoPath);
95
+ let audit;
96
+ try {
97
+ audit = parseAuditJson(stdout);
98
+ }
99
+ catch (err) {
100
+ // Non-zero exit with empty/invalid JSON -- the scanner cannot
101
+ // distinguish "no vulnerabilities" from "npm crashed". Surface
102
+ // the failure to the engine.
103
+ throw new Error(`npm audit JSON parse failed (exit ${exitCode}): ${err.message}`);
104
+ }
105
+ const vulns = audit.vulnerabilities ?? {};
106
+ const warnings = [];
107
+ // Cross-reference -- OSV + GHSA + local IOC feed, all resilient.
108
+ const packagesForFeed = extractPackagesFromVulns(vulns);
109
+ const [osvResult, ghsaResult, localEntries] = await Promise.all([
110
+ osv({ packages: packagesForFeed.map((p) => ({ ...p, ecosystem: 'npm' })) }).catch((err) => ({
111
+ advisories: [],
112
+ warnings: [`osv: ${err.message}`],
113
+ })),
114
+ ghsa({ packages: packagesForFeed.map((p) => ({ name: p.name, ecosystem: 'npm' })) }).catch((err) => ({ advisories: [], warnings: [`ghsa: ${err.message}`] })),
115
+ local(ctx.config.organizationId, 'deps').catch((err) => {
116
+ warnings.push(`local-ioc-feed: ${err.message}`);
117
+ return [];
118
+ }),
119
+ ]);
120
+ warnings.push(...osvResult.warnings, ...ghsaResult.warnings);
121
+ const osvByPackage = new Map();
122
+ for (const adv of osvResult.advisories) {
123
+ for (const a of adv.affected) {
124
+ if (a.package.ecosystem.toLowerCase() === 'npm') {
125
+ const list = osvByPackage.get(a.package.name) ?? [];
126
+ list.push(adv);
127
+ osvByPackage.set(a.package.name, list);
128
+ }
129
+ }
130
+ }
131
+ const ghsaByPackage = new Map();
132
+ for (const adv of ghsaResult.advisories) {
133
+ for (const v of adv.vulnerabilities) {
134
+ if (v.package.ecosystem.toLowerCase() === 'npm') {
135
+ const list = ghsaByPackage.get(v.package.name) ?? [];
136
+ list.push(adv);
137
+ ghsaByPackage.set(v.package.name, list);
138
+ }
139
+ }
140
+ }
141
+ // Deduplicate findings across npm-audit + local-ioc by (identifier).
142
+ const findings = [];
143
+ const seen = new Set();
144
+ for (const [packageName, advisory] of Object.entries(vulns)) {
145
+ const severity = mapSeverity(advisory.severity);
146
+ if (severity === null)
147
+ continue;
148
+ const range = typeof advisory.range === 'string' ? advisory.range : '*';
149
+ const identifier = `${packageName}@${range}`;
150
+ if (seen.has(identifier))
151
+ continue;
152
+ seen.add(identifier);
153
+ const payload = {
154
+ name: packageName,
155
+ severity: advisory.severity,
156
+ via: advisory.via ?? [],
157
+ range,
158
+ nodes: advisory.nodes ?? [],
159
+ fixAvailable: advisory.fixAvailable ?? false,
160
+ osv: osvByPackage.get(packageName) ?? [],
161
+ ghsa: ghsaByPackage.get(packageName) ?? [],
162
+ };
163
+ findings.push({ iocClass: 'deps', severity, identifier, payload });
164
+ }
165
+ // Local IOC entries with ioc_class='deps' add findings even if
166
+ // upstream audit/feeds are silent. Pattern is matched against
167
+ // each audited package's "name@range".
168
+ for (const entry of localEntries) {
169
+ if (entry.iocClass !== 'deps')
170
+ continue;
171
+ for (const [name, advisory] of Object.entries(vulns)) {
172
+ const range = typeof advisory.range === 'string' ? advisory.range : '*';
173
+ const candidate = `${name}@${range}`;
174
+ if (!matchesPattern(candidate, entry))
175
+ continue;
176
+ const identifier = `${candidate}#ioc:${entry.id}`;
177
+ if (seen.has(identifier))
178
+ continue;
179
+ seen.add(identifier);
180
+ findings.push({
181
+ iocClass: 'deps',
182
+ severity: entry.severity,
183
+ identifier,
184
+ payload: {
185
+ source: 'local_ioc_feed',
186
+ entry_id: entry.id,
187
+ pattern_type: entry.patternType,
188
+ pattern: entry.pattern,
189
+ advisory_text: entry.advisoryText,
190
+ matched: candidate,
191
+ },
192
+ });
193
+ }
194
+ }
195
+ const packagesAudited = computePackagesAudited(audit);
196
+ const coverage = {
197
+ packages_audited: packagesAudited,
198
+ advisories_returned: Object.keys(vulns).length,
199
+ npm_audit_exit_code: exitCode,
200
+ osv_advisories: osvResult.advisories.length,
201
+ ghsa_advisories: ghsaResult.advisories.length,
202
+ local_ioc_entries: localEntries.length,
203
+ };
204
+ if (warnings.length > 0) {
205
+ coverage.warnings = warnings;
206
+ }
207
+ return { findings, coverage };
208
+ },
209
+ };
210
+ }
211
+ function computePackagesAudited(audit) {
212
+ const deps = audit.metadata?.dependencies;
213
+ if (!deps)
214
+ return 0;
215
+ if (typeof deps === 'object' && 'total' in deps && typeof deps.total === 'number') {
216
+ return deps.total;
217
+ }
218
+ // Some npm versions emit a flat record of counts; sum the numeric values.
219
+ if (isRecord(deps)) {
220
+ let total = 0;
221
+ for (const v of Object.values(deps)) {
222
+ if (typeof v === 'number')
223
+ total += v;
224
+ }
225
+ return total;
226
+ }
227
+ return 0;
228
+ }
229
+ // ---------------------------------------------------------------------------
230
+ // Default export -- real spawn-backed scanner registered by the engine.
231
+ // ---------------------------------------------------------------------------
232
+ export const depsScanner = createDepsScanner({
233
+ runNpmAudit: defaultRunNpmAudit,
234
+ });
235
+ /** Test seam re-export for unit tests. */
236
+ export const _internal = {
237
+ defaultRunNpmAudit,
238
+ mapSeverity,
239
+ parseAuditJson,
240
+ computePackagesAudited,
241
+ };
242
+ //# sourceMappingURL=deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/scanners/deps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * npm audit signatures scanner -- security scan engine plugin.
3
+ *
4
+ * Runs `npm audit signatures --json` against the product's repo and converts
5
+ * each unverified or invalid-signature entry into a FindingDraft. Signature
6
+ * verification has no npm-provided severity tier, so every finding is marked
7
+ * 'medium' -- a signature problem is always actionable, but not on its own a
8
+ * critical-severity issue without further context.
9
+ *
10
+ * Pattern reference: verification-engine.ts (dep-injection seam for spawn).
11
+ *
12
+ * @module scanners/signatures
13
+ */
14
+ import type { Scanner } from '../security-scan-engine.js';
15
+ export interface NpmAuditSignaturesResult {
16
+ stdout: string;
17
+ stderr: string;
18
+ exitCode: number | null;
19
+ }
20
+ export type RunNpmAuditSignatures = (cwd: string) => Promise<NpmAuditSignaturesResult>;
21
+ interface SignatureEntry {
22
+ name: string;
23
+ version: string;
24
+ raw: Record<string, unknown>;
25
+ reason: string;
26
+ }
27
+ /**
28
+ * Parse the npm audit signatures JSON envelope. Tolerates shape variations
29
+ * across npm versions -- `invalid` and `missing` may be absent or empty.
30
+ * Throws if the payload is not valid JSON or not an object.
31
+ */
32
+ export declare function parseSignaturesReport(stdout: string): {
33
+ invalid: SignatureEntry[];
34
+ missing: SignatureEntry[];
35
+ totalChecked: number;
36
+ };
37
+ export interface SignaturesScannerDeps {
38
+ runNpmAuditSignatures: RunNpmAuditSignatures;
39
+ }
40
+ export declare function createSignaturesScanner(deps: SignaturesScannerDeps): Scanner;
41
+ /** Default scanner wired to the real npm CLI. */
42
+ export declare const signaturesScanner: Scanner;
43
+ export {};
44
+ //# sourceMappingURL=signatures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signatures.d.ts","sourceRoot":"","sources":["../../src/scanners/signatures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAIV,OAAO,EACR,MAAM,4BAA4B,CAAC;AAMpC,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;AAwCvF,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAiCD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG;IACrD,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CA4BA;AAMD,MAAM,WAAW,qBAAqB;IACpC,qBAAqB,EAAE,qBAAqB,CAAC;CAC9C;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAiC5E;AAED,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,EAAE,OAE9B,CAAC"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * npm audit signatures scanner -- security scan engine plugin.
3
+ *
4
+ * Runs `npm audit signatures --json` against the product's repo and converts
5
+ * each unverified or invalid-signature entry into a FindingDraft. Signature
6
+ * verification has no npm-provided severity tier, so every finding is marked
7
+ * 'medium' -- a signature problem is always actionable, but not on its own a
8
+ * critical-severity issue without further context.
9
+ *
10
+ * Pattern reference: verification-engine.ts (dep-injection seam for spawn).
11
+ *
12
+ * @module scanners/signatures
13
+ */
14
+ import { spawn } from 'node:child_process';
15
+ const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
16
+ /** Default implementation: spawn the real npm CLI in the repo. */
17
+ const defaultRunNpmAuditSignatures = (cwd) => new Promise((resolve, reject) => {
18
+ const child = spawn('npm', ['audit', 'signatures', '--json'], {
19
+ cwd,
20
+ stdio: ['ignore', 'pipe', 'pipe'],
21
+ });
22
+ let stdout = '';
23
+ let stderr = '';
24
+ child.stdout?.on('data', (chunk) => {
25
+ stdout += chunk.toString('utf8');
26
+ });
27
+ child.stderr?.on('data', (chunk) => {
28
+ stderr += chunk.toString('utf8');
29
+ });
30
+ const timer = setTimeout(() => {
31
+ try {
32
+ child.kill('SIGKILL');
33
+ }
34
+ catch { /* ignore */ }
35
+ }, DEFAULT_TIMEOUT_MS);
36
+ child.on('error', (err) => {
37
+ clearTimeout(timer);
38
+ reject(err);
39
+ });
40
+ child.on('close', (code) => {
41
+ clearTimeout(timer);
42
+ resolve({ stdout, stderr, exitCode: code });
43
+ });
44
+ });
45
+ function isRecord(value) {
46
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
47
+ }
48
+ function stringField(record, key) {
49
+ const v = record[key];
50
+ return typeof v === 'string' ? v : undefined;
51
+ }
52
+ function numberField(record, key) {
53
+ const v = record[key];
54
+ return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
55
+ }
56
+ function entriesFromArray(value, fallbackReason) {
57
+ if (!Array.isArray(value))
58
+ return [];
59
+ const out = [];
60
+ for (const item of value) {
61
+ if (!isRecord(item))
62
+ continue;
63
+ const name = stringField(item, 'name');
64
+ const version = stringField(item, 'version');
65
+ if (!name || !version)
66
+ continue;
67
+ const reason = stringField(item, 'reason') ?? fallbackReason;
68
+ out.push({ name, version, raw: item, reason });
69
+ }
70
+ return out;
71
+ }
72
+ /**
73
+ * Parse the npm audit signatures JSON envelope. Tolerates shape variations
74
+ * across npm versions -- `invalid` and `missing` may be absent or empty.
75
+ * Throws if the payload is not valid JSON or not an object.
76
+ */
77
+ export function parseSignaturesReport(stdout) {
78
+ const trimmed = stdout.trim();
79
+ if (trimmed.length === 0) {
80
+ throw new Error('npm audit signatures produced empty output');
81
+ }
82
+ let parsed;
83
+ try {
84
+ parsed = JSON.parse(trimmed);
85
+ }
86
+ catch (err) {
87
+ throw new Error(`failed to parse npm audit signatures JSON: ${err.message}`);
88
+ }
89
+ if (!isRecord(parsed)) {
90
+ throw new Error('npm audit signatures JSON root is not an object');
91
+ }
92
+ const invalid = entriesFromArray(parsed.invalid, 'invalid_signature');
93
+ const missing = entriesFromArray(parsed.missing, 'missing');
94
+ // Best-effort total. npm has shipped a few different field names over the
95
+ // years (`packagesAudited`, `count`, etc.). Fall back to invalid+missing
96
+ // when nothing usable is reported.
97
+ const totalChecked = numberField(parsed, 'packagesAudited') ??
98
+ numberField(parsed, 'totalChecked') ??
99
+ (isRecord(parsed.count) ? numberField(parsed.count, 'total') : undefined) ??
100
+ invalid.length + missing.length;
101
+ return { invalid, missing, totalChecked };
102
+ }
103
+ export function createSignaturesScanner(deps) {
104
+ return {
105
+ iocClass: 'signatures',
106
+ async scan(ctx) {
107
+ const { stdout, exitCode } = await deps.runNpmAuditSignatures(ctx.repoPath);
108
+ const report = parseSignaturesReport(stdout);
109
+ const findings = [];
110
+ const emit = (entry) => {
111
+ findings.push({
112
+ iocClass: 'signatures',
113
+ severity: 'medium',
114
+ identifier: `${entry.name}@${entry.version}`,
115
+ payload: {
116
+ ...entry.raw,
117
+ reason: entry.reason,
118
+ },
119
+ });
120
+ };
121
+ for (const entry of report.invalid)
122
+ emit(entry);
123
+ for (const entry of report.missing)
124
+ emit(entry);
125
+ const coverage = {
126
+ packages_checked: report.totalChecked,
127
+ unverified: report.invalid.length + report.missing.length,
128
+ missing: report.missing.length,
129
+ invalid: report.invalid.length,
130
+ npm_exit_code: exitCode,
131
+ };
132
+ return { findings, coverage };
133
+ },
134
+ };
135
+ }
136
+ /** Default scanner wired to the real npm CLI. */
137
+ export const signaturesScanner = createSignaturesScanner({
138
+ runNpmAuditSignatures: defaultRunNpmAuditSignatures,
139
+ });
140
+ //# sourceMappingURL=signatures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signatures.js","sourceRoot":"","sources":["../../src/scanners/signatures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * GitHub Actions workflow scanner -- security-scan engine plugin.
3
+ *
4
+ * Detects the `pull_request_target` + checkout-head + cache-write pattern
5
+ * that lets a fork PR's untrusted code execute with the base repo's
6
+ * elevated permissions and secrets, and persists artifacts (caches)
7
+ * back to the base repo. This is the canonical "pwn request" supply
8
+ * chain vulnerability.
9
+ *
10
+ * Matched when ALL three are true for a single job:
11
+ * 1. Workflow `on:` includes `pull_request_target` (string | array | object).
12
+ * 2. The job contains an `actions/checkout@*` step whose `with.ref` is
13
+ * one of `${{ github.event.pull_request.head.sha }}` or
14
+ * `${{ github.event.pull_request.head.ref }}`.
15
+ * 3. The job contains a cache-write step:
16
+ * - `actions/cache@*` (any non-`restore`-suffixed action), or
17
+ * - `actions/setup-*` step with `with.cache: true` (or any truthy
18
+ * string), or
19
+ * - any step after the head-ref checkout whose `with.cache` is truthy.
20
+ *
21
+ * Pattern reference: verification-engine.ts (Deps + default factory).
22
+ *
23
+ * @module scanners/workflow
24
+ */
25
+ import type { Scanner } from '../security-scan-engine.js';
26
+ export interface WorkflowScannerDeps {
27
+ /** List workflow files in the repo's `.github/workflows/` directory. */
28
+ readWorkflowsDir: (repoPath: string) => Promise<string[]>;
29
+ /** Read a workflow file's contents as UTF-8. */
30
+ readFile: (path: string) => Promise<string>;
31
+ }
32
+ export declare function createWorkflowScanner(deps?: WorkflowScannerDeps): Scanner;
33
+ export declare const workflowScanner: Scanner;
34
+ //# sourceMappingURL=workflow.d.ts.map
@@ -0,0 +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;AAMjG,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;CAC7C;AAmKD,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,mBAAiC,GAAG,OAAO,CA4DtF;AAWD,eAAO,MAAM,eAAe,EAAE,OAAiC,CAAC"}