@opensip-cli/external-tool-adapter 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__/acceptance-harness.test.d.ts +2 -0
- package/dist/__tests__/acceptance-harness.test.d.ts.map +1 -0
- package/dist/__tests__/acceptance-harness.test.js +106 -0
- package/dist/__tests__/acceptance-harness.test.js.map +1 -0
- package/dist/__tests__/artifact-path.test.d.ts +2 -0
- package/dist/__tests__/artifact-path.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-path.test.js +19 -0
- package/dist/__tests__/artifact-path.test.js.map +1 -0
- package/dist/__tests__/binary-resolver.test.d.ts +2 -0
- package/dist/__tests__/binary-resolver.test.d.ts.map +1 -0
- package/dist/__tests__/binary-resolver.test.js +64 -0
- package/dist/__tests__/binary-resolver.test.js.map +1 -0
- package/dist/__tests__/define-external-tool-adapter.test.d.ts +2 -0
- package/dist/__tests__/define-external-tool-adapter.test.d.ts.map +1 -0
- package/dist/__tests__/define-external-tool-adapter.test.js +165 -0
- package/dist/__tests__/define-external-tool-adapter.test.js.map +1 -0
- package/dist/__tests__/doctor-command.test.d.ts +2 -0
- package/dist/__tests__/doctor-command.test.d.ts.map +1 -0
- package/dist/__tests__/doctor-command.test.js +124 -0
- package/dist/__tests__/doctor-command.test.js.map +1 -0
- package/dist/__tests__/exit-model.test.d.ts +2 -0
- package/dist/__tests__/exit-model.test.d.ts.map +1 -0
- package/dist/__tests__/exit-model.test.js +30 -0
- package/dist/__tests__/exit-model.test.js.map +1 -0
- package/dist/__tests__/fingerprint.test.d.ts +2 -0
- package/dist/__tests__/fingerprint.test.d.ts.map +1 -0
- package/dist/__tests__/fingerprint.test.js +39 -0
- package/dist/__tests__/fingerprint.test.js.map +1 -0
- package/dist/__tests__/gate-render.test.d.ts +2 -0
- package/dist/__tests__/gate-render.test.d.ts.map +1 -0
- package/dist/__tests__/gate-render.test.js +82 -0
- package/dist/__tests__/gate-render.test.js.map +1 -0
- package/dist/__tests__/ingest-json.test.d.ts +2 -0
- package/dist/__tests__/ingest-json.test.d.ts.map +1 -0
- package/dist/__tests__/ingest-json.test.js +53 -0
- package/dist/__tests__/ingest-json.test.js.map +1 -0
- package/dist/__tests__/ingest-sarif.test.d.ts +2 -0
- package/dist/__tests__/ingest-sarif.test.d.ts.map +1 -0
- package/dist/__tests__/ingest-sarif.test.js +283 -0
- package/dist/__tests__/ingest-sarif.test.js.map +1 -0
- package/dist/__tests__/manifest-commands.test.d.ts +2 -0
- package/dist/__tests__/manifest-commands.test.d.ts.map +1 -0
- package/dist/__tests__/manifest-commands.test.js +67 -0
- package/dist/__tests__/manifest-commands.test.js.map +1 -0
- package/dist/__tests__/provenance.test.d.ts +2 -0
- package/dist/__tests__/provenance.test.d.ts.map +1 -0
- package/dist/__tests__/provenance.test.js +48 -0
- package/dist/__tests__/provenance.test.js.map +1 -0
- package/dist/__tests__/redact.test.d.ts +2 -0
- package/dist/__tests__/redact.test.d.ts.map +1 -0
- package/dist/__tests__/redact.test.js +37 -0
- package/dist/__tests__/redact.test.js.map +1 -0
- package/dist/__tests__/run-loop-artifact.test.d.ts +21 -0
- package/dist/__tests__/run-loop-artifact.test.d.ts.map +1 -0
- package/dist/__tests__/run-loop-artifact.test.js +186 -0
- package/dist/__tests__/run-loop-artifact.test.js.map +1 -0
- package/dist/__tests__/run-loop-exit.test.d.ts +21 -0
- package/dist/__tests__/run-loop-exit.test.d.ts.map +1 -0
- package/dist/__tests__/run-loop-exit.test.js +123 -0
- package/dist/__tests__/run-loop-exit.test.js.map +1 -0
- package/dist/__tests__/run-loop-gate.test.d.ts +10 -0
- package/dist/__tests__/run-loop-gate.test.d.ts.map +1 -0
- package/dist/__tests__/run-loop-gate.test.js +159 -0
- package/dist/__tests__/run-loop-gate.test.js.map +1 -0
- package/dist/__tests__/session-payload.test.d.ts +12 -0
- package/dist/__tests__/session-payload.test.d.ts.map +1 -0
- package/dist/__tests__/session-payload.test.js +131 -0
- package/dist/__tests__/session-payload.test.js.map +1 -0
- package/dist/__tests__/severity-map.test.d.ts +2 -0
- package/dist/__tests__/severity-map.test.d.ts.map +1 -0
- package/dist/__tests__/severity-map.test.js +57 -0
- package/dist/__tests__/severity-map.test.js.map +1 -0
- package/dist/acceptance-harness.d.ts +48 -0
- package/dist/acceptance-harness.d.ts.map +1 -0
- package/dist/acceptance-harness.js +78 -0
- package/dist/acceptance-harness.js.map +1 -0
- package/dist/adapter-config.d.ts +58 -0
- package/dist/adapter-config.d.ts.map +1 -0
- package/dist/adapter-config.js +73 -0
- package/dist/adapter-config.js.map +1 -0
- package/dist/adapter-manifest.d.ts +57 -0
- package/dist/adapter-manifest.d.ts.map +1 -0
- package/dist/adapter-manifest.js +68 -0
- package/dist/adapter-manifest.js.map +1 -0
- package/dist/artifact-path.d.ts +26 -0
- package/dist/artifact-path.d.ts.map +1 -0
- package/dist/artifact-path.js +22 -0
- package/dist/artifact-path.js.map +1 -0
- package/dist/binary-resolver.d.ts +51 -0
- package/dist/binary-resolver.d.ts.map +1 -0
- package/dist/binary-resolver.js +66 -0
- package/dist/binary-resolver.js.map +1 -0
- package/dist/define-external-tool-adapter.d.ts +25 -0
- package/dist/define-external-tool-adapter.d.ts.map +1 -0
- package/dist/define-external-tool-adapter.js +149 -0
- package/dist/define-external-tool-adapter.js.map +1 -0
- package/dist/doctor-command.d.ts +81 -0
- package/dist/doctor-command.d.ts.map +1 -0
- package/dist/doctor-command.js +160 -0
- package/dist/doctor-command.js.map +1 -0
- package/dist/exit-model.d.ts +33 -0
- package/dist/exit-model.d.ts.map +1 -0
- package/dist/exit-model.js +35 -0
- package/dist/exit-model.js.map +1 -0
- package/dist/fingerprint.d.ts +26 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +32 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/gate-render.d.ts +18 -0
- package/dist/gate-render.d.ts.map +1 -0
- package/dist/gate-render.js +25 -0
- package/dist/gate-render.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest-json.d.ts +32 -0
- package/dist/ingest-json.d.ts.map +1 -0
- package/dist/ingest-json.js +66 -0
- package/dist/ingest-json.js.map +1 -0
- package/dist/ingest-sarif.d.ts +113 -0
- package/dist/ingest-sarif.d.ts.map +1 -0
- package/dist/ingest-sarif.js +158 -0
- package/dist/ingest-sarif.js.map +1 -0
- package/dist/manifest-commands.d.ts +23 -0
- package/dist/manifest-commands.d.ts.map +1 -0
- package/dist/manifest-commands.js +47 -0
- package/dist/manifest-commands.js.map +1 -0
- package/dist/process-exec.d.ts +51 -0
- package/dist/process-exec.d.ts.map +1 -0
- package/dist/process-exec.js +99 -0
- package/dist/process-exec.js.map +1 -0
- package/dist/provenance.d.ts +19 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +31 -0
- package/dist/provenance.js.map +1 -0
- package/dist/redact.d.ts +24 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +38 -0
- package/dist/redact.js.map +1 -0
- package/dist/run-context.d.ts +24 -0
- package/dist/run-context.d.ts.map +1 -0
- package/dist/run-context.js +36 -0
- package/dist/run-context.js.map +1 -0
- package/dist/run-loop.d.ts +64 -0
- package/dist/run-loop.d.ts.map +1 -0
- package/dist/run-loop.js +320 -0
- package/dist/run-loop.js.map +1 -0
- package/dist/scan-emit.d.ts +81 -0
- package/dist/scan-emit.d.ts.map +1 -0
- package/dist/scan-emit.js +125 -0
- package/dist/scan-emit.js.map +1 -0
- package/dist/session-payload.d.ts +81 -0
- package/dist/session-payload.d.ts.map +1 -0
- package/dist/session-payload.js +86 -0
- package/dist/session-payload.js.map +1 -0
- package/dist/severity-map.d.ts +43 -0
- package/dist/severity-map.d.ts.map +1 -0
- package/dist/severity-map.js +84 -0
- package/dist/severity-map.js.map +1 -0
- package/dist/types.d.ts +228 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/dist/version-command.d.ts +36 -0
- package/dist/version-command.d.ts.map +1 -0
- package/dist/version-command.js +74 -0
- package/dist/version-command.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The scan run loop (ADR-0090 §4 / ADR-0091).
|
|
3
|
+
*
|
|
4
|
+
* resolve binary → build run context → probe version → compute artifact path →
|
|
5
|
+
* `execFile` (no shell, timeout + output cap) → interpret exit → read native
|
|
6
|
+
* output → persist via `cli.writeArtifact` (the host seam — NEVER raw fs) →
|
|
7
|
+
* parse (SARIF via shared `ingestSarif`; JSON via the descriptor `parse`) →
|
|
8
|
+
* stamp provenance → `buildSignalEnvelope` + worker-side fingerprint stamping →
|
|
9
|
+
* emit via `cli.emitEnvelope` (`--json`) / `cli.render` (human) → `cli.deliverSignals`
|
|
10
|
+
* (host derives the findings exit from the verdict) → return a `ToolRunCompletion`.
|
|
11
|
+
*
|
|
12
|
+
* For an INSTALLED adapter this body runs WORKER-SIDE; every `cli.*` call here is
|
|
13
|
+
* captured by the worker's recording context and replayed through the host seams
|
|
14
|
+
* (`writeArtifact` via host RPC; `emitEnvelope`/`render`/`deliverSignals`/
|
|
15
|
+
* `setExitCode` via the forwarded-result record). So the substrate never imports
|
|
16
|
+
* `cli` and the host stays the only privileged-effect process.
|
|
17
|
+
*
|
|
18
|
+
* This IO orchestration is excluded from unit coverage; pure helpers are covered
|
|
19
|
+
* directly and adapter worker E2Es exercise the whole loop.
|
|
20
|
+
*/
|
|
21
|
+
import type { BinaryResolveDeps } from './binary-resolver.js';
|
|
22
|
+
import type { ProbeVersionInput, ProcessResult, RunProcessInput } from './process-exec.js';
|
|
23
|
+
import type { ScanCompletion } from './scan-emit.js';
|
|
24
|
+
import type { BinarySpec, ExternalCommandSpec } from './types.js';
|
|
25
|
+
import type { FingerprintStrategy, ToolCliContext } from '@opensip-cli/core';
|
|
26
|
+
/** Injectable IO seam (real impls default; tests pass stubs). */
|
|
27
|
+
export interface ScanLoopDeps {
|
|
28
|
+
readonly binaryDeps: BinaryResolveDeps;
|
|
29
|
+
readonly runProcess: (input: RunProcessInput) => Promise<ProcessResult>;
|
|
30
|
+
readonly probeVersion: (input: ProbeVersionInput) => string | undefined;
|
|
31
|
+
readonly readFile: (path: string) => string;
|
|
32
|
+
/** The artifact file's byte size — used to cap the read (OOM guard) before {@link readFile}. */
|
|
33
|
+
readonly fileSize: (path: string) => number;
|
|
34
|
+
/**
|
|
35
|
+
* The environment the operator binary-pin (`OPENSIP_<TOOL>_BIN`) is read from
|
|
36
|
+
* — injected (mirrors the doctor/version probe's `DoctorProbeDeps.env`) so the
|
|
37
|
+
* read flows through a seam, not a raw `process.env` reach, and stays
|
|
38
|
+
* unit-testable. The pin name is per-tool/dynamic, so it cannot be a static
|
|
39
|
+
* EnvRegistry `EnvVarSpec`.
|
|
40
|
+
*/
|
|
41
|
+
readonly env: NodeJS.ProcessEnv;
|
|
42
|
+
readonly timeoutMs: number;
|
|
43
|
+
readonly maxBuffer: number;
|
|
44
|
+
}
|
|
45
|
+
export interface ScanLoopInput {
|
|
46
|
+
readonly cli: ToolCliContext;
|
|
47
|
+
readonly tool: string;
|
|
48
|
+
readonly adapterPackage?: string;
|
|
49
|
+
readonly command: ExternalCommandSpec;
|
|
50
|
+
readonly binary: BinarySpec;
|
|
51
|
+
readonly fingerprintStrategy: FingerprintStrategy;
|
|
52
|
+
/** The parsed command flags (`json` / `cwd` / `reportTo` / `apiKey`). */
|
|
53
|
+
readonly opts: Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Run one scanner command end-to-end.
|
|
57
|
+
*
|
|
58
|
+
* Returns the {@link ScanCompletion} the host persists/dispatches, or `undefined`
|
|
59
|
+
* when the invocation is a config error (`--gate-save` + `--gate-compare` together)
|
|
60
|
+
* — the loop has already recorded the failure + exit via `cli.reportFailure`, so
|
|
61
|
+
* there is no envelope/session to return.
|
|
62
|
+
*/
|
|
63
|
+
export declare function runScanLoop(input: ScanLoopInput, overrides?: Partial<ScanLoopDeps>): Promise<ScanCompletion | undefined>;
|
|
64
|
+
//# sourceMappingURL=run-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-loop.d.ts","sourceRoot":"","sources":["../src/run-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAwBH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAGV,UAAU,EACV,mBAAmB,EAEpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,mBAAmB,EAAU,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAUrF,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACxE,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAaD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAClD,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AA2HD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,aAAa,EACpB,SAAS,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAChC,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAoMrC"}
|
package/dist/run-loop.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The scan run loop (ADR-0090 §4 / ADR-0091).
|
|
3
|
+
*
|
|
4
|
+
* resolve binary → build run context → probe version → compute artifact path →
|
|
5
|
+
* `execFile` (no shell, timeout + output cap) → interpret exit → read native
|
|
6
|
+
* output → persist via `cli.writeArtifact` (the host seam — NEVER raw fs) →
|
|
7
|
+
* parse (SARIF via shared `ingestSarif`; JSON via the descriptor `parse`) →
|
|
8
|
+
* stamp provenance → `buildSignalEnvelope` + worker-side fingerprint stamping →
|
|
9
|
+
* emit via `cli.emitEnvelope` (`--json`) / `cli.render` (human) → `cli.deliverSignals`
|
|
10
|
+
* (host derives the findings exit from the verdict) → return a `ToolRunCompletion`.
|
|
11
|
+
*
|
|
12
|
+
* For an INSTALLED adapter this body runs WORKER-SIDE; every `cli.*` call here is
|
|
13
|
+
* captured by the worker's recording context and replayed through the host seams
|
|
14
|
+
* (`writeArtifact` via host RPC; `emitEnvelope`/`render`/`deliverSignals`/
|
|
15
|
+
* `setExitCode` via the forwarded-result record). So the substrate never imports
|
|
16
|
+
* `cli` and the host stays the only privileged-effect process.
|
|
17
|
+
*
|
|
18
|
+
* This IO orchestration is excluded from unit coverage; pure helpers are covered
|
|
19
|
+
* directly and adapter worker E2Es exercise the whole loop.
|
|
20
|
+
*/
|
|
21
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
22
|
+
import { performance } from 'node:perf_hooks';
|
|
23
|
+
import { buildSignalEnvelope, EXIT_CODES } from '@opensip-cli/contracts';
|
|
24
|
+
import { ConfigurationError, isErrorSignal, resolveProjectPaths, resolveVerdictPolicy, TimeoutError, ToolError, } from '@opensip-cli/core';
|
|
25
|
+
import { resolveBinary, defaultBinaryEnvVar } from './binary-resolver.js';
|
|
26
|
+
import { DEFAULT_EXIT_MODEL, interpretExit } from './exit-model.js';
|
|
27
|
+
import { asObject, getString, safeParseJson } from './ingest-json.js';
|
|
28
|
+
import { ingestSarif } from './ingest-sarif.js';
|
|
29
|
+
import { defaultBinaryDeps, probeBinaryVersion, runScannerProcess } from './process-exec.js';
|
|
30
|
+
import { stampProvenanceAll } from './provenance.js';
|
|
31
|
+
import { buildAdapterRunContext } from './run-context.js';
|
|
32
|
+
import { buildScanCompletion, deliverOptions, emitScanCompletion } from './scan-emit.js';
|
|
33
|
+
/** Logger `module` field for every event this loop emits. */
|
|
34
|
+
const MODULE = 'external-tool-adapter';
|
|
35
|
+
/** Default scanner process budget. */
|
|
36
|
+
const DEFAULT_TIMEOUT_MS = 300_000;
|
|
37
|
+
const DEFAULT_MAX_BUFFER = 64 * 1024 * 1024;
|
|
38
|
+
const STDERR_TAIL = 2000;
|
|
39
|
+
const DEFAULT_DEPS = {
|
|
40
|
+
binaryDeps: defaultBinaryDeps,
|
|
41
|
+
runProcess: runScannerProcess,
|
|
42
|
+
probeVersion: probeBinaryVersion,
|
|
43
|
+
readFile: (path) => readFileSync(path, 'utf8'),
|
|
44
|
+
fileSize: (path) => statSync(path).size,
|
|
45
|
+
env: process.env,
|
|
46
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
47
|
+
maxBuffer: DEFAULT_MAX_BUFFER,
|
|
48
|
+
};
|
|
49
|
+
const ARTIFACT_EXT = {
|
|
50
|
+
sarif: 'sarif',
|
|
51
|
+
json: 'json',
|
|
52
|
+
stdout: 'out',
|
|
53
|
+
};
|
|
54
|
+
function defaultArtifactName(tool, kind) {
|
|
55
|
+
return `${tool}.${ARTIFACT_EXT[kind]}`;
|
|
56
|
+
}
|
|
57
|
+
/** Read the namespaced operator pin `binaries.<tool>.path` from the resolved config. */
|
|
58
|
+
function configuredBinaryPath(config, tool) {
|
|
59
|
+
return getString(asObject(config.binaries)?.[tool], 'path');
|
|
60
|
+
}
|
|
61
|
+
/** Parse native output into signals (SARIF via shared ingest; JSON/stdout via the descriptor). */
|
|
62
|
+
function parseSignals(command, raw, ctx) {
|
|
63
|
+
if (command.output.kind === 'sarif') {
|
|
64
|
+
const parsed = safeParseJson(raw);
|
|
65
|
+
if (!parsed.ok)
|
|
66
|
+
return [];
|
|
67
|
+
return ingestSarif(parsed.value, { source: ctx.tool });
|
|
68
|
+
}
|
|
69
|
+
if (command.parse === undefined)
|
|
70
|
+
return [];
|
|
71
|
+
const json = command.output.kind === 'json' ? safeParseJson(raw) : undefined;
|
|
72
|
+
const payload = {
|
|
73
|
+
kind: command.output.kind,
|
|
74
|
+
raw,
|
|
75
|
+
...(json?.ok === true ? { json: json.value } : {}),
|
|
76
|
+
};
|
|
77
|
+
return command.parse(payload, ctx);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Read a FILE-backed scanner report and classify its validity (A11). Size-guarded:
|
|
81
|
+
* an over-cap report (OOM guard) is never read into memory. Returns the reason a
|
|
82
|
+
* report is unusable so the caller can fault with a size-distinguished message and
|
|
83
|
+
* never overwrite the on-disk report with an empty/truncated buffer.
|
|
84
|
+
*/
|
|
85
|
+
function readReportArtifact(path, deps) {
|
|
86
|
+
let size = -1;
|
|
87
|
+
try {
|
|
88
|
+
size = deps.fileSize(path);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return { raw: '', artifactValid: false, invalidReason: 'missing' };
|
|
92
|
+
}
|
|
93
|
+
if (size > deps.maxBuffer)
|
|
94
|
+
return { raw: '', artifactValid: false, invalidReason: 'oversize' };
|
|
95
|
+
let raw;
|
|
96
|
+
try {
|
|
97
|
+
raw = deps.readFile(path);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return { raw: '', artifactValid: false, invalidReason: 'missing' };
|
|
101
|
+
}
|
|
102
|
+
if (raw.length === 0)
|
|
103
|
+
return { raw, artifactValid: false, invalidReason: 'empty' };
|
|
104
|
+
if (!safeParseJson(raw).ok)
|
|
105
|
+
return { raw, artifactValid: false, invalidReason: 'unparseable' };
|
|
106
|
+
return { raw, artifactValid: true };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A3: append the descriptor's exclusion of opensip's `.runtime` artifact store to
|
|
110
|
+
* the scanner argv. The substrate supplies the path (run-context-driven) while the
|
|
111
|
+
* descriptor supplies the per-scanner flag shape; a config-file form (gitleaks's
|
|
112
|
+
* allowlist) is written through the host `writeArtifact` seam into the per-run dir,
|
|
113
|
+
* never a raw substrate fs write. No user-facing flag — the command manifest is
|
|
114
|
+
* unchanged.
|
|
115
|
+
*/
|
|
116
|
+
async function applyScanExclusion(cli, command, ctx, args) {
|
|
117
|
+
if (command.excludeScan === undefined)
|
|
118
|
+
return;
|
|
119
|
+
const exclusion = command.excludeScan({
|
|
120
|
+
excludePath: resolveProjectPaths(ctx.projectRoot).runtimeDir,
|
|
121
|
+
configPath: (name) => ctx.artifactPath(name),
|
|
122
|
+
});
|
|
123
|
+
if (exclusion.configFile !== undefined) {
|
|
124
|
+
await cli.writeArtifact(exclusion.configFile.path, exclusion.configFile.contents);
|
|
125
|
+
}
|
|
126
|
+
if (exclusion.args !== undefined)
|
|
127
|
+
args.push(...exclusion.args);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* A11: raise the typed `ADAPTER.ARTIFACT.INVALID` fault for an unusable file-backed
|
|
131
|
+
* report, distinguishing the oversize-cap case from missing/empty/unparseable.
|
|
132
|
+
*/
|
|
133
|
+
function faultInvalidArtifact(tool, command, read, maxBuffer, stderr) {
|
|
134
|
+
const maxMiB = Math.floor(maxBuffer / (1024 * 1024));
|
|
135
|
+
const detail = read.invalidReason === 'oversize'
|
|
136
|
+
? `report exceeded the ${String(maxMiB)} MiB cap`
|
|
137
|
+
: `report ${read.invalidReason ?? 'invalid'}`;
|
|
138
|
+
throw new ToolError(`${tool} produced no usable ${command.output.kind} report (${detail}).`, 'ADAPTER.ARTIFACT.INVALID', { stderrTail: stderr.slice(-STDERR_TAIL) });
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Run one scanner command end-to-end.
|
|
142
|
+
*
|
|
143
|
+
* Returns the {@link ScanCompletion} the host persists/dispatches, or `undefined`
|
|
144
|
+
* when the invocation is a config error (`--gate-save` + `--gate-compare` together)
|
|
145
|
+
* — the loop has already recorded the failure + exit via `cli.reportFailure`, so
|
|
146
|
+
* there is no envelope/session to return.
|
|
147
|
+
*/
|
|
148
|
+
export async function runScanLoop(input, overrides) {
|
|
149
|
+
const deps = { ...DEFAULT_DEPS, ...overrides };
|
|
150
|
+
const { cli, tool, command, binary } = input;
|
|
151
|
+
// ADR-0036 gate-ratchet: --gate-save and --gate-compare are mutually exclusive
|
|
152
|
+
// (mirrors fit's runGateMode). Validate BEFORE any IO so a misconfiguration
|
|
153
|
+
// fails fast — no wasted scanner subprocess — and return early (the host replays
|
|
154
|
+
// the recorded reportFailure → exit 2).
|
|
155
|
+
if (input.opts.gateSave === true && input.opts.gateCompare === true) {
|
|
156
|
+
cli.logger.warn({
|
|
157
|
+
evt: 'adapter.gate.config_error',
|
|
158
|
+
module: MODULE,
|
|
159
|
+
tool,
|
|
160
|
+
reason: 'mutually-exclusive flags',
|
|
161
|
+
});
|
|
162
|
+
await cli.reportFailure({
|
|
163
|
+
message: 'Error: --gate-save and --gate-compare are mutually exclusive.',
|
|
164
|
+
exitCode: EXIT_CODES.CONFIGURATION_ERROR,
|
|
165
|
+
jsonRequested: input.opts.json === true,
|
|
166
|
+
});
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
const config = (cli.scope.toolConfig?.[tool] ?? {});
|
|
170
|
+
const envVar = binary.envVar ?? defaultBinaryEnvVar(tool);
|
|
171
|
+
const resolution = resolveBinary({
|
|
172
|
+
command: binary.command,
|
|
173
|
+
configuredPath: configuredBinaryPath(config, tool),
|
|
174
|
+
envPath: deps.env[envVar],
|
|
175
|
+
}, deps.binaryDeps);
|
|
176
|
+
if (!resolution.found) {
|
|
177
|
+
throw new ConfigurationError(`${tool}: ${resolution.reason}. Run 'opensip ${tool} doctor' for setup help.`, { code: 'ADAPTER.BINARY.NOT_FOUND' });
|
|
178
|
+
}
|
|
179
|
+
const version = deps.probeVersion({
|
|
180
|
+
path: resolution.path,
|
|
181
|
+
versionArgs: binary.versionArgs,
|
|
182
|
+
parse: binary.versionParse,
|
|
183
|
+
timeoutMs: deps.timeoutMs,
|
|
184
|
+
});
|
|
185
|
+
const ctx = buildAdapterRunContext({
|
|
186
|
+
cli,
|
|
187
|
+
tool,
|
|
188
|
+
adapterPackage: input.adapterPackage,
|
|
189
|
+
binary: { path: resolution.path, layer: resolution.layer, version },
|
|
190
|
+
config,
|
|
191
|
+
});
|
|
192
|
+
const artifactName = command.output.path ?? defaultArtifactName(tool, command.output.kind);
|
|
193
|
+
const artifactFullPath = ctx.artifactPath(artifactName);
|
|
194
|
+
// A1/A7: create the per-run artifact dir through the host seam before the
|
|
195
|
+
// scanner opens its report path. The substrate never mkdirs `.runtime` itself;
|
|
196
|
+
// host RPC owns the privileged FS effect.
|
|
197
|
+
await cli.ensureArtifactDir(artifactFullPath);
|
|
198
|
+
// A3: keep the scanner off opensip's own persisted reports under `.runtime/`
|
|
199
|
+
// (see applyScanExclusion) — inherited by every adapter, no user flag.
|
|
200
|
+
const args = [...command.args(ctx)];
|
|
201
|
+
await applyScanExclusion(cli, command, ctx, args);
|
|
202
|
+
cli.logger.info({
|
|
203
|
+
evt: 'adapter.binary.resolved',
|
|
204
|
+
module: MODULE,
|
|
205
|
+
tool,
|
|
206
|
+
layer: resolution.layer,
|
|
207
|
+
path: resolution.path,
|
|
208
|
+
version: version ?? null,
|
|
209
|
+
});
|
|
210
|
+
const startedAt = new Date().toISOString();
|
|
211
|
+
const begin = performance.now();
|
|
212
|
+
const proc = await deps.runProcess({
|
|
213
|
+
command: resolution.path,
|
|
214
|
+
args,
|
|
215
|
+
cwd: ctx.projectRoot,
|
|
216
|
+
timeoutMs: deps.timeoutMs,
|
|
217
|
+
maxBuffer: deps.maxBuffer,
|
|
218
|
+
});
|
|
219
|
+
const durationMs = Math.max(0, Math.round(performance.now() - begin));
|
|
220
|
+
if (proc.timedOut) {
|
|
221
|
+
cli.logger.warn({
|
|
222
|
+
evt: 'adapter.scan.faulted',
|
|
223
|
+
module: MODULE,
|
|
224
|
+
tool,
|
|
225
|
+
reason: 'timeout',
|
|
226
|
+
});
|
|
227
|
+
throw new TimeoutError(`${tool} scan timed out after ${String(deps.timeoutMs)}ms`, {
|
|
228
|
+
code: 'ADAPTER.SCAN.TIMEOUT',
|
|
229
|
+
stderrTail: proc.stderr.slice(-STDERR_TAIL),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// Read the native output: stdout commands use the captured stdout (always valid);
|
|
233
|
+
// file-backed commands read + classify the report (A11) via readReportArtifact.
|
|
234
|
+
const read = command.output.kind === 'stdout'
|
|
235
|
+
? { raw: proc.stdout, artifactValid: true }
|
|
236
|
+
: readReportArtifact(artifactFullPath, deps);
|
|
237
|
+
const verdict = interpretExit(proc.code, command.exitCodes ?? DEFAULT_EXIT_MODEL, {
|
|
238
|
+
artifactValid: read.artifactValid,
|
|
239
|
+
});
|
|
240
|
+
if (verdict === 'fault') {
|
|
241
|
+
cli.logger.warn({
|
|
242
|
+
evt: 'adapter.scan.faulted',
|
|
243
|
+
module: MODULE,
|
|
244
|
+
tool,
|
|
245
|
+
code: proc.code,
|
|
246
|
+
});
|
|
247
|
+
throw new ToolError(`${tool} scan failed (exit ${String(proc.code)})`, 'ADAPTER.SCAN.FAULT', {
|
|
248
|
+
stderrTail: proc.stderr.slice(-STDERR_TAIL),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
// A11: a FILE-backed scanner MUST produce a readable, parseable report. An invalid
|
|
252
|
+
// artifact is a FAULT even under an `ok`/`findings` verdict — otherwise trivy's
|
|
253
|
+
// `ok:[0]` model would read a broken report as a clean PASS and the `writeArtifact`
|
|
254
|
+
// below would overwrite the real report with the empty buffer. We throw BEFORE
|
|
255
|
+
// writeArtifact, so the scanner's report on disk is never destroyed.
|
|
256
|
+
if (!read.artifactValid) {
|
|
257
|
+
cli.logger.warn({
|
|
258
|
+
evt: 'adapter.scan.faulted',
|
|
259
|
+
module: MODULE,
|
|
260
|
+
tool,
|
|
261
|
+
reason: `artifact-${read.invalidReason ?? 'invalid'}`,
|
|
262
|
+
});
|
|
263
|
+
faultInvalidArtifact(tool, command, read, deps.maxBuffer, proc.stderr);
|
|
264
|
+
}
|
|
265
|
+
const raw = read.raw;
|
|
266
|
+
// Persist the raw artifact through the HOST seam (0600 + retention, ADR-0080/0091).
|
|
267
|
+
await cli.writeArtifact(artifactFullPath, raw);
|
|
268
|
+
cli.logger.info({
|
|
269
|
+
evt: 'adapter.artifact.stored',
|
|
270
|
+
module: MODULE,
|
|
271
|
+
tool,
|
|
272
|
+
path: artifactFullPath,
|
|
273
|
+
bytes: raw.length,
|
|
274
|
+
});
|
|
275
|
+
const parsed = parseSignals(command, raw, ctx);
|
|
276
|
+
const provenance = {
|
|
277
|
+
tool,
|
|
278
|
+
adapterPackage: input.adapterPackage,
|
|
279
|
+
binaryPath: resolution.path,
|
|
280
|
+
binaryVersion: version,
|
|
281
|
+
args,
|
|
282
|
+
configPath: ctx.configPath,
|
|
283
|
+
};
|
|
284
|
+
const signals = stampProvenanceAll(parsed, provenance);
|
|
285
|
+
const unitPassed = !signals.some(isErrorSignal);
|
|
286
|
+
const envelope = buildSignalEnvelope({
|
|
287
|
+
tool,
|
|
288
|
+
runId: ctx.runId,
|
|
289
|
+
createdAt: startedAt,
|
|
290
|
+
units: [{ slug: command.name, passed: unitPassed, violationCount: signals.length, durationMs }],
|
|
291
|
+
signals,
|
|
292
|
+
policy: resolveVerdictPolicy(tool),
|
|
293
|
+
runFaulted: false,
|
|
294
|
+
fingerprintStrategy: input.fingerprintStrategy,
|
|
295
|
+
});
|
|
296
|
+
const deliver = deliverOptions(input.opts, ctx.projectRoot);
|
|
297
|
+
// The session contribution (incl. the dashboard-shaped grouped payload) is shaped
|
|
298
|
+
// in a sibling helper so this orchestration body stays flat (file-length budget).
|
|
299
|
+
const completion = buildScanCompletion({
|
|
300
|
+
tool,
|
|
301
|
+
cwd: deliver.cwd,
|
|
302
|
+
envelope,
|
|
303
|
+
signals,
|
|
304
|
+
binary: { path: resolution.path, layer: resolution.layer, version: version ?? null },
|
|
305
|
+
artifact: artifactFullPath,
|
|
306
|
+
durationMs,
|
|
307
|
+
});
|
|
308
|
+
// Emit + deliver + return. The gate-ratchet branch (ADR-0036) and the normal
|
|
309
|
+
// emit live in a sibling helper so this orchestration body stays flat.
|
|
310
|
+
return emitScanCompletion({
|
|
311
|
+
cli,
|
|
312
|
+
tool,
|
|
313
|
+
opts: input.opts,
|
|
314
|
+
envelope,
|
|
315
|
+
signalCount: signals.length,
|
|
316
|
+
deliver,
|
|
317
|
+
completion,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=run-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-loop.js","sourceRoot":"","sources":["../src/run-loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Scan-result emission + the gate-ratchet decision (ADR-0036).
|
|
3
|
+
*
|
|
4
|
+
* Split from {@link ./run-loop} (the IO orchestration: resolve → exec → parse →
|
|
5
|
+
* envelope) so the "what to emit + which gate branch" decision is a small,
|
|
6
|
+
* directly-testable unit and the loop body stays a flat pipeline. Every effect
|
|
7
|
+
* here is a documented `ToolCliContext` seam — for an INSTALLED adapter these run
|
|
8
|
+
* worker-side and replay through the host (FRR for `render`/`emitEnvelope`; host
|
|
9
|
+
* RPC for `saveBaseline`/`compareBaseline`/`deliverSignals`).
|
|
10
|
+
*/
|
|
11
|
+
import type { BinaryResolutionLayer } from './types.js';
|
|
12
|
+
import type { SignalEnvelope } from '@opensip-cli/contracts';
|
|
13
|
+
import type { Signal, ToolCliContext } from '@opensip-cli/core';
|
|
14
|
+
/** What the host persists + dispatches after a scan (the run loop's return shape). */
|
|
15
|
+
export interface ScanCompletion {
|
|
16
|
+
readonly envelope: SignalEnvelope;
|
|
17
|
+
readonly session: {
|
|
18
|
+
readonly tool: string;
|
|
19
|
+
readonly cwd: string;
|
|
20
|
+
readonly score: number;
|
|
21
|
+
readonly passed: boolean;
|
|
22
|
+
readonly payload: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** The `deliverSignals` options bag (cwd + optional egress targets). */
|
|
26
|
+
export interface DeliverOpts {
|
|
27
|
+
readonly cwd: string;
|
|
28
|
+
readonly reportTo?: string;
|
|
29
|
+
readonly apiKey?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Build the `deliverSignals` options bag from the parsed flags (cwd + optional egress). */
|
|
32
|
+
export declare function deliverOptions(opts: Record<string, unknown>, fallbackCwd: string): DeliverOpts;
|
|
33
|
+
/** Inputs the run loop hands {@link buildScanCompletion} to shape the session row. */
|
|
34
|
+
export interface ScanCompletionInput {
|
|
35
|
+
readonly tool: string;
|
|
36
|
+
readonly cwd: string;
|
|
37
|
+
readonly envelope: SignalEnvelope;
|
|
38
|
+
readonly signals: readonly Signal[];
|
|
39
|
+
readonly binary: {
|
|
40
|
+
readonly path: string;
|
|
41
|
+
readonly layer: BinaryResolutionLayer;
|
|
42
|
+
readonly version: string | null;
|
|
43
|
+
};
|
|
44
|
+
readonly artifact: string;
|
|
45
|
+
readonly durationMs: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Shape the {@link ScanCompletion} the host persists/dispatches. The session
|
|
49
|
+
* payload carries the dashboard-shaped grouped detail (`__version`/`summary`/
|
|
50
|
+
* `checks[]` from {@link buildAdapterSessionPayload}) so the HTML report renders
|
|
51
|
+
* the scan's findings instead of falsely "clean" — plus the operational
|
|
52
|
+
* provenance the row also keeps (binary/artifact/findings/durationMs). Built from
|
|
53
|
+
* the already-redacted signals, so no raw secret reaches the persisted row.
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildScanCompletion(input: ScanCompletionInput): ScanCompletion;
|
|
56
|
+
export interface EmitScanCompletionInput {
|
|
57
|
+
readonly cli: ToolCliContext;
|
|
58
|
+
readonly tool: string;
|
|
59
|
+
readonly opts: Record<string, unknown>;
|
|
60
|
+
readonly envelope: SignalEnvelope;
|
|
61
|
+
readonly signalCount: number;
|
|
62
|
+
readonly deliver: DeliverOpts;
|
|
63
|
+
readonly completion: ScanCompletion;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Emit the scan result, deliver its signals, and return the completion. Branches:
|
|
67
|
+
*
|
|
68
|
+
* - `--gate-save` (ADR-0020/0035): record the baseline via the host seam, render
|
|
69
|
+
* `gate-done`, and deliver WITHOUT a runFailed override so the host derives the
|
|
70
|
+
* findings exit from the verdict.
|
|
71
|
+
* - `--gate-compare` (ADR-0035/0036): diff against the saved baseline, render the
|
|
72
|
+
* diff, and pass `degraded && failOnDegraded` as the host runFailed override
|
|
73
|
+
* (the findings verdict does NOT gate here — only NET-NEW findings fail).
|
|
74
|
+
* - normal: emit the envelope (`--json`) or a human summary, deliver without an
|
|
75
|
+
* override.
|
|
76
|
+
*
|
|
77
|
+
* Both gate paths still RETURN the session contribution, so a gate run persists a
|
|
78
|
+
* session alongside a normal scan.
|
|
79
|
+
*/
|
|
80
|
+
export declare function emitScanCompletion(input: EmitScanCompletionInput): Promise<ScanCompletion>;
|
|
81
|
+
//# sourceMappingURL=scan-emit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-emit.d.ts","sourceRoot":"","sources":["../src/scan-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKhE,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC3C,CAAC;CACH;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,WAAW,CAM9F;AAED,sFAAsF;AACtF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;QACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;KACjC,CAAC;IACF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,cAAc,CAiB9E;AAiCD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;CACrC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,cAAc,CAAC,CAuChG"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Scan-result emission + the gate-ratchet decision (ADR-0036).
|
|
3
|
+
*
|
|
4
|
+
* Split from {@link ./run-loop} (the IO orchestration: resolve → exec → parse →
|
|
5
|
+
* envelope) so the "what to emit + which gate branch" decision is a small,
|
|
6
|
+
* directly-testable unit and the loop body stays a flat pipeline. Every effect
|
|
7
|
+
* here is a documented `ToolCliContext` seam — for an INSTALLED adapter these run
|
|
8
|
+
* worker-side and replay through the host (FRR for `render`/`emitEnvelope`; host
|
|
9
|
+
* RPC for `saveBaseline`/`compareBaseline`/`deliverSignals`).
|
|
10
|
+
*/
|
|
11
|
+
import { resolveFailOnDegraded } from '@opensip-cli/core';
|
|
12
|
+
import { renderGateCompareLines, renderGateSaveLines } from './gate-render.js';
|
|
13
|
+
import { buildAdapterSessionPayload } from './session-payload.js';
|
|
14
|
+
/** Logger `module` field for every event this module emits. */
|
|
15
|
+
const MODULE = 'external-tool-adapter';
|
|
16
|
+
/** Build the `deliverSignals` options bag from the parsed flags (cwd + optional egress). */
|
|
17
|
+
export function deliverOptions(opts, fallbackCwd) {
|
|
18
|
+
return {
|
|
19
|
+
cwd: typeof opts.cwd === 'string' ? opts.cwd : fallbackCwd,
|
|
20
|
+
...(typeof opts.reportTo === 'string' ? { reportTo: opts.reportTo } : {}),
|
|
21
|
+
...(typeof opts.apiKey === 'string' ? { apiKey: opts.apiKey } : {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Shape the {@link ScanCompletion} the host persists/dispatches. The session
|
|
26
|
+
* payload carries the dashboard-shaped grouped detail (`__version`/`summary`/
|
|
27
|
+
* `checks[]` from {@link buildAdapterSessionPayload}) so the HTML report renders
|
|
28
|
+
* the scan's findings instead of falsely "clean" — plus the operational
|
|
29
|
+
* provenance the row also keeps (binary/artifact/findings/durationMs). Built from
|
|
30
|
+
* the already-redacted signals, so no raw secret reaches the persisted row.
|
|
31
|
+
*/
|
|
32
|
+
export function buildScanCompletion(input) {
|
|
33
|
+
return {
|
|
34
|
+
envelope: input.envelope,
|
|
35
|
+
session: {
|
|
36
|
+
tool: input.tool,
|
|
37
|
+
cwd: input.cwd,
|
|
38
|
+
score: input.envelope.verdict.score,
|
|
39
|
+
passed: input.envelope.verdict.passed,
|
|
40
|
+
payload: {
|
|
41
|
+
...buildAdapterSessionPayload(input.signals),
|
|
42
|
+
binary: input.binary,
|
|
43
|
+
artifact: input.artifact,
|
|
44
|
+
findings: input.signals.length,
|
|
45
|
+
durationMs: input.durationMs,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** The human (non-`--json`) one-line scan summary. */
|
|
51
|
+
function summaryLines(tool, signalCount, score, passed) {
|
|
52
|
+
return [
|
|
53
|
+
`${tool}: ${String(signalCount)} finding(s)`,
|
|
54
|
+
`verdict: ${passed ? 'PASS' : 'FAIL'} (score ${score.toFixed(2)})`,
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
/** One `adapter.scan.completed` log line, with the optional gate-mode annotations. */
|
|
58
|
+
function logCompleted(input) {
|
|
59
|
+
const { cli, tool, signalCount, passed, gate, degraded } = input;
|
|
60
|
+
cli.logger.info({
|
|
61
|
+
evt: 'adapter.scan.completed',
|
|
62
|
+
module: MODULE,
|
|
63
|
+
tool,
|
|
64
|
+
findings: signalCount,
|
|
65
|
+
passed,
|
|
66
|
+
...(gate === undefined ? {} : { gate }),
|
|
67
|
+
...(degraded === undefined ? {} : { degraded }),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Emit the scan result, deliver its signals, and return the completion. Branches:
|
|
72
|
+
*
|
|
73
|
+
* - `--gate-save` (ADR-0020/0035): record the baseline via the host seam, render
|
|
74
|
+
* `gate-done`, and deliver WITHOUT a runFailed override so the host derives the
|
|
75
|
+
* findings exit from the verdict.
|
|
76
|
+
* - `--gate-compare` (ADR-0035/0036): diff against the saved baseline, render the
|
|
77
|
+
* diff, and pass `degraded && failOnDegraded` as the host runFailed override
|
|
78
|
+
* (the findings verdict does NOT gate here — only NET-NEW findings fail).
|
|
79
|
+
* - normal: emit the envelope (`--json`) or a human summary, deliver without an
|
|
80
|
+
* override.
|
|
81
|
+
*
|
|
82
|
+
* Both gate paths still RETURN the session contribution, so a gate run persists a
|
|
83
|
+
* session alongside a normal scan.
|
|
84
|
+
*/
|
|
85
|
+
export async function emitScanCompletion(input) {
|
|
86
|
+
const { cli, tool, opts, envelope, signalCount, deliver, completion } = input;
|
|
87
|
+
if (opts.gateSave === true) {
|
|
88
|
+
await cli.saveBaseline(tool, envelope);
|
|
89
|
+
await cli.render({ type: 'gate-done', lines: renderGateSaveLines(tool, signalCount) });
|
|
90
|
+
await cli.deliverSignals(envelope, deliver);
|
|
91
|
+
logCompleted({ cli, tool, signalCount, passed: envelope.verdict.passed, gate: 'save' });
|
|
92
|
+
return completion;
|
|
93
|
+
}
|
|
94
|
+
if (opts.gateCompare === true) {
|
|
95
|
+
const result = await cli.compareBaseline(tool, envelope);
|
|
96
|
+
await cli.render({ type: 'gate-done', lines: renderGateCompareLines(tool, result) });
|
|
97
|
+
await cli.deliverSignals(envelope, {
|
|
98
|
+
...deliver,
|
|
99
|
+
runFailed: result.degraded && resolveFailOnDegraded(tool),
|
|
100
|
+
});
|
|
101
|
+
logCompleted({
|
|
102
|
+
cli,
|
|
103
|
+
tool,
|
|
104
|
+
signalCount,
|
|
105
|
+
passed: envelope.verdict.passed,
|
|
106
|
+
gate: 'compare',
|
|
107
|
+
degraded: result.degraded,
|
|
108
|
+
});
|
|
109
|
+
return completion;
|
|
110
|
+
}
|
|
111
|
+
if (opts.json === true) {
|
|
112
|
+
cli.emitEnvelope(envelope);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
await cli.render({
|
|
116
|
+
type: 'text-lines',
|
|
117
|
+
title: `${tool} scan`,
|
|
118
|
+
lines: summaryLines(tool, signalCount, envelope.verdict.score, envelope.verdict.passed),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
await cli.deliverSignals(envelope, deliver);
|
|
122
|
+
logCompleted({ cli, tool, signalCount, passed: envelope.verdict.passed });
|
|
123
|
+
return completion;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=scan-emit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-emit.js","sourceRoot":"","sources":["../src/scan-emit.ts"],"names":[],"mappings":"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"}
|