@opensip-cli/tool-trivy 0.1.15
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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +33 -0
- package/dist/__tests__/tool.test.d.ts +19 -0
- package/dist/__tests__/tool.test.d.ts.map +1 -0
- package/dist/__tests__/tool.test.js +234 -0
- package/dist/__tests__/tool.test.js.map +1 -0
- package/dist/__tests__/worker-e2e.test.d.ts +32 -0
- package/dist/__tests__/worker-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/worker-e2e.test.js +339 -0
- package/dist/__tests__/worker-e2e.test.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/tool.d.ts +87 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +152 -0
- package/dist/tool.js.map +1 -0
- package/package.json +133 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier-2 worker E2E (ADR-0090 D6 Tier 2) — install / discover / dispatch the trivy
|
|
3
|
+
* adapter over a REAL forked worker, end-to-end against the BUILT CLI.
|
|
4
|
+
*
|
|
5
|
+
* Trivy is the SARIF adapter, so this also proves the shared `ingestSarif` read
|
|
6
|
+
* path through the FULL installed-tool dispatch:
|
|
7
|
+
* - the trivy package is presented as a genuinely INSTALLED npm tool in a
|
|
8
|
+
* throwaway project (symlinked into its `node_modules` so the worker resolves
|
|
9
|
+
* the adapter's workspace deps from the monorepo via realpath);
|
|
10
|
+
* - `OPENSIP_CLI_ALLOW_INSTALLED_TOOLS` trusts it (installed tools are
|
|
11
|
+
* deny-by-default);
|
|
12
|
+
* - a FAKE `trivy` binary on PATH makes the run deterministic: it copies the
|
|
13
|
+
* committed golden SARIF to `--output` and exits 0 — Trivy exits 0 EVEN WITH
|
|
14
|
+
* findings, so the CLI's nonzero exit comes from the findings DERIVED FROM THE
|
|
15
|
+
* PARSED SARIF, not from the process exit (the Trivy exit-model proof). The
|
|
16
|
+
* worker fork curates its env to an allow-list, so the golden path is forwarded
|
|
17
|
+
* via the documented `OPENSIP_CLI_TOOL_ENV_PASSTHROUGH`.
|
|
18
|
+
*
|
|
19
|
+
* `opensip trivy` forks a worker that re-discovers + imports the real runtime and
|
|
20
|
+
* runs the scan loop; this suite asserts the worker→host result + host-side
|
|
21
|
+
* effects: normalized signals match the golden (with the RECOVERED severities —
|
|
22
|
+
* `critical` from CVSS 9.8, not `high` from `level:"error"`), the raw SARIF
|
|
23
|
+
* artifact lands at `.runtime/artifacts/trivy/<runId>/trivy.sarif` with mode 0600,
|
|
24
|
+
* the `--json` envelope is well-formed, the session row persists with provenance,
|
|
25
|
+
* and the full gate ratchet (gate-save → clean compare → net-new vuln surfaces)
|
|
26
|
+
* works.
|
|
27
|
+
*
|
|
28
|
+
* Requires `pnpm build` first (the CLI dist + the trivy dist). Missing builds FAIL
|
|
29
|
+
* loudly (no silent skip).
|
|
30
|
+
*/
|
|
31
|
+
import { execFileSync } from 'node:child_process';
|
|
32
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync, } from 'node:fs';
|
|
33
|
+
import { tmpdir } from 'node:os';
|
|
34
|
+
import { dirname, join } from 'node:path';
|
|
35
|
+
import { fileURLToPath } from 'node:url';
|
|
36
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
37
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
38
|
+
// .../packages/tool-trivy/src/__tests__ → repo root is four levels up.
|
|
39
|
+
const REPO_ROOT = join(HERE, '..', '..', '..', '..');
|
|
40
|
+
const CLI_DIST = join(REPO_ROOT, 'packages', 'cli', 'dist', 'index.js');
|
|
41
|
+
const TRIVY_PKG_DIR = join(REPO_ROOT, 'packages', 'tool-trivy');
|
|
42
|
+
const FIXTURES = join(TRIVY_PKG_DIR, '__fixtures__');
|
|
43
|
+
const GOLDEN_PATH = join(FIXTURES, 'trivy-golden.sarif');
|
|
44
|
+
const TRIVY_STABLE_ID = 'a26ea0eb-ee3b-4e22-a3f3-7e1f93e16000';
|
|
45
|
+
const EXPECTED = JSON.parse(readFileSync(join(FIXTURES, 'expected-signals.json'), 'utf8'));
|
|
46
|
+
let projectDir;
|
|
47
|
+
let binDir;
|
|
48
|
+
let baseEnv;
|
|
49
|
+
/** Run the built CLI as a child process, capturing stdout/stderr + exit code. */
|
|
50
|
+
function runCli(args, extraEnv = {}, cwd = projectDir) {
|
|
51
|
+
try {
|
|
52
|
+
const stdout = execFileSync('node', [CLI_DIST, ...args], {
|
|
53
|
+
cwd,
|
|
54
|
+
env: { ...process.env, ...baseEnv, ...extraEnv },
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
57
|
+
});
|
|
58
|
+
return { stdout, stderr: '', status: 0 };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const e = error;
|
|
62
|
+
return { stdout: e.stdout ?? '', stderr: e.stderr ?? '', status: e.status ?? 1 };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Scaffold a throwaway opensip-cli project that resolves the installed trivy tool. */
|
|
66
|
+
function makeTrivyProject() {
|
|
67
|
+
const dir = mkdtempSync(join(tmpdir(), 'opensip-trivy-gate-'));
|
|
68
|
+
writeFileSync(join(dir, 'opensip-cli.config.yml'), 'schemaVersion: 1\ntargets: {}\n', 'utf8');
|
|
69
|
+
const scopeDir = join(dir, 'node_modules', '@opensip-cli');
|
|
70
|
+
mkdirSync(scopeDir, { recursive: true });
|
|
71
|
+
symlinkSync(TRIVY_PKG_DIR, join(scopeDir, 'tool-trivy'), 'dir');
|
|
72
|
+
return dir;
|
|
73
|
+
}
|
|
74
|
+
/** Read the `--json` outcome wrapper (`{ kind, status, exitCode, envelope?, data? }`) from a run. */
|
|
75
|
+
function outcomeJson(run) {
|
|
76
|
+
return JSON.parse(run.stdout);
|
|
77
|
+
}
|
|
78
|
+
beforeAll(() => {
|
|
79
|
+
if (!existsSync(CLI_DIST)) {
|
|
80
|
+
throw new Error(`built CLI not found at ${CLI_DIST} — run \`pnpm build\` first`);
|
|
81
|
+
}
|
|
82
|
+
if (!existsSync(join(TRIVY_PKG_DIR, 'dist', 'index.js'))) {
|
|
83
|
+
throw new Error('built tool-trivy dist not found — run `pnpm build` first');
|
|
84
|
+
}
|
|
85
|
+
projectDir = mkdtempSync(join(tmpdir(), 'opensip-trivy-e2e-'));
|
|
86
|
+
// Project marker so the worker's `scope: 'project'` bootstrap resolves a project.
|
|
87
|
+
writeFileSync(join(projectDir, 'opensip-cli.config.yml'), 'schemaVersion: 1\ntargets: {}\n', 'utf8');
|
|
88
|
+
// Present the REAL trivy package as an installed npm tool. A SYMLINK (not a copy)
|
|
89
|
+
// so the worker resolves the adapter's `@opensip-cli/*` workspace deps from the
|
|
90
|
+
// monorepo via realpath — a copy would orphan them.
|
|
91
|
+
const scopeDir = join(projectDir, 'node_modules', '@opensip-cli');
|
|
92
|
+
mkdirSync(scopeDir, { recursive: true });
|
|
93
|
+
symlinkSync(TRIVY_PKG_DIR, join(scopeDir, 'tool-trivy'), 'dir');
|
|
94
|
+
// A FAKE trivy on PATH for determinism (copies the golden SARIF to --output,
|
|
95
|
+
// exits 0). PATH is auto-forwarded into the worker fork's curated env.
|
|
96
|
+
binDir = mkdtempSync(join(tmpdir(), 'opensip-trivy-bin-'));
|
|
97
|
+
cpSync(join(FIXTURES, 'fake-trivy'), join(binDir, 'trivy'));
|
|
98
|
+
execFileSync('chmod', ['+x', join(binDir, 'trivy')]);
|
|
99
|
+
baseEnv = {
|
|
100
|
+
PATH: `${binDir}:${process.env.PATH ?? ''}`,
|
|
101
|
+
// The committed fake binary reads the golden path from here; forward it through
|
|
102
|
+
// the worker fork's env allow-list via the documented passthrough.
|
|
103
|
+
FAKE_TRIVY_GOLDEN: GOLDEN_PATH,
|
|
104
|
+
OPENSIP_CLI_TOOL_ENV_PASSTHROUGH: 'FAKE_TRIVY_GOLDEN',
|
|
105
|
+
// Installed tools are deny-by-default — trust the trivy id (the admission check
|
|
106
|
+
// keys on `opensipTools.id`; the UUID is included to match the ADR-0048 stable
|
|
107
|
+
// id convention).
|
|
108
|
+
OPENSIP_CLI_ALLOW_INSTALLED_TOOLS: `${TRIVY_STABLE_ID} trivy`,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
afterAll(() => {
|
|
112
|
+
if (projectDir !== undefined)
|
|
113
|
+
rmSync(projectDir, { recursive: true, force: true });
|
|
114
|
+
if (binDir !== undefined)
|
|
115
|
+
rmSync(binDir, { recursive: true, force: true });
|
|
116
|
+
});
|
|
117
|
+
describe('trivy worker E2E — opensip trivy (real forked worker)', () => {
|
|
118
|
+
let scan;
|
|
119
|
+
let envelope;
|
|
120
|
+
beforeAll(() => {
|
|
121
|
+
scan = runCli(['trivy', '--json']);
|
|
122
|
+
const outcome = outcomeJson(scan);
|
|
123
|
+
envelope = outcome.envelope;
|
|
124
|
+
});
|
|
125
|
+
it('forks a worker and emits a well-formed signal envelope for trivy', () => {
|
|
126
|
+
expect(envelope.tool).toBe('trivy');
|
|
127
|
+
expect(envelope.runId).toMatch(/^RUN_/);
|
|
128
|
+
// The fake binary exits 0, but the parsed SARIF has critical/high findings ⇒ the
|
|
129
|
+
// verdict FAILS and the CLI exits 1 (findings derived from SARIF, not exit code).
|
|
130
|
+
expect(envelope.verdict.passed).toBe(false);
|
|
131
|
+
expect(scan.status).toBe(1);
|
|
132
|
+
});
|
|
133
|
+
it('normalizes the worker SARIF output to the golden signal shapes (recovered severities)', () => {
|
|
134
|
+
const shapes = envelope.signals.map((s) => ({
|
|
135
|
+
ruleId: s.ruleId,
|
|
136
|
+
severity: s.severity,
|
|
137
|
+
message: s.message,
|
|
138
|
+
file: s.filePath,
|
|
139
|
+
line: s.line,
|
|
140
|
+
column: s.column,
|
|
141
|
+
}));
|
|
142
|
+
expect(shapes).toEqual(EXPECTED);
|
|
143
|
+
// The headline recovery: a level:"error" + security-severity:"9.8" finding is
|
|
144
|
+
// `critical`, NOT `high` (which level alone would give).
|
|
145
|
+
const certifi = envelope.signals.find((s) => s.ruleId === 'CVE-2023-37920');
|
|
146
|
+
expect(certifi?.severity).toBe('critical');
|
|
147
|
+
});
|
|
148
|
+
it('stamps message-hash fingerprints + provenance + recovered native severity worker-side', () => {
|
|
149
|
+
for (const s of envelope.signals) {
|
|
150
|
+
expect(s.fingerprint).toMatch(/^[0-9a-f]{64}$/);
|
|
151
|
+
const provenance = s.metadata.provenance;
|
|
152
|
+
expect(provenance.tool).toBe('trivy');
|
|
153
|
+
expect(provenance.adapterPackage).toBe('@opensip-cli/tool-trivy');
|
|
154
|
+
}
|
|
155
|
+
const certifi = envelope.signals.find((s) => s.ruleId === 'CVE-2023-37920');
|
|
156
|
+
expect(certifi?.metadata.securitySeverity).toBe('9.8');
|
|
157
|
+
expect(certifi?.metadata.nativeLevel).toBe('error');
|
|
158
|
+
const misconfig = envelope.signals.find((s) => s.ruleId === 'DS002');
|
|
159
|
+
expect(misconfig?.metadata.nativeLevel).toBe('warning');
|
|
160
|
+
});
|
|
161
|
+
it('lands the raw SARIF artifact under .runtime/artifacts/trivy/<runId>/trivy.sarif with mode 0600', () => {
|
|
162
|
+
const runDir = join(projectDir, 'opensip-cli', '.runtime', 'artifacts', 'trivy');
|
|
163
|
+
expect(existsSync(runDir)).toBe(true);
|
|
164
|
+
const runs = readdirSync(runDir);
|
|
165
|
+
expect(runs.length).toBeGreaterThan(0);
|
|
166
|
+
const artifact = join(runDir, runs[0], 'trivy.sarif');
|
|
167
|
+
expect(existsSync(artifact)).toBe(true);
|
|
168
|
+
// Owner-only read/write (0600) — the artifact carries the raw scanner output.
|
|
169
|
+
expect(statSync(artifact).mode & 0o777).toBe(0o600);
|
|
170
|
+
// The persisted artifact is the byte-preserved golden (three SARIF results).
|
|
171
|
+
const doc = JSON.parse(readFileSync(artifact, 'utf8'));
|
|
172
|
+
expect(doc.runs[0].results).toHaveLength(3);
|
|
173
|
+
});
|
|
174
|
+
it('persists a session row with the trivy tool + provenance payload', () => {
|
|
175
|
+
const list = runCli(['sessions', 'list', '--json']);
|
|
176
|
+
expect(list.status).toBe(0);
|
|
177
|
+
const outcome = outcomeJson(list);
|
|
178
|
+
const data = outcome.data;
|
|
179
|
+
const sessions = data?.sessions ?? [];
|
|
180
|
+
const trivyRow = sessions.find((s) => s.tool === 'trivy');
|
|
181
|
+
expect(trivyRow).toBeDefined();
|
|
182
|
+
expect(trivyRow?.passed).toBe(false);
|
|
183
|
+
const payload = trivyRow?.payload;
|
|
184
|
+
expect(payload?.binary?.path).toContain('trivy');
|
|
185
|
+
expect(payload?.findings).toBe(3);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
describe('trivy worker E2E — doctor / version diagnostics', () => {
|
|
189
|
+
it('doctor --json reports a ready, resolved binary (exit 0)', () => {
|
|
190
|
+
const run = runCli(['trivy', 'doctor', '--json']);
|
|
191
|
+
expect(run.status).toBe(0);
|
|
192
|
+
const report = outcomeJson(run).data;
|
|
193
|
+
expect(report.tool).toBe('trivy');
|
|
194
|
+
expect(report.ready).toBe(true);
|
|
195
|
+
expect(report.binary.found).toBe(true);
|
|
196
|
+
expect(report.version.detected).toBe('0.50.1');
|
|
197
|
+
expect(report.version.status).toBe('ok');
|
|
198
|
+
});
|
|
199
|
+
it('doctor reports NOT ready (exit 2) when the resolved binary is missing', () => {
|
|
200
|
+
// Pin the binary to a non-existent absolute path via the env layer (which beats
|
|
201
|
+
// PATH and hard-misses) so resolution fails WITHOUT breaking the toolchain/worker
|
|
202
|
+
// fork. Forward the pin into the worker (doctor probes worker-side) via the
|
|
203
|
+
// documented passthrough.
|
|
204
|
+
const run = runCli(['trivy', 'doctor', '--json'], {
|
|
205
|
+
OPENSIP_TRIVY_BIN: '/nonexistent/path/to/trivy',
|
|
206
|
+
OPENSIP_CLI_TOOL_ENV_PASSTHROUGH: 'FAKE_TRIVY_GOLDEN OPENSIP_TRIVY_BIN',
|
|
207
|
+
});
|
|
208
|
+
expect(run.status).toBe(2);
|
|
209
|
+
const report = outcomeJson(run).data;
|
|
210
|
+
expect(report.ready).toBe(false);
|
|
211
|
+
expect(report.binary.found).toBe(false);
|
|
212
|
+
});
|
|
213
|
+
it('version --json prints the resolved trivy binary version', () => {
|
|
214
|
+
const run = runCli(['trivy', 'version', '--json']);
|
|
215
|
+
expect(run.status).toBe(0);
|
|
216
|
+
const report = outcomeJson(run).data;
|
|
217
|
+
expect(report.found).toBe(true);
|
|
218
|
+
expect(report.version).toBe('0.50.1');
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe('trivy worker E2E — installed tools are deny-by-default', () => {
|
|
222
|
+
it('without the trust allowlist, `opensip trivy` is not admitted', () => {
|
|
223
|
+
const run = runCli(['trivy', '--json'], { OPENSIP_CLI_ALLOW_INSTALLED_TOOLS: '' });
|
|
224
|
+
// Deny-by-default: the command never mounts (unknown command / not found), so
|
|
225
|
+
// the scan does NOT run.
|
|
226
|
+
expect(run.status).not.toBe(0);
|
|
227
|
+
expect(`${run.stdout}${run.stderr}`.toLowerCase()).toMatch(/unknown command|not found|trivy/);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
/**
|
|
231
|
+
* §4.12 ratchet acceptance — the FULL baseline/gate loop over a REAL forked worker.
|
|
232
|
+
* The substrate wires `--gate-save` / `--gate-compare` once (ADR-0036), so the trivy
|
|
233
|
+
* adapter inherits it. This proves: capture a baseline → an unchanged re-scan is
|
|
234
|
+
* clean (exit 0) → a NET-NEW vulnerability surfaces and fails (exit ≠ 0).
|
|
235
|
+
*
|
|
236
|
+
* Runs in its OWN throwaway project so the baseline + sessions are isolated from the
|
|
237
|
+
* scan suites above. The fake binary copies `FAKE_TRIVY_GOLDEN` to `--output`;
|
|
238
|
+
* pointing it at an AUGMENTED golden (the three originals + one new SARIF result +
|
|
239
|
+
* rule) is how the "regression" run injects a net-new finding.
|
|
240
|
+
*/
|
|
241
|
+
describe('trivy worker E2E — full gate ratchet (§4.12)', () => {
|
|
242
|
+
let gateProject;
|
|
243
|
+
let augmentedGolden;
|
|
244
|
+
let save;
|
|
245
|
+
let compareClean;
|
|
246
|
+
let compareRegressed;
|
|
247
|
+
beforeAll(() => {
|
|
248
|
+
gateProject = makeTrivyProject();
|
|
249
|
+
// The augmented golden = the committed three findings + one NET-NEW SARIF result
|
|
250
|
+
// (a distinct ruleId/message ⇒ a distinct message-hash fingerprint ⇒ the ratchet
|
|
251
|
+
// sees it as net-new, not unchanged). A matching rule descriptor carries its CVSS.
|
|
252
|
+
const original = JSON.parse(readFileSync(GOLDEN_PATH, 'utf8'));
|
|
253
|
+
const run0 = original.runs[0];
|
|
254
|
+
run0.tool.driver.rules.push({
|
|
255
|
+
id: 'CVE-2024-99999',
|
|
256
|
+
name: 'LanguageSpecificPackageVulnerability',
|
|
257
|
+
shortDescription: { text: 'axios: Server-Side Request Forgery' },
|
|
258
|
+
helpUri: 'https://avd.aquasec.com/nvd/cve-2024-99999',
|
|
259
|
+
defaultConfiguration: { level: 'error' },
|
|
260
|
+
properties: { 'security-severity': '8.6', tags: ['vulnerability', 'security', 'HIGH'] },
|
|
261
|
+
});
|
|
262
|
+
run0.results.push({
|
|
263
|
+
ruleId: 'CVE-2024-99999',
|
|
264
|
+
ruleIndex: 3,
|
|
265
|
+
level: 'error',
|
|
266
|
+
message: { text: 'axios: Server-Side Request Forgery' },
|
|
267
|
+
locations: [
|
|
268
|
+
{
|
|
269
|
+
physicalLocation: {
|
|
270
|
+
artifactLocation: { uri: 'requirements.txt', uriBaseId: 'ROOTPATH' },
|
|
271
|
+
region: { startLine: 1, startColumn: 1 },
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
});
|
|
276
|
+
augmentedGolden = join(gateProject, 'augmented-golden.sarif');
|
|
277
|
+
writeFileSync(augmentedGolden, JSON.stringify(original), 'utf8');
|
|
278
|
+
// 1) Capture the baseline (three findings). The findings gate (ADR-0020) makes
|
|
279
|
+
// gate-save itself exit 1 — it records the baseline AND honours the verdict.
|
|
280
|
+
save = runCli(['trivy', '--gate-save'], {}, gateProject);
|
|
281
|
+
// 2) Re-scan the SAME golden and compare → no net-new ⇒ clean (exit 0).
|
|
282
|
+
compareClean = runCli(['trivy', '--gate-compare'], {}, gateProject);
|
|
283
|
+
// 3) Compare against the augmented golden → one net-new finding ⇒ degraded.
|
|
284
|
+
compareRegressed = runCli(['trivy', '--gate-compare'], { FAKE_TRIVY_GOLDEN: augmentedGolden }, gateProject);
|
|
285
|
+
});
|
|
286
|
+
afterAll(() => {
|
|
287
|
+
if (gateProject !== undefined)
|
|
288
|
+
rmSync(gateProject, { recursive: true, force: true });
|
|
289
|
+
});
|
|
290
|
+
it('--gate-save records the baseline and persists a session', () => {
|
|
291
|
+
// The findings gate makes gate-save exit 1 (critical/high findings present), but
|
|
292
|
+
// the baseline IS written — proven by the clean compare below.
|
|
293
|
+
expect(save.status).toBe(1);
|
|
294
|
+
const list = runCli(['sessions', 'list', '--json'], {}, gateProject);
|
|
295
|
+
const data = outcomeJson(list).data;
|
|
296
|
+
const trivyRows = (data.sessions ?? []).filter((s) => s.tool === 'trivy');
|
|
297
|
+
expect(trivyRows.length).toBeGreaterThanOrEqual(1);
|
|
298
|
+
});
|
|
299
|
+
it('--gate-compare on the SAME scan is a clean no-op (exit 0, no regression)', () => {
|
|
300
|
+
// Pre-existing findings recorded in the baseline are NOT a regression — only
|
|
301
|
+
// net-new findings fail the ratchet. The clean exit also proves gate-save wrote
|
|
302
|
+
// the baseline (the missing-baseline → exit 2 contract is asserted directly in
|
|
303
|
+
// the "typed exit-class survives the worker boundary" suite below).
|
|
304
|
+
expect(compareRegressed).toBeDefined();
|
|
305
|
+
expect(compareClean.status).toBe(0);
|
|
306
|
+
expect(`${compareClean.stdout}${compareClean.stderr}`).toMatch(/STABLE|no change/i);
|
|
307
|
+
});
|
|
308
|
+
it('--gate-compare surfaces a NET-NEW vulnerability and exits non-zero (degraded)', () => {
|
|
309
|
+
expect(compareRegressed.status).not.toBe(0);
|
|
310
|
+
const out = `${compareRegressed.stdout}${compareRegressed.stderr}`;
|
|
311
|
+
// The net-new advisory is named in the diff; the verdict footer says DEGRADED.
|
|
312
|
+
expect(out).toContain('CVE-2024-99999');
|
|
313
|
+
expect(out).toMatch(/DEGRADED|Added/i);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
/**
|
|
317
|
+
* A5/A6 — the typed exit-class must survive the worker boundary on the host-RPC
|
|
318
|
+
* REJECT path too. `--gate-compare` with NO saved baseline makes the host
|
|
319
|
+
* `compareBaseline` seam reject with a ConfigurationError (BASELINE_MISSING). That
|
|
320
|
+
* rejection crosses the worker IPC as a structured reply; before the fix the worker
|
|
321
|
+
* shim rebuilt a PLAIN Error → SystemError → exit 1, silently losing the frozen
|
|
322
|
+
* exit-2 config contract. This asserts the contract end-to-end over a REAL fork.
|
|
323
|
+
*/
|
|
324
|
+
describe('trivy worker E2E — gate-compare with no baseline exits 2 (A5/A6)', () => {
|
|
325
|
+
it('`opensip trivy --gate-compare` before any --gate-save exits 2 (not 1)', () => {
|
|
326
|
+
// A fresh project with NO baseline captured: the compareBaseline host seam
|
|
327
|
+
// rejects ConfigurationError(BASELINE_MISSING) over the host-RPC channel.
|
|
328
|
+
const freshProject = makeTrivyProject();
|
|
329
|
+
try {
|
|
330
|
+
const run = runCli(['trivy', '--gate-compare'], {}, freshProject);
|
|
331
|
+
expect(run.status).toBe(2);
|
|
332
|
+
expect(`${run.stdout}${run.stderr}`).toMatch(/baseline|gate-save/i);
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
rmSync(freshProject, { recursive: true, force: true });
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
//# sourceMappingURL=worker-e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/worker-e2e.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@opensip-cli/tool-trivy` public barrel.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the `tool` descriptor the host loads by name through the installed
|
|
5
|
+
* external-tool worker-dispatch path (`mod.tool`), plus a `default` alias and the
|
|
6
|
+
* identity/stable-id constants. There is NO per-adapter SARIF parser: Trivy routes
|
|
7
|
+
* its SARIF output through the substrate's shared `ingestSarif` (the single SARIF
|
|
8
|
+
* read path, ADR-0091). The adapter internals are otherwise not public API.
|
|
9
|
+
*/
|
|
10
|
+
export { tool, tool as default, TRIVY_IDENTITY, TRIVY_STABLE_ID } from './tool.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@opensip-cli/tool-trivy` public barrel.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the `tool` descriptor the host loads by name through the installed
|
|
5
|
+
* external-tool worker-dispatch path (`mod.tool`), plus a `default` alias and the
|
|
6
|
+
* identity/stable-id constants. There is NO per-adapter SARIF parser: Trivy routes
|
|
7
|
+
* its SARIF output through the substrate's shared `ingestSarif` (the single SARIF
|
|
8
|
+
* read path, ADR-0091). The adapter internals are otherwise not public API.
|
|
9
|
+
*/
|
|
10
|
+
export { tool, tool as default, TRIVY_IDENTITY, TRIVY_STABLE_ID } from './tool.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/tool.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@opensip-cli/tool-trivy` Tool descriptor (ADR-0090 / ADR-0091 / ADR-0092).
|
|
3
|
+
*
|
|
4
|
+
* The third External Tool Adapter — and the FIRST real consumer of the substrate's
|
|
5
|
+
* shared `ingestSarif`. It wraps the user-installed `trivy` vulnerability +
|
|
6
|
+
* misconfiguration scanner as an ordinary opensip-cli `Tool` via {@link
|
|
7
|
+
* defineExternalToolAdapter}. The substrate owns binary resolution, the run loop
|
|
8
|
+
* (resolve → execFile → ingest → normalize → persist via the host artifact seam),
|
|
9
|
+
* provenance, and the auto-added `doctor`/`version` commands; this module declares
|
|
10
|
+
* only the trivy identity, the wrapped binary, and the `scan` command (args only).
|
|
11
|
+
*
|
|
12
|
+
* Unlike gitleaks/osv-scanner (JSON adapters with a per-adapter `parse`), Trivy is
|
|
13
|
+
* the SARIF adapter: its `scan` command declares `output: { kind: 'sarif' }` and
|
|
14
|
+
* OMITS `parse`. The substrate's shared `ingestSarif` reads the SARIF 2.1.0 log,
|
|
15
|
+
* recovering each finding's four-bucket severity from the rule descriptor's
|
|
16
|
+
* `properties["security-severity"]` (a CVSS number) BEFORE the lossy `level`
|
|
17
|
+
* fallback (the OpenSIP SARIF writer collapses critical AND high → `error`, so a
|
|
18
|
+
* `9.8` result with `level:"error"` must normalize to `critical`, not `high`).
|
|
19
|
+
*
|
|
20
|
+
* Layer 4: imports the substrate + `@opensip-cli/core` ONLY — never the CLI,
|
|
21
|
+
* output, or any other adapter (dependency-cruiser enforced). The
|
|
22
|
+
* `single-sarif-ingest` rule means this adapter MUST NOT parse SARIF itself; it
|
|
23
|
+
* relies on the one substrate reader.
|
|
24
|
+
*
|
|
25
|
+
* This is an OPT-IN, installed tool (NOT in `bundled-tools.manifest.json`): the
|
|
26
|
+
* host never imports this runtime; an `opensip trivy` invocation forks a worker
|
|
27
|
+
* that re-discovers + imports it and runs the handler. Installed tools are
|
|
28
|
+
* deny-by-default — a run needs `OPENSIP_CLI_ALLOW_INSTALLED_TOOLS` to include the
|
|
29
|
+
* trivy id.
|
|
30
|
+
*/
|
|
31
|
+
import type { Tool, ToolIdentity } from '@opensip-cli/core';
|
|
32
|
+
import type { AdapterRunContext } from '@opensip-cli/external-tool-adapter';
|
|
33
|
+
/** Human identity (`opensip trivy`). No aliases. */
|
|
34
|
+
export declare const TRIVY_IDENTITY: ToolIdentity;
|
|
35
|
+
/** Stable UUID identity (ADR-0048); mirrors `opensipTools.stableId` in package.json. */
|
|
36
|
+
export declare const TRIVY_STABLE_ID = "a26ea0eb-ee3b-4e22-a3f3-7e1f93e16000";
|
|
37
|
+
/**
|
|
38
|
+
* Normalize the `trivy --version` stdout to a bare semver. Trivy prints a
|
|
39
|
+
* multi-line banner whose first line is e.g. `Version: 0.50.1` (followed by the
|
|
40
|
+
* vulnerability/Java DB metadata); take the first semver-shaped token and strip a
|
|
41
|
+
* leading `v`.
|
|
42
|
+
*
|
|
43
|
+
* VERIFY-against-installed-binary: exact `trivy --version` output format.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseTrivyVersion(stdout: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Build the trivy scan argv (no shell — args are passed to `execFile`). Scans the
|
|
48
|
+
* project filesystem (`fs <root>`) for vulnerabilities + secrets + misconfigurations
|
|
49
|
+
* and writes a SARIF 2.1.0 report to the host-owned artifact path the substrate
|
|
50
|
+
* composes for this run.
|
|
51
|
+
*
|
|
52
|
+
* Scanner selection (A8): `trivy fs` defaults to `vuln,secret` ONLY — misconfig is
|
|
53
|
+
* OFF unless requested. The adapter advertises misconfig (metadata, docs, the DS002
|
|
54
|
+
* golden), so it MUST pass `--scanners vuln,secret,misconfig` or a real run would
|
|
55
|
+
* silently emit zero misconfig findings while the fixtures assert them. VERIFY-
|
|
56
|
+
* against-installed-trivy: the `--scanners` value set + name across versions.
|
|
57
|
+
*
|
|
58
|
+
* Local-only posture (ADR-0092): Trivy fetches its vulnerability DB from GHCR on
|
|
59
|
+
* first run, so the scan is pinned offline with `--skip-db-update`,
|
|
60
|
+
* `--skip-java-db-update`, `--offline-scan`, and — because the misconfig scanner
|
|
61
|
+
* pulls a separate "checks" bundle from GHCR — `--skip-check-update`. This REQUIRES
|
|
62
|
+
* a pre-populated cache (the vuln DB AND, for misconfig, the checks bundle); the
|
|
63
|
+
* adapter's `doctor`/`installHint` notes the cache caveat.
|
|
64
|
+
*
|
|
65
|
+
* VERIFY-against-installed-binary: the local-only flag set across versions.
|
|
66
|
+
* Trivy is NOT passed `--exit-code` — it exits 0 even with findings, and the
|
|
67
|
+
* substrate derives findings from the parsed SARIF (any nonzero is a fault).
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildScanArgs(ctx: AdapterRunContext): readonly string[];
|
|
70
|
+
/**
|
|
71
|
+
* A3: build trivy's exclusion of opensip's own `.runtime` artifact store. Trivy
|
|
72
|
+
* takes a directory skip via `--skip-dirs`; the substrate supplies the path and
|
|
73
|
+
* the run loop appends the flag (no user-facing flag, so the command manifest is
|
|
74
|
+
* unchanged). VERIFY-against-installed-binary: `--skip-dirs` accepts an absolute
|
|
75
|
+
* path / glob and may be passed after the positional scan root.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildTrivyExclude(input: {
|
|
78
|
+
readonly excludePath: string;
|
|
79
|
+
}): {
|
|
80
|
+
readonly args: readonly string[];
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* The trivy external-tool adapter `Tool`. The host loads it by name through the
|
|
84
|
+
* installed-tool worker-dispatch path (the barrel re-exports it as `tool`).
|
|
85
|
+
*/
|
|
86
|
+
export declare const tool: Tool;
|
|
87
|
+
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAE5E,oDAAoD;AACpD,eAAO,MAAM,cAAc,EAAE,YAE5B,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,eAAe,yCAAyC,CAAC;AAEtE;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,iBAAiB,GAAG,SAAS,MAAM,EAAE,CAevE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAC1E,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC,CAEA;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,EAAE,IA8CjB,CAAC"}
|
package/dist/tool.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@opensip-cli/tool-trivy` Tool descriptor (ADR-0090 / ADR-0091 / ADR-0092).
|
|
3
|
+
*
|
|
4
|
+
* The third External Tool Adapter — and the FIRST real consumer of the substrate's
|
|
5
|
+
* shared `ingestSarif`. It wraps the user-installed `trivy` vulnerability +
|
|
6
|
+
* misconfiguration scanner as an ordinary opensip-cli `Tool` via {@link
|
|
7
|
+
* defineExternalToolAdapter}. The substrate owns binary resolution, the run loop
|
|
8
|
+
* (resolve → execFile → ingest → normalize → persist via the host artifact seam),
|
|
9
|
+
* provenance, and the auto-added `doctor`/`version` commands; this module declares
|
|
10
|
+
* only the trivy identity, the wrapped binary, and the `scan` command (args only).
|
|
11
|
+
*
|
|
12
|
+
* Unlike gitleaks/osv-scanner (JSON adapters with a per-adapter `parse`), Trivy is
|
|
13
|
+
* the SARIF adapter: its `scan` command declares `output: { kind: 'sarif' }` and
|
|
14
|
+
* OMITS `parse`. The substrate's shared `ingestSarif` reads the SARIF 2.1.0 log,
|
|
15
|
+
* recovering each finding's four-bucket severity from the rule descriptor's
|
|
16
|
+
* `properties["security-severity"]` (a CVSS number) BEFORE the lossy `level`
|
|
17
|
+
* fallback (the OpenSIP SARIF writer collapses critical AND high → `error`, so a
|
|
18
|
+
* `9.8` result with `level:"error"` must normalize to `critical`, not `high`).
|
|
19
|
+
*
|
|
20
|
+
* Layer 4: imports the substrate + `@opensip-cli/core` ONLY — never the CLI,
|
|
21
|
+
* output, or any other adapter (dependency-cruiser enforced). The
|
|
22
|
+
* `single-sarif-ingest` rule means this adapter MUST NOT parse SARIF itself; it
|
|
23
|
+
* relies on the one substrate reader.
|
|
24
|
+
*
|
|
25
|
+
* This is an OPT-IN, installed tool (NOT in `bundled-tools.manifest.json`): the
|
|
26
|
+
* host never imports this runtime; an `opensip trivy` invocation forks a worker
|
|
27
|
+
* that re-discovers + imports it and runs the handler. Installed tools are
|
|
28
|
+
* deny-by-default — a run needs `OPENSIP_CLI_ALLOW_INSTALLED_TOOLS` to include the
|
|
29
|
+
* trivy id.
|
|
30
|
+
*/
|
|
31
|
+
import { readPackageVersion } from '@opensip-cli/core';
|
|
32
|
+
import { defineExternalToolAdapter } from '@opensip-cli/external-tool-adapter';
|
|
33
|
+
/** Human identity (`opensip trivy`). No aliases. */
|
|
34
|
+
export const TRIVY_IDENTITY = {
|
|
35
|
+
name: 'trivy',
|
|
36
|
+
};
|
|
37
|
+
/** Stable UUID identity (ADR-0048); mirrors `opensipTools.stableId` in package.json. */
|
|
38
|
+
export const TRIVY_STABLE_ID = 'a26ea0eb-ee3b-4e22-a3f3-7e1f93e16000';
|
|
39
|
+
/**
|
|
40
|
+
* Normalize the `trivy --version` stdout to a bare semver. Trivy prints a
|
|
41
|
+
* multi-line banner whose first line is e.g. `Version: 0.50.1` (followed by the
|
|
42
|
+
* vulnerability/Java DB metadata); take the first semver-shaped token and strip a
|
|
43
|
+
* leading `v`.
|
|
44
|
+
*
|
|
45
|
+
* VERIFY-against-installed-binary: exact `trivy --version` output format.
|
|
46
|
+
*/
|
|
47
|
+
export function parseTrivyVersion(stdout) {
|
|
48
|
+
// Fully bounded ({1,5} digit runs, {1,2} dotted segments) so the matcher is
|
|
49
|
+
// linear — major.minor[.patch], optional leading `v`.
|
|
50
|
+
const match = /v?(\d{1,5}(?:\.\d{1,5}){1,2})/.exec(stdout);
|
|
51
|
+
return match?.[1] ?? stdout.trim();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build the trivy scan argv (no shell — args are passed to `execFile`). Scans the
|
|
55
|
+
* project filesystem (`fs <root>`) for vulnerabilities + secrets + misconfigurations
|
|
56
|
+
* and writes a SARIF 2.1.0 report to the host-owned artifact path the substrate
|
|
57
|
+
* composes for this run.
|
|
58
|
+
*
|
|
59
|
+
* Scanner selection (A8): `trivy fs` defaults to `vuln,secret` ONLY — misconfig is
|
|
60
|
+
* OFF unless requested. The adapter advertises misconfig (metadata, docs, the DS002
|
|
61
|
+
* golden), so it MUST pass `--scanners vuln,secret,misconfig` or a real run would
|
|
62
|
+
* silently emit zero misconfig findings while the fixtures assert them. VERIFY-
|
|
63
|
+
* against-installed-trivy: the `--scanners` value set + name across versions.
|
|
64
|
+
*
|
|
65
|
+
* Local-only posture (ADR-0092): Trivy fetches its vulnerability DB from GHCR on
|
|
66
|
+
* first run, so the scan is pinned offline with `--skip-db-update`,
|
|
67
|
+
* `--skip-java-db-update`, `--offline-scan`, and — because the misconfig scanner
|
|
68
|
+
* pulls a separate "checks" bundle from GHCR — `--skip-check-update`. This REQUIRES
|
|
69
|
+
* a pre-populated cache (the vuln DB AND, for misconfig, the checks bundle); the
|
|
70
|
+
* adapter's `doctor`/`installHint` notes the cache caveat.
|
|
71
|
+
*
|
|
72
|
+
* VERIFY-against-installed-binary: the local-only flag set across versions.
|
|
73
|
+
* Trivy is NOT passed `--exit-code` — it exits 0 even with findings, and the
|
|
74
|
+
* substrate derives findings from the parsed SARIF (any nonzero is a fault).
|
|
75
|
+
*/
|
|
76
|
+
export function buildScanArgs(ctx) {
|
|
77
|
+
return [
|
|
78
|
+
'fs',
|
|
79
|
+
'--scanners',
|
|
80
|
+
'vuln,secret,misconfig',
|
|
81
|
+
'--format',
|
|
82
|
+
'sarif',
|
|
83
|
+
'--output',
|
|
84
|
+
ctx.artifactPath('trivy.sarif'),
|
|
85
|
+
'--skip-db-update',
|
|
86
|
+
'--skip-java-db-update',
|
|
87
|
+
'--offline-scan',
|
|
88
|
+
'--skip-check-update',
|
|
89
|
+
ctx.projectRoot,
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A3: build trivy's exclusion of opensip's own `.runtime` artifact store. Trivy
|
|
94
|
+
* takes a directory skip via `--skip-dirs`; the substrate supplies the path and
|
|
95
|
+
* the run loop appends the flag (no user-facing flag, so the command manifest is
|
|
96
|
+
* unchanged). VERIFY-against-installed-binary: `--skip-dirs` accepts an absolute
|
|
97
|
+
* path / glob and may be passed after the positional scan root.
|
|
98
|
+
*/
|
|
99
|
+
export function buildTrivyExclude(input) {
|
|
100
|
+
return { args: ['--skip-dirs', input.excludePath] };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The trivy external-tool adapter `Tool`. The host loads it by name through the
|
|
104
|
+
* installed-tool worker-dispatch path (the barrel re-exports it as `tool`).
|
|
105
|
+
*/
|
|
106
|
+
export const tool = defineExternalToolAdapter({
|
|
107
|
+
identity: TRIVY_IDENTITY,
|
|
108
|
+
metadata: {
|
|
109
|
+
id: TRIVY_STABLE_ID,
|
|
110
|
+
version: readPackageVersion(import.meta.url),
|
|
111
|
+
description: 'Vulnerability + misconfig scanning via Trivy',
|
|
112
|
+
adapterPackage: '@opensip-cli/tool-trivy',
|
|
113
|
+
},
|
|
114
|
+
binary: {
|
|
115
|
+
command: 'trivy',
|
|
116
|
+
versionArgs: ['--version'],
|
|
117
|
+
versionParse: parseTrivyVersion,
|
|
118
|
+
// `trivy fs --format sarif --output` is stable from 0.40. VERIFY-against-
|
|
119
|
+
// installed-binary (the local-only flag names have shifted across versions).
|
|
120
|
+
minVersion: '0.40.0',
|
|
121
|
+
// Operator pin (config `binaries.trivy.path` / `OPENSIP_TRIVY_BIN`) beats
|
|
122
|
+
// PATH; resolution never fetches a binary.
|
|
123
|
+
resolution: ['config', 'path'],
|
|
124
|
+
installHint: 'Install trivy: https://aquasecurity.github.io/trivy/latest/getting-started/installation/ (brew install trivy). Pre-populate the offline caches: the vuln DB (e.g. `trivy image --download-db-only`) AND, for misconfiguration scanning, the checks bundle (e.g. run `trivy fs .` once online so the misconfig checks are cached).',
|
|
125
|
+
},
|
|
126
|
+
// Trivy queries its LOCAL vuln DB cache via execFile with --offline-scan — no
|
|
127
|
+
// network, no credentials at scan time (the DB cache must be pre-populated).
|
|
128
|
+
network: 'local-only',
|
|
129
|
+
commands: [
|
|
130
|
+
{
|
|
131
|
+
name: 'scan',
|
|
132
|
+
description: 'Scan the project filesystem for vulnerabilities and misconfigurations (Trivy)',
|
|
133
|
+
args: buildScanArgs,
|
|
134
|
+
// SARIF adapter: no `parse` — the substrate's shared `ingestSarif` reads it,
|
|
135
|
+
// recovering severity from `driver.rules[ruleIndex].properties["security-severity"]`.
|
|
136
|
+
output: { kind: 'sarif', path: 'trivy.sarif' },
|
|
137
|
+
// A3: never re-walk opensip's own persisted reports under `.runtime/` (see
|
|
138
|
+
// {@link buildTrivyExclude}).
|
|
139
|
+
excludeScan: buildTrivyExclude,
|
|
140
|
+
// ADR-0091 Phase-0 decision 4 (Trivy): Trivy exits `0` even WITH findings (no
|
|
141
|
+
// `--exit-code` passed), so findings are derived from the parsed SARIF, not the
|
|
142
|
+
// exit code. Only `0` is clean; there is NO findings code; any nonzero (>= 1)
|
|
143
|
+
// is a genuine fault. (Passing `--exit-code 1` would collide findings-1 with
|
|
144
|
+
// error-1 — the gitleaks sharp edge — so it is deliberately omitted.)
|
|
145
|
+
exitCodes: { ok: [0], findings: [], errorFrom: 1 },
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
// Scanner output is line-volatile → the line-shift-tolerant message hash, not the
|
|
149
|
+
// host `ruleId|file|line|col` default. Stamped worker-side in the run loop.
|
|
150
|
+
fingerprintStrategy: 'message-hash',
|
|
151
|
+
});
|
|
152
|
+
//# sourceMappingURL=tool.js.map
|
package/dist/tool.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAK/E,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,IAAI,EAAE,OAAO;CACd,CAAC;AAEF,wFAAwF;AACxF,MAAM,CAAC,MAAM,eAAe,GAAG,sCAAsC,CAAC;AAEtE;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,4EAA4E;IAC5E,sDAAsD;IACtD,MAAM,KAAK,GAAG,+BAA+B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3D,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;AACrC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,aAAa,CAAC,GAAsB;IAClD,OAAO;QACL,IAAI;QACJ,YAAY;QACZ,uBAAuB;QACvB,UAAU;QACV,OAAO;QACP,UAAU;QACV,GAAG,CAAC,YAAY,CAAC,aAAa,CAAC;QAC/B,kBAAkB;QAClB,uBAAuB;QACvB,gBAAgB;QAChB,qBAAqB;QACrB,GAAG,CAAC,WAAW;KAChB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAuC;IAGvE,OAAO,EAAE,IAAI,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAS,yBAAyB,CAAC;IAClD,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE;QACR,EAAE,EAAE,eAAe;QACnB,OAAO,EAAE,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QAC5C,WAAW,EAAE,8CAA8C;QAC3D,cAAc,EAAE,yBAAyB;KAC1C;IACD,MAAM,EAAE;QACN,OAAO,EAAE,OAAO;QAChB,WAAW,EAAE,CAAC,WAAW,CAAC;QAC1B,YAAY,EAAE,iBAAiB;QAC/B,0EAA0E;QAC1E,6EAA6E;QAC7E,UAAU,EAAE,QAAQ;QACpB,0EAA0E;QAC1E,2CAA2C;QAC3C,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC9B,WAAW,EACT,mUAAmU;KACtU;IACD,8EAA8E;IAC9E,6EAA6E;IAC7E,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE;QACR;YACE,IAAI,EAAE,MAAM;YACZ,WAAW,EAAE,+EAA+E;YAC5F,IAAI,EAAE,aAAa;YACnB,6EAA6E;YAC7E,sFAAsF;YACtF,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE;YAC9C,2EAA2E;YAC3E,8BAA8B;YAC9B,WAAW,EAAE,iBAAiB;YAC9B,8EAA8E;YAC9E,gFAAgF;YAChF,8EAA8E;YAC9E,6EAA6E;YAC7E,sEAAsE;YACtE,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE;SACnD;KACF;IACD,kFAAkF;IAClF,4EAA4E;IAC5E,mBAAmB,EAAE,cAAc;CACpC,CAAC,CAAC"}
|