@xenos1996/usa 2.27.0 → 2.28.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 (82) hide show
  1. package/dist/cli-help.d.ts.map +1 -1
  2. package/dist/cli-help.js +2 -0
  3. package/dist/cli-help.js.map +1 -1
  4. package/dist/cli-intent.d.ts +4 -0
  5. package/dist/cli-intent.d.ts.map +1 -0
  6. package/dist/cli-intent.js +108 -0
  7. package/dist/cli-intent.js.map +1 -0
  8. package/dist/cli-scan.d.ts +12 -0
  9. package/dist/cli-scan.d.ts.map +1 -0
  10. package/dist/cli-scan.js +142 -0
  11. package/dist/cli-scan.js.map +1 -0
  12. package/dist/cli.d.ts.map +1 -1
  13. package/dist/cli.js +4 -0
  14. package/dist/cli.js.map +1 -1
  15. package/dist/engine/evaluate.d.ts.map +1 -1
  16. package/dist/engine/evaluate.js +31 -8
  17. package/dist/engine/evaluate.js.map +1 -1
  18. package/dist/intent/match.d.ts +38 -0
  19. package/dist/intent/match.d.ts.map +1 -0
  20. package/dist/intent/match.js +108 -0
  21. package/dist/intent/match.js.map +1 -0
  22. package/dist/intent/otm.d.ts +16 -0
  23. package/dist/intent/otm.d.ts.map +1 -0
  24. package/dist/intent/otm.js +144 -0
  25. package/dist/intent/otm.js.map +1 -0
  26. package/dist/intent/types.d.ts +111 -0
  27. package/dist/intent/types.d.ts.map +1 -0
  28. package/dist/intent/types.js +16 -0
  29. package/dist/intent/types.js.map +1 -0
  30. package/dist/scanner/adapters/codeql.d.ts +14 -0
  31. package/dist/scanner/adapters/codeql.d.ts.map +1 -0
  32. package/dist/scanner/adapters/codeql.js +134 -0
  33. package/dist/scanner/adapters/codeql.js.map +1 -0
  34. package/dist/scanner/adapters/dependency-cruiser.d.ts +3 -0
  35. package/dist/scanner/adapters/dependency-cruiser.d.ts.map +1 -0
  36. package/dist/scanner/adapters/dependency-cruiser.js +174 -0
  37. package/dist/scanner/adapters/dependency-cruiser.js.map +1 -0
  38. package/dist/scanner/adapters/ort.d.ts +12 -0
  39. package/dist/scanner/adapters/ort.d.ts.map +1 -0
  40. package/dist/scanner/adapters/ort.js +259 -0
  41. package/dist/scanner/adapters/ort.js.map +1 -0
  42. package/dist/scanner/adapters/scorecard.d.ts +7 -0
  43. package/dist/scanner/adapters/scorecard.d.ts.map +1 -0
  44. package/dist/scanner/adapters/scorecard.js +209 -0
  45. package/dist/scanner/adapters/scorecard.js.map +1 -0
  46. package/dist/scanner/adapters/semgrep.d.ts +3 -0
  47. package/dist/scanner/adapters/semgrep.d.ts.map +1 -0
  48. package/dist/scanner/adapters/semgrep.js +163 -0
  49. package/dist/scanner/adapters/semgrep.js.map +1 -0
  50. package/dist/scanner/adapters/trivy.d.ts +3 -0
  51. package/dist/scanner/adapters/trivy.d.ts.map +1 -0
  52. package/dist/scanner/adapters/trivy.js +163 -0
  53. package/dist/scanner/adapters/trivy.js.map +1 -0
  54. package/dist/scanner/config.d.ts +4 -0
  55. package/dist/scanner/config.d.ts.map +1 -0
  56. package/dist/scanner/config.js +15 -0
  57. package/dist/scanner/config.js.map +1 -0
  58. package/dist/scanner/identity.d.ts +17 -0
  59. package/dist/scanner/identity.d.ts.map +1 -0
  60. package/dist/scanner/identity.js +50 -0
  61. package/dist/scanner/identity.js.map +1 -0
  62. package/dist/scanner/normalize.d.ts +40 -0
  63. package/dist/scanner/normalize.d.ts.map +1 -0
  64. package/dist/scanner/normalize.js +85 -0
  65. package/dist/scanner/normalize.js.map +1 -0
  66. package/dist/scanner/registry.d.ts +19 -0
  67. package/dist/scanner/registry.d.ts.map +1 -0
  68. package/dist/scanner/registry.js +43 -0
  69. package/dist/scanner/registry.js.map +1 -0
  70. package/dist/scanner/run.d.ts +70 -0
  71. package/dist/scanner/run.d.ts.map +1 -0
  72. package/dist/scanner/run.js +419 -0
  73. package/dist/scanner/run.js.map +1 -0
  74. package/dist/scanner/sarif.d.ts +21 -0
  75. package/dist/scanner/sarif.d.ts.map +1 -0
  76. package/dist/scanner/sarif.js +156 -0
  77. package/dist/scanner/sarif.js.map +1 -0
  78. package/dist/scanner/types.d.ts +236 -0
  79. package/dist/scanner/types.d.ts.map +1 -0
  80. package/dist/scanner/types.js +11 -0
  81. package/dist/scanner/types.js.map +1 -0
  82. package/package.json +1 -1
@@ -0,0 +1,209 @@
1
+ /**
2
+ * OpenSSF Scorecard — Tier 2.
3
+ *
4
+ * Two modes, because the tool has two natures and they are not interchangeable:
5
+ *
6
+ * - `--local <folder>` reads a checkout and runs the **file-based** checks
7
+ * offline. Verified here (v5.6.0): SAST, License, Pinned-Dependencies,
8
+ * Dependency-Update-Tool, Binary-Artifacts, Fuzzing, Packaging.
9
+ * - `--repo <url>` reaches the platform API and runs the **platform** checks
10
+ * (Branch-Protection, Token-Permissions, Signed-Releases, Code-Review, …).
11
+ * It needs network and a token.
12
+ *
13
+ * Measured in local mode, the platform checks do not degrade to `?` — they make
14
+ * the whole run fail (`Unsupported RequestType [0] by check: Branch-Protection`).
15
+ * So the check list is explicit in local mode; leaving it off is a crash, not a
16
+ * partial result.
17
+ *
18
+ * The aggregate score is deliberately NOT emitted as evidence. Scorecard's own
19
+ * README says the aggregate "tells you nothing about what individual behaviors a
20
+ * repository is or is not doing"; a per-check reason is evidence, a 7.5/10 is a
21
+ * headline.
22
+ */
23
+ import { spawnSync } from 'node:child_process';
24
+ import { statSync } from 'node:fs';
25
+ import path from 'node:path';
26
+ import { buildEvidence } from '../identity.js';
27
+ import { DETERMINISTIC_CONFIDENCE, dig, tryParseJson } from '../normalize.js';
28
+ /** The file-based checks that survive `--local` (verified against v5.6.0). */
29
+ export const LOCAL_CHECKS = [
30
+ 'SAST',
31
+ 'License',
32
+ 'Pinned-Dependencies',
33
+ 'Dependency-Update-Tool',
34
+ 'Binary-Artifacts',
35
+ 'Fuzzing',
36
+ 'Packaging',
37
+ ];
38
+ export const scorecardAdapter = {
39
+ id: 'scorecard',
40
+ name: 'OpenSSF Scorecard repository posture probes',
41
+ /** Only the repo mode reaches the network; local mode is fully offline. */
42
+ needsNetwork: false,
43
+ defaultConfig() {
44
+ return { mode: 'local', checks: LOCAL_CHECKS };
45
+ },
46
+ probe() {
47
+ const executable = which('scorecard');
48
+ if (!executable) {
49
+ return {
50
+ available: false,
51
+ version: '',
52
+ reason: 'scorecard executable not found on PATH (release binary from ossf/scorecard)',
53
+ };
54
+ }
55
+ return { available: true, version: versionOf(executable), executable };
56
+ },
57
+ buildCommand(target, config) {
58
+ const mode = String(config.mode ?? 'local');
59
+ const checks = Array.isArray(config.checks) ? config.checks : LOCAL_CHECKS;
60
+ if (mode === 'repo') {
61
+ const repo = String(config.repo ?? '') || deriveRepoSlug(target.path);
62
+ return {
63
+ argv: ['scorecard', '--repo', repo, '--format', 'json', '--show-details'],
64
+ timeoutMs: 300_000,
65
+ network: true,
66
+ };
67
+ }
68
+ return {
69
+ argv: [
70
+ 'scorecard',
71
+ '--local',
72
+ target.workdir,
73
+ '--format',
74
+ 'json',
75
+ '--checks',
76
+ checks.join(','),
77
+ ],
78
+ timeoutMs: 300_000,
79
+ };
80
+ },
81
+ parse(raw, context) {
82
+ const parsed = tryParseJson(raw);
83
+ if (!parsed.ok) {
84
+ return [unknown(context, `scorecard output was not valid JSON: ${parsed.error}`)];
85
+ }
86
+ const checks = dig(parsed.value, ['checks']);
87
+ if (!Array.isArray(checks)) {
88
+ return [unknown(context, 'scorecard JSON carried no checks array')];
89
+ }
90
+ const toolVersion = String(dig(parsed.value, ['scorecard', 'version']) ?? context.adapterVersion);
91
+ return checks.flatMap((check, index) => checkToEvidence(check, index, toolVersion));
92
+ },
93
+ };
94
+ /** One probe → one evidence row carrying the tool's own reason. */
95
+ function checkToEvidence(check, index, toolVersion) {
96
+ const name = String(dig(check, ['name']) ?? 'unknown-check');
97
+ const score = Number(dig(check, ['score']));
98
+ const reason = String(dig(check, ['reason']) ?? 'no reason given');
99
+ const docUrl = String(dig(check, ['documentation', 'url']) ?? '');
100
+ const source = {
101
+ tool: 'scorecard',
102
+ kind: 'posture-probe',
103
+ version: toolVersion,
104
+ identifier: name,
105
+ };
106
+ // A negative score is scorecard's "could not evaluate" — UNKNOWN, never a fail
107
+ // and never a pass. The reason string says why, and that is the evidence.
108
+ if (!Number.isFinite(score) || score < 0) {
109
+ return [
110
+ buildEvidence({
111
+ source,
112
+ provenance: placeholder(),
113
+ claim: `${name}: could not be evaluated — ${reason}`,
114
+ severity: 'INFO',
115
+ confidence: DETERMINISTIC_CONFIDENCE,
116
+ status: 'UNKNOWN',
117
+ location: null,
118
+ rawReference: { jsonPath: `checks[${index}]`, excerpt: reason.slice(0, 160) },
119
+ }),
120
+ ];
121
+ }
122
+ // Scorecard's 0–10 is a scale, not a verdict, and the same number means
123
+ // different things per probe: License 9 is "detected, not identified" while
124
+ // Binary-Artifacts 6 is "binaries present". Collapsing either to FAIL loses
125
+ // the distinction and collapsing to PASS launders the caveat, so the adapter
126
+ // emits PARTIAL and carries the number in the claim. A rule decides what
127
+ // threshold means a failure — that is a policy question, not a parsing one.
128
+ const status = score >= 10 ? 'PASS' : 'PARTIAL';
129
+ return [
130
+ buildEvidence({
131
+ source,
132
+ provenance: placeholder(),
133
+ claim: `${name} (${score}/10): ${reason}`,
134
+ severity: status === 'PASS' ? 'INFO' : 'MEDIUM',
135
+ confidence: DETERMINISTIC_CONFIDENCE,
136
+ status,
137
+ location: null,
138
+ rawReference: { jsonPath: `checks[${index}]`, excerpt: docUrl.slice(0, 160) },
139
+ }),
140
+ ];
141
+ }
142
+ function unknown(context, reason) {
143
+ return {
144
+ id: `UNKNOWN-scorecard-${reason}`.replace(/[^A-Za-z0-9-]/g, '-').slice(0, 48),
145
+ source: {
146
+ tool: 'scorecard',
147
+ version: context.adapterVersion,
148
+ kind: 'check',
149
+ identifier: 'UNKNOWN',
150
+ },
151
+ provenance: placeholder(),
152
+ claim: reason,
153
+ severity: 'INFO',
154
+ severitySource: 'adapter-default',
155
+ confidence: DETERMINISTIC_CONFIDENCE,
156
+ status: 'UNKNOWN',
157
+ location: null,
158
+ rawReference: {},
159
+ };
160
+ }
161
+ function placeholder() {
162
+ return {
163
+ executionId: '',
164
+ command: [],
165
+ config: {},
166
+ repositoryRevision: '',
167
+ timestamp: '',
168
+ executable: '',
169
+ timeoutMs: 0,
170
+ elapsedMs: 0,
171
+ };
172
+ }
173
+ /** `git@github.com:org/repo.git` or `https://github.com/org/repo.git` → `org/repo`. */
174
+ export function deriveRepoSlug(targetPath) {
175
+ const proc = spawnSync('git', ['remote', 'get-url', 'origin'], {
176
+ cwd: targetPath,
177
+ encoding: 'utf8',
178
+ timeout: 15_000,
179
+ });
180
+ const url = String(proc.stdout ?? '').trim();
181
+ const match = /github\.com[:/]([\w.-]+\/[\w.-]+?)(?:\.git)?\/?$/.exec(url);
182
+ return match?.[1] ?? '';
183
+ }
184
+ function which(binary) {
185
+ for (const dir of (process.env.PATH ?? '').split(path.delimiter)) {
186
+ if (!dir)
187
+ continue;
188
+ const candidate = path.join(dir, binary);
189
+ try {
190
+ if (statSync(candidate).isFile())
191
+ return candidate;
192
+ }
193
+ catch {
194
+ /* keep looking */
195
+ }
196
+ }
197
+ return null;
198
+ }
199
+ /**
200
+ * `scorecard version` prints an ASCII-art banner before the number, so the
201
+ * pattern demands a full semver — a loose `[\d.]+` matches the art instead.
202
+ * The JSON report also carries the version, and the parser prefers that one.
203
+ */
204
+ function versionOf(executable) {
205
+ const proc = spawnSync(executable, ['version'], { encoding: 'utf8', timeout: 20_000 });
206
+ const match = /\bv?(\d+\.\d+\.\d+)\b/.exec(String(proc.stdout ?? proc.stderr ?? ''));
207
+ return match?.[1] ?? 'unknown';
208
+ }
209
+ //# sourceMappingURL=scorecard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scorecard.js","sourceRoot":"","sources":["../../../src/scanner/adapters/scorecard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ScannerAdapter } from '../types.js';
2
+ export declare const semgrepAdapter: ScannerAdapter;
3
+ //# sourceMappingURL=semgrep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semgrep.d.ts","sourceRoot":"","sources":["../../../src/scanner/adapters/semgrep.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAA0B,cAAc,EAAiB,MAAM,aAAa,CAAC;AAWzF,eAAO,MAAM,cAAc,EAAE,cAmF5B,CAAC"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Semgrep — Tier 1.
3
+ *
4
+ * Verified by installing semgrep 1.182.0 and running it against
5
+ * `fixtures/insecure-app` with a vendored rule; the committed fixture is a real
6
+ * capture. Two measured details shape this adapter: `path` is absolute when the
7
+ * target is absolute (so it is relativised to the scan root), and
8
+ * `extra.fingerprint` is the literal string "requires login" on the OSS engine
9
+ * without an account (so the evidence id is content-derived, never the
10
+ * fingerprint).
11
+ *
12
+ * Exit-code behaviour is verified from the CLI reference: default `scan`
13
+ * **exits 0 even with findings**; only `--error` exits 1. The runner never
14
+ * infers a verdict from the status code, so this does not affect semantics —
15
+ * but it is why the adapter does not pass `--error`.
16
+ */
17
+ import { spawnSync } from 'node:child_process';
18
+ import { statSync } from 'node:fs';
19
+ import path from 'node:path';
20
+ import { buildEvidence } from '../identity.js';
21
+ import { DETERMINISTIC_CONFIDENCE, dig, lineNumber, relativePath, severity, tryParseJson, } from '../normalize.js';
22
+ export const semgrepAdapter = {
23
+ id: 'semgrep',
24
+ name: 'Semgrep SAST scanner',
25
+ networkConfigKey: 'scanners.semgrep.rules',
26
+ defaultConfig() {
27
+ return { rules: '', severity: [] };
28
+ },
29
+ probe() {
30
+ const executable = which('semgrep');
31
+ if (!executable) {
32
+ return { available: false, version: '', reason: 'semgrep executable not found on PATH' };
33
+ }
34
+ return { available: true, version: versionOf(executable), executable };
35
+ },
36
+ buildCommand(_target, config) {
37
+ const rules = String(config.rules ?? '');
38
+ const argv = [
39
+ 'semgrep',
40
+ 'scan',
41
+ '--json',
42
+ // No --error: exit status is not a verdict, and 1-on-findings would look
43
+ // like a crash to any later naive consumer.
44
+ ...(rules ? ['--config', rules] : ['--config', 'auto']),
45
+ // Network is only needed for --config auto; a vendored rules dir is offline.
46
+ ...(rules ? [] : []),
47
+ ];
48
+ return { argv, timeoutMs: 300_000, network: !rules };
49
+ },
50
+ parse(raw, context) {
51
+ const parsed = tryParseJson(raw);
52
+ if (!parsed.ok) {
53
+ return [unknown(context, `semgrep output was not valid JSON: ${parsed.error}`)];
54
+ }
55
+ const results = dig(parsed.value, ['results']);
56
+ if (!Array.isArray(results)) {
57
+ return [unknown(context, 'semgrep JSON had no results array (no rules ran?)')];
58
+ }
59
+ // `results: []` means "found nothing" only if semgrep actually read
60
+ // something. `paths.scanned` is the tool's own count, and an empty list
61
+ // there is a blind spot — the same distinction trivy's `Results` guard and
62
+ // dependency-cruiser's rules guard exist to keep. An absent key is not
63
+ // evidence of an empty scan (older builds omit it), so only a present,
64
+ // empty array fires.
65
+ const scanned = dig(parsed.value, ['paths', 'scanned']);
66
+ if (Array.isArray(scanned) && scanned.length === 0) {
67
+ return [
68
+ unknown(context, 'semgrep scanned no files — nothing was analysed, not a clean result'),
69
+ ];
70
+ }
71
+ const out = [];
72
+ for (const [index, result] of results.entries()) {
73
+ out.push(buildEvidence({
74
+ source: {
75
+ tool: 'semgrep',
76
+ kind: 'sast-finding',
77
+ version: context.adapterVersion,
78
+ identifier: String(dig(result, ['check_id']) ?? 'unknown-rule'),
79
+ },
80
+ provenance: placeholder(),
81
+ claim: String(dig(result, ['extra', 'message']) ?? 'semgrep match').slice(0, 160),
82
+ severity: severity(dig(result, ['extra', 'severity'])),
83
+ // Heuristic rule engine, registry-dependent; the number is a policy
84
+ // statement from the adapter, not a measurement.
85
+ confidence: 0.9,
86
+ status: 'FAIL',
87
+ location: {
88
+ // The runner scans a scratch copy, so semgrep reports absolute
89
+ // paths there; evidence must name the file in the project.
90
+ file: relativePath(context.target.workdir, String(dig(result, ['path']) ?? 'unknown')),
91
+ line: lineNumber(dig(result, ['start', 'line'])),
92
+ },
93
+ rawReference: { jsonPath: `results[${index}]` },
94
+ cwes: cwesOf(dig(result, ['extra', 'metadata', 'cwe'])),
95
+ }));
96
+ }
97
+ return out;
98
+ },
99
+ };
100
+ /** `["CWE-94: Improper Control…"]` → `["CWE-94"]`. */
101
+ function cwesOf(value) {
102
+ if (!Array.isArray(value))
103
+ return [];
104
+ const out = new Set();
105
+ for (const entry of value) {
106
+ const match = /CWE[-_ ]?(\d{1,5})/i.exec(String(entry));
107
+ if (match)
108
+ out.add(`CWE-${Number(match[1])}`);
109
+ }
110
+ return [...out];
111
+ }
112
+ function unknown(context, reason) {
113
+ return {
114
+ id: `UNKNOWN-semgrep-${reason}`.replace(/[^A-Za-z0-9-]/g, '-').slice(0, 48),
115
+ source: {
116
+ tool: 'semgrep',
117
+ version: context.adapterVersion,
118
+ kind: 'check',
119
+ identifier: 'UNKNOWN',
120
+ },
121
+ provenance: placeholder(),
122
+ claim: reason,
123
+ severity: 'INFO',
124
+ severitySource: 'adapter-default',
125
+ confidence: DETERMINISTIC_CONFIDENCE,
126
+ status: 'UNKNOWN',
127
+ location: null,
128
+ rawReference: {},
129
+ };
130
+ }
131
+ function placeholder() {
132
+ return {
133
+ executionId: '',
134
+ command: [],
135
+ config: {},
136
+ repositoryRevision: '',
137
+ timestamp: '',
138
+ executable: '',
139
+ timeoutMs: 0,
140
+ elapsedMs: 0,
141
+ };
142
+ }
143
+ function which(binary) {
144
+ for (const dir of (process.env.PATH ?? '').split(path.delimiter)) {
145
+ if (!dir)
146
+ continue;
147
+ const candidate = path.join(dir, binary);
148
+ try {
149
+ if (statSync(candidate).isFile())
150
+ return candidate;
151
+ }
152
+ catch {
153
+ /* keep looking */
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+ function versionOf(executable) {
159
+ const proc = spawnSync(executable, ['--version'], { encoding: 'utf8', timeout: 30_000 });
160
+ const match = /([\d.]+)/.exec(String(proc.stdout ?? ''));
161
+ return match?.[1] ?? 'unknown';
162
+ }
163
+ //# sourceMappingURL=semgrep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semgrep.js","sourceRoot":"","sources":["../../../src/scanner/adapters/semgrep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ScannerAdapter } from '../types.js';
2
+ export declare const trivyAdapter: ScannerAdapter;
3
+ //# sourceMappingURL=trivy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trivy.d.ts","sourceRoot":"","sources":["../../../src/scanner/adapters/trivy.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAA0B,cAAc,EAAiB,MAAM,aAAa,CAAC;AAIzF,eAAO,MAAM,YAAY,EAAE,cAyF1B,CAAC"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Trivy — Tier 1, and the reference adapter.
3
+ *
4
+ * Verified by running the real binary (v0.75.0) on a fixture package-lock and
5
+ * reading the actual JSON shape, including `PkgIdentifier.PURL`, which gives
6
+ * every finding a stable native identifier.
7
+ */
8
+ import { spawnSync } from 'node:child_process';
9
+ import { statSync } from 'node:fs';
10
+ import path from 'node:path';
11
+ import { buildEvidence } from '../identity.js';
12
+ import { DETERMINISTIC_CONFIDENCE, dig, lineNumber, severity, tryParseJson } from '../normalize.js';
13
+ export const trivyAdapter = {
14
+ id: 'trivy',
15
+ name: 'Trivy vulnerability / secret / misconfiguration scanner',
16
+ defaultConfig() {
17
+ return { scanners: ['vuln', 'secret', 'misconfig'], severity: ['HIGH', 'CRITICAL'] };
18
+ },
19
+ probe() {
20
+ const executable = which('trivy');
21
+ if (!executable) {
22
+ return { available: false, version: '', reason: 'trivy executable not found on PATH' };
23
+ }
24
+ return { available: true, version: versionOf(executable), executable };
25
+ },
26
+ buildCommand(target, config) {
27
+ return {
28
+ argv: [
29
+ 'trivy',
30
+ 'fs',
31
+ '--format',
32
+ 'json',
33
+ '--quiet',
34
+ '--scanners',
35
+ config.scanners?.join(',') ?? 'vuln,secret,misconfig',
36
+ // Exit 0 regardless of findings: exit status is never the verdict, so a
37
+ // findings-rich scan must not be smuggled in as a crash.
38
+ '--exit-code',
39
+ '0',
40
+ '--skip-version-check',
41
+ target.workdir,
42
+ ],
43
+ timeoutMs: 300_000,
44
+ };
45
+ },
46
+ parse(raw, context) {
47
+ const parsed = tryParseJson(raw);
48
+ if (!parsed.ok) {
49
+ return [unknown(context, `trivy output was not valid JSON: ${parsed.error}`)];
50
+ }
51
+ const results = dig(parsed.value, ['Results']);
52
+ if (!Array.isArray(results)) {
53
+ // An absent Results array means "nothing scanned", which is not
54
+ // "nothing found" — the difference the evidence model exists to keep.
55
+ return [unknown(context, 'trivy report carried no Results array (nothing scanned?)')];
56
+ }
57
+ const out = [];
58
+ for (const [index, result] of results.entries()) {
59
+ const target = String(dig(result, ['Target']) ?? 'unknown');
60
+ out.push(...evidenceOf(result, 'Vulnerabilities', index, 'vulnerability', context.adapterVersion, (item) => [
61
+ String(dig(item, ['VulnerabilityID']) ?? 'unknown'),
62
+ `${dig(item, ['PkgName'])} ${dig(item, ['InstalledVersion'])} is vulnerable to ${dig(item, ['VulnerabilityID'])} (fixed in ${dig(item, ['FixedVersion']) ?? 'unfixed'})`,
63
+ { file: target },
64
+ ]));
65
+ out.push(...evidenceOf(result, 'Secrets', index, 'secret', context.adapterVersion, (item) => [
66
+ String(dig(item, ['RuleID']) ?? 'unknown'),
67
+ `${dig(item, ['Title']) ?? 'possible credential'} detected`,
68
+ { file: target, line: lineNumber(dig(item, ['StartLine'])) },
69
+ ]));
70
+ out.push(...evidenceOf(result, 'Misconfigurations', index, 'misconfiguration', context.adapterVersion, (item) => [
71
+ String(dig(item, ['ID']) ?? 'unknown'),
72
+ `${dig(item, ['Title']) ?? 'misconfiguration'}: ${String(dig(item, ['Message']) ?? '').slice(0, 160)}`,
73
+ { file: target, line: lineNumber(dig(item, ['CauseMetadata', 'StartLine'])) },
74
+ ]));
75
+ }
76
+ return out;
77
+ },
78
+ };
79
+ /** One trivy result category → evidence rows. */
80
+ function evidenceOf(result, key, index, kind, adapterVersion, extract) {
81
+ const items = dig(result, [key]);
82
+ if (!Array.isArray(items))
83
+ return [];
84
+ const out = [];
85
+ for (const item of items) {
86
+ const [identifier, claim, location] = extract(item);
87
+ out.push(buildEvidence({
88
+ source: {
89
+ tool: 'trivy',
90
+ kind,
91
+ version: adapterVersion,
92
+ identifier,
93
+ },
94
+ provenance: placeholder(),
95
+ claim,
96
+ severity: severity(dig(item, ['Severity'])),
97
+ // Secrets are heuristic engine matches; the others are deterministic
98
+ // record reads. Saying so is a confidence a reviewer can check.
99
+ confidence: kind === 'secret' ? 0.9 : DETERMINISTIC_CONFIDENCE,
100
+ status: 'FAIL',
101
+ location,
102
+ rawReference: { jsonPath: `Results[${index}]`, excerpt: claim.slice(0, 160) },
103
+ }));
104
+ }
105
+ return out;
106
+ }
107
+ /** The runner stamps real provenance on every row after parsing. */
108
+ function placeholder() {
109
+ return {
110
+ executionId: '',
111
+ command: [],
112
+ config: {},
113
+ repositoryRevision: '',
114
+ timestamp: '',
115
+ executable: '',
116
+ timeoutMs: 0,
117
+ elapsedMs: 0,
118
+ };
119
+ }
120
+ function unknown(context, reason) {
121
+ return {
122
+ id: `UNKNOWN-trivy-${reason}`.replace(/[^A-Za-z0-9-]/g, '-').slice(0, 48),
123
+ source: {
124
+ tool: 'trivy',
125
+ version: context.adapterVersion,
126
+ kind: 'check',
127
+ identifier: 'UNKNOWN',
128
+ },
129
+ provenance: placeholder(),
130
+ claim: reason,
131
+ severity: 'INFO',
132
+ severitySource: 'adapter-default',
133
+ confidence: DETERMINISTIC_CONFIDENCE,
134
+ status: 'UNKNOWN',
135
+ location: null,
136
+ rawReference: {},
137
+ };
138
+ }
139
+ /* ---------------------------------------------------------------- process */
140
+ function which(binary) {
141
+ for (const dir of (process.env.PATH ?? '').split(path.delimiter)) {
142
+ if (!dir)
143
+ continue;
144
+ const candidate = path.join(dir, binary);
145
+ try {
146
+ if (statSync(candidate).isFile())
147
+ return candidate;
148
+ }
149
+ catch {
150
+ /* keep looking — a missing entry is not an error */
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+ function versionOf(executable) {
156
+ const proc = spawnSync(executable, ['--version'], {
157
+ encoding: 'utf8',
158
+ timeout: 15_000,
159
+ });
160
+ const match = /Version:?\s*([\w.]+)/.exec(String(proc.stdout ?? ''));
161
+ return match?.[1] ?? 'unknown';
162
+ }
163
+ //# sourceMappingURL=trivy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trivy.js","sourceRoot":"","sources":["../../../src/scanner/adapters/trivy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ScannerConfig } from './types.js';
2
+ /** Per-project scanner config, `.usa/scanners.yaml`. */
3
+ export declare function loadScannerConfig(root: string): Record<string, ScannerConfig>;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/scanner/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAU7E"}
@@ -0,0 +1,15 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ /** Per-project scanner config, `.usa/scanners.yaml`. */
5
+ export function loadScannerConfig(root) {
6
+ const file = path.join(root, '.usa', 'scanners.yaml');
7
+ try {
8
+ const doc = parseYaml(fs.readFileSync(file, 'utf8'));
9
+ return doc?.scanners ?? {};
10
+ }
11
+ catch {
12
+ return {};
13
+ }
14
+ }
15
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/scanner/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAG1C,wDAAwD;AACxD,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC;IACtD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAElD,CAAC;QACF,OAAO,GAAG,EAAE,QAAQ,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Evidence, EvidenceSource, EvidenceLocation, RawReference } from './types.js';
2
+ export declare function evidenceId(source: EvidenceSource, location: EvidenceLocation | null, claim: string): string;
3
+ export declare function buildEvidence(args: {
4
+ source: Omit<EvidenceSource, 'version'> & {
5
+ version?: string;
6
+ };
7
+ provenance: Evidence['provenance'];
8
+ claim: string;
9
+ severity: Evidence['severity'];
10
+ severitySource?: Evidence['severitySource'];
11
+ confidence: number;
12
+ status: Evidence['status'];
13
+ location: EvidenceLocation | null;
14
+ rawReference?: RawReference;
15
+ cwes?: string[];
16
+ }): Evidence;
17
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/scanner/identity.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS3F,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,EACjC,KAAK,EAAE,MAAM,GACZ,MAAM,CAUR;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,UAAU,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC/B,cAAc,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3B,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,GAAG,QAAQ,CAoBX"}