@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,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The single, substrate-LOCAL SARIF ingest (ADR-0091).
|
|
3
|
+
*
|
|
4
|
+
* This is the ONLY SARIF read/parse path in the workspace (enforced by the
|
|
5
|
+
* `single-sarif-ingest` dependency-cruiser rule). The OpenSIP SARIF *writer*'s
|
|
6
|
+
* shape types are file-private to `@opensip-cli/output` (a layer-3 peer the
|
|
7
|
+
* substrate may not import) and deliberately minimal — so a foreign-scanner
|
|
8
|
+
* ingest defines its OWN defensive INPUT types here: it must read
|
|
9
|
+
* `result.fingerprints`/`partialFingerprints`/`guid`, `properties`, multiple
|
|
10
|
+
* `runs`/`locations`, `ruleIndex`→`rules` joins, and
|
|
11
|
+
* `properties["security-severity"]`.
|
|
12
|
+
*
|
|
13
|
+
* Severity recovery is the core job: the writer collapses BOTH `critical` and
|
|
14
|
+
* `high` → SARIF `error`, so a level-only inverse is ambiguous. `ingestSarif`
|
|
15
|
+
* reads the CVSS `security-severity` number (off the rule descriptor, falling
|
|
16
|
+
* back to the result) and applies the FIRST/NVD v3 bands BEFORE falling back to
|
|
17
|
+
* `level`. The native level/severity are preserved on `Signal.metadata`.
|
|
18
|
+
*
|
|
19
|
+
* Message stability is the second job: real scanners (Trivy) put a VERBOSE,
|
|
20
|
+
* version-volatile block in `result.message.text` (e.g. a `Package:/Installed
|
|
21
|
+
* Version:/Severity:/Fixed Version:/Link:` listing). Surfacing that as
|
|
22
|
+
* `Signal.message` would also feed it into the `message-hash` fingerprint, so the
|
|
23
|
+
* baseline would churn on every dependency bump — defeating the whole point of a
|
|
24
|
+
* line-shift-tolerant hash. So `ingestSarif` prefers the STABLE rule title
|
|
25
|
+
* (`driver.rules[ruleIndex].shortDescription.text`) for `Signal.message` when
|
|
26
|
+
* present and stashes the verbose `result.message.text` on `metadata.detail`. It
|
|
27
|
+
* falls back to `result.message.text` only when no rule/shortDescription exists.
|
|
28
|
+
*/
|
|
29
|
+
import { createSignal } from '@opensip-cli/core';
|
|
30
|
+
import { cvssToSeverity, parseCvss, sarifLevelToSeverity } from './severity-map.js';
|
|
31
|
+
const SECURITY_SEVERITY_KEY = 'security-severity';
|
|
32
|
+
/** Find the rule descriptor for a result by `ruleIndex` first, then by id. */
|
|
33
|
+
function resolveRule(result, rules) {
|
|
34
|
+
if (typeof result.ruleIndex === 'number' &&
|
|
35
|
+
result.ruleIndex >= 0 &&
|
|
36
|
+
result.ruleIndex < rules.length) {
|
|
37
|
+
return rules[result.ruleIndex];
|
|
38
|
+
}
|
|
39
|
+
if (typeof result.ruleId === 'string') {
|
|
40
|
+
return rules.find((rule) => rule.id === result.ruleId);
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
/** Read `properties["security-severity"]` off the rule, then the result. */
|
|
45
|
+
function readSecuritySeverity(result, rule) {
|
|
46
|
+
for (const props of [rule?.properties, result.properties]) {
|
|
47
|
+
const raw = props?.[SECURITY_SEVERITY_KEY];
|
|
48
|
+
const cvss = parseCvss(raw);
|
|
49
|
+
if (cvss !== undefined)
|
|
50
|
+
return { raw, cvss };
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Recover the four-bucket OpenSIP severity for one SARIF result: prefer the CVSS
|
|
56
|
+
* `security-severity` number (bands), else fall back to `level` (then the rule's
|
|
57
|
+
* `defaultConfiguration.level`). The native `securitySeverity`/`level` are
|
|
58
|
+
* returned so the caller can preserve them on metadata.
|
|
59
|
+
*/
|
|
60
|
+
function recoverSeverity(result, rule) {
|
|
61
|
+
const security = readSecuritySeverity(result, rule);
|
|
62
|
+
if (security !== undefined) {
|
|
63
|
+
return {
|
|
64
|
+
severity: cvssToSeverity(security.cvss),
|
|
65
|
+
securitySeverity: security.raw,
|
|
66
|
+
nativeLevel: result.level,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const level = result.level ?? rule?.defaultConfiguration?.level;
|
|
70
|
+
return { severity: sarifLevelToSeverity(level), nativeLevel: level };
|
|
71
|
+
}
|
|
72
|
+
/** Collapse a result's locations to the primary site + a count of extras. */
|
|
73
|
+
function primaryLocation(result) {
|
|
74
|
+
const locations = result.locations ?? [];
|
|
75
|
+
const first = locations[0]?.physicalLocation;
|
|
76
|
+
return {
|
|
77
|
+
file: first?.artifactLocation?.uri,
|
|
78
|
+
line: first?.region?.startLine,
|
|
79
|
+
column: first?.region?.startColumn,
|
|
80
|
+
extra: locations.length > 1 ? locations.length - 1 : 0,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolve `Signal.message` (the fingerprint basis) for one result, preferring the
|
|
85
|
+
* STABLE rule title over the version-volatile `result.message.text`. When a title
|
|
86
|
+
* is used and a distinct verbose result message exists, the verbose block is
|
|
87
|
+
* returned as `detail` to stash on metadata (so nothing is lost).
|
|
88
|
+
*/
|
|
89
|
+
function resolveMessage(result, rule, ruleId) {
|
|
90
|
+
const title = rule?.shortDescription?.text;
|
|
91
|
+
const verbose = result.message?.text;
|
|
92
|
+
if (title !== undefined && title.length > 0) {
|
|
93
|
+
return {
|
|
94
|
+
message: title,
|
|
95
|
+
...(verbose !== undefined && verbose !== title ? { detail: verbose } : {}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return { message: verbose ?? rule?.fullDescription?.text ?? ruleId };
|
|
99
|
+
}
|
|
100
|
+
/** Build the opaque metadata bag for one result (native severity/level/fingerprint/help/detail). */
|
|
101
|
+
function buildMetadata(result, rule, severity, extras) {
|
|
102
|
+
const nativeFingerprint = result.guid ?? result.fingerprints ?? result.partialFingerprints;
|
|
103
|
+
const helpUri = rule?.helpUri ?? result.helpUri;
|
|
104
|
+
return {
|
|
105
|
+
nativeLevel: severity.nativeLevel ?? null,
|
|
106
|
+
nativeSeverity: severity.securitySeverity ?? severity.nativeLevel ?? null,
|
|
107
|
+
...(severity.securitySeverity === undefined
|
|
108
|
+
? {}
|
|
109
|
+
: { securitySeverity: severity.securitySeverity }),
|
|
110
|
+
...(nativeFingerprint === undefined ? {} : { nativeFingerprint }),
|
|
111
|
+
...(extras.extraLocations > 0 ? { additionalLocations: extras.extraLocations } : {}),
|
|
112
|
+
...(extras.detail === undefined ? {} : { detail: extras.detail }),
|
|
113
|
+
...(helpUri === undefined ? {} : { helpUri }),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Normalize one SARIF result to a Signal, joined against its run's rules. */
|
|
117
|
+
function resultToSignal(result, rules, source, category) {
|
|
118
|
+
const rule = resolveRule(result, rules);
|
|
119
|
+
const ruleId = result.ruleId ?? rule?.id ?? 'unknown';
|
|
120
|
+
const severity = recoverSeverity(result, rule);
|
|
121
|
+
const { message, detail } = resolveMessage(result, rule, ruleId);
|
|
122
|
+
const location = primaryLocation(result);
|
|
123
|
+
return createSignal({
|
|
124
|
+
source,
|
|
125
|
+
severity: severity.severity,
|
|
126
|
+
category,
|
|
127
|
+
ruleId,
|
|
128
|
+
message,
|
|
129
|
+
code: {
|
|
130
|
+
...(location.file === undefined ? {} : { file: location.file }),
|
|
131
|
+
...(location.line === undefined ? {} : { line: location.line }),
|
|
132
|
+
...(location.column === undefined ? {} : { column: location.column }),
|
|
133
|
+
},
|
|
134
|
+
metadata: buildMetadata(result, rule, severity, { extraLocations: location.extra, detail }),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Ingest a SARIF 2.1.0 log into normalized {@link Signal}s. Defensive over
|
|
139
|
+
* foreign output: tolerates multiple runs, missing rules/locations, and
|
|
140
|
+
* `ruleIndex` joins. Severity is recovered from CVSS `security-severity` before
|
|
141
|
+
* `level` (ADR-0091); native fingerprints/level/severity are preserved on
|
|
142
|
+
* `metadata`. `Signal.fingerprint` is left unstamped — the host ratchet's
|
|
143
|
+
* `message-hash` strategy stamps it worker-side at envelope construction.
|
|
144
|
+
*/
|
|
145
|
+
export function ingestSarif(sarif, options) {
|
|
146
|
+
const category = options?.category ?? 'security';
|
|
147
|
+
const signals = [];
|
|
148
|
+
for (const run of sarif.runs ?? []) {
|
|
149
|
+
const driver = run.tool?.driver;
|
|
150
|
+
const rules = driver?.rules ?? [];
|
|
151
|
+
const source = options?.source ?? driver?.name?.toLowerCase() ?? 'sarif';
|
|
152
|
+
for (const result of run.results ?? []) {
|
|
153
|
+
signals.push(resultToSignal(result, rules, source, category));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return signals;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=ingest-sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-sarif.js","sourceRoot":"","sources":["../src/ingest-sarif.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Manifest command-shell derivation (ADR-0090, Phase-0 decision 7).
|
|
3
|
+
*
|
|
4
|
+
* An installed adapter mounts from its STATIC `package.json#opensipTools.commands`,
|
|
5
|
+
* not its runtime — and `assertCommandNamesMatch` THROWS if the static shells
|
|
6
|
+
* drift from the runtime `commandSpecs` (at install + worker import). So the
|
|
7
|
+
* shells must be GENERATED from the runtime, never hand-authored.
|
|
8
|
+
*
|
|
9
|
+
* `deriveAdapterManifestCommands(tool)` produces exactly that serializable data
|
|
10
|
+
* (the scan + doctor + version shells). A Phase-3 generator extension
|
|
11
|
+
* (`build-tool-command-manifests.mjs`) writes it into each adapter package.json
|
|
12
|
+
* and `--check`s parity; this helper is the single source the generator consumes.
|
|
13
|
+
*/
|
|
14
|
+
import type { ManifestCommandShell } from './types.js';
|
|
15
|
+
import type { Tool } from '@opensip-cli/core';
|
|
16
|
+
/**
|
|
17
|
+
* Derive the serializable `opensipTools.commands` shells from a built adapter
|
|
18
|
+
* {@link Tool}'s `commandSpecs` — name, description, aliases, common flags,
|
|
19
|
+
* options (e.g. the gate flags, minus `parse`), scope, output mode, the nesting
|
|
20
|
+
* `parent`, and (for `raw-stream`) the `rawStreamReason`. Pure.
|
|
21
|
+
*/
|
|
22
|
+
export declare function deriveAdapterManifestCommands(tool: Tool): readonly ManifestCommandShell[];
|
|
23
|
+
//# sourceMappingURL=manifest-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-commands.d.ts","sourceRoot":"","sources":["../src/manifest-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAwC,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAkBpF;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,oBAAoB,EAAE,CAYzF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Manifest command-shell derivation (ADR-0090, Phase-0 decision 7).
|
|
3
|
+
*
|
|
4
|
+
* An installed adapter mounts from its STATIC `package.json#opensipTools.commands`,
|
|
5
|
+
* not its runtime — and `assertCommandNamesMatch` THROWS if the static shells
|
|
6
|
+
* drift from the runtime `commandSpecs` (at install + worker import). So the
|
|
7
|
+
* shells must be GENERATED from the runtime, never hand-authored.
|
|
8
|
+
*
|
|
9
|
+
* `deriveAdapterManifestCommands(tool)` produces exactly that serializable data
|
|
10
|
+
* (the scan + doctor + version shells). A Phase-3 generator extension
|
|
11
|
+
* (`build-tool-command-manifests.mjs`) writes it into each adapter package.json
|
|
12
|
+
* and `--check`s parity; this helper is the single source the generator consumes.
|
|
13
|
+
*/
|
|
14
|
+
import { isAbsolute } from 'node:path';
|
|
15
|
+
/**
|
|
16
|
+
* `OptionSpec` → `ManifestOptionDescriptor`: every field except the
|
|
17
|
+
* non-serializable `parse` closure, and minus a machine-specific absolute-path
|
|
18
|
+
* string `default` (resolved lazily at runtime, so baking it would make the
|
|
19
|
+
* manifest non-deterministic). Byte-identical to the shared generator's
|
|
20
|
+
* `deriveOptionDescriptor` (`scripts/build-tool-command-manifests.mjs`), so the
|
|
21
|
+
* two derivation paths agree (the `tool.test.ts` parity guard pins this).
|
|
22
|
+
*/
|
|
23
|
+
function toManifestOption(option) {
|
|
24
|
+
const dropDefault = typeof option.default === 'string' && isAbsolute(option.default);
|
|
25
|
+
const entries = Object.entries(option).filter(([key]) => key !== 'parse' && !(dropDefault && key === 'default'));
|
|
26
|
+
return Object.fromEntries(entries);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Derive the serializable `opensipTools.commands` shells from a built adapter
|
|
30
|
+
* {@link Tool}'s `commandSpecs` — name, description, aliases, common flags,
|
|
31
|
+
* options (e.g. the gate flags, minus `parse`), scope, output mode, the nesting
|
|
32
|
+
* `parent`, and (for `raw-stream`) the `rawStreamReason`. Pure.
|
|
33
|
+
*/
|
|
34
|
+
export function deriveAdapterManifestCommands(tool) {
|
|
35
|
+
return (tool.commandSpecs ?? []).map((spec) => ({
|
|
36
|
+
name: spec.name,
|
|
37
|
+
description: spec.description,
|
|
38
|
+
aliases: [...(spec.aliases ?? [])],
|
|
39
|
+
commonFlags: [...spec.commonFlags],
|
|
40
|
+
...(spec.options === undefined ? {} : { options: spec.options.map(toManifestOption) }),
|
|
41
|
+
scope: spec.scope,
|
|
42
|
+
output: spec.output,
|
|
43
|
+
...(spec.parent === undefined ? {} : { parent: spec.parent }),
|
|
44
|
+
...(spec.rawStreamReason === undefined ? {} : { rawStreamReason: spec.rawStreamReason }),
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=manifest-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-commands.js","sourceRoot":"","sources":["../src/manifest-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAKvC;;;;;;;GAOG;AACH,SAAS,gBAAgB,CAAC,MAAkB;IAC1C,MAAM,WAAW,GAAG,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAC3C,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,WAAW,IAAI,GAAG,KAAK,SAAS,CAAC,CAClE,CAAC;IACF,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAA6B,CAAC;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAAU;IACtD,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAClC,WAAW,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;QAClC,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACtF,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7D,GAAG,CAAC,IAAI,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;KACzF,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The substrate's SINGLE subprocess/IO boundary (ADR-0090 §11).
|
|
3
|
+
*
|
|
4
|
+
* `execFile` / `execFileSync` ONLY — NEVER `exec` with a shell string. Args are
|
|
5
|
+
* descriptor-controlled arrays, so a scanner argument can never be interpreted as
|
|
6
|
+
* a shell metacharacter. This module wraps the three IO primitives the substrate
|
|
7
|
+
* needs — PATH lookup (`which`/`where`), the scanner process runner (timeout +
|
|
8
|
+
* output cap), and the version probe — plus the real {@link BinaryResolveDeps}.
|
|
9
|
+
*
|
|
10
|
+
* It is the one file excluded from unit coverage: it runs a real external binary
|
|
11
|
+
* and is exercised end-to-end by each adapter's worker E2E (ADR-0090 D6 Tier 2).
|
|
12
|
+
* Every decision it feeds (resolution, exit modeling, ingest) is pure and covered
|
|
13
|
+
* directly.
|
|
14
|
+
*/
|
|
15
|
+
import type { BinaryResolveDeps } from './binary-resolver.js';
|
|
16
|
+
/** The captured outcome of a scanner process run. */
|
|
17
|
+
export interface ProcessResult {
|
|
18
|
+
/** The exit code (`-1` when the process was killed by the timeout). */
|
|
19
|
+
readonly code: number;
|
|
20
|
+
readonly stdout: string;
|
|
21
|
+
readonly stderr: string;
|
|
22
|
+
readonly timedOut: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface RunProcessInput {
|
|
25
|
+
readonly command: string;
|
|
26
|
+
readonly args: readonly string[];
|
|
27
|
+
readonly cwd: string;
|
|
28
|
+
readonly timeoutMs: number;
|
|
29
|
+
/** Max captured stdout+stderr bytes before the run is aborted. */
|
|
30
|
+
readonly maxBuffer: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Run a scanner binary with `execFile` (no shell), capturing stdout/stderr and
|
|
34
|
+
* the exit code EVEN on a nonzero exit (a scanner signalling findings via exit
|
|
35
|
+
* code is not an error here — {@link interpretExit} classifies it). Rejects only
|
|
36
|
+
* on a spawn failure (e.g. `ENOENT`); a timeout resolves with `timedOut: true`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function runScannerProcess(input: RunProcessInput): Promise<ProcessResult>;
|
|
39
|
+
export interface ProbeVersionInput {
|
|
40
|
+
readonly path: string;
|
|
41
|
+
readonly versionArgs: readonly string[];
|
|
42
|
+
readonly parse?: (stdout: string) => string;
|
|
43
|
+
readonly timeoutMs: number;
|
|
44
|
+
}
|
|
45
|
+
/** Probe a binary's version (`execFileSync`, no shell). Returns `undefined` on any failure. */
|
|
46
|
+
export declare function probeBinaryVersion(input: ProbeVersionInput): string | undefined;
|
|
47
|
+
/** PATH lookup via `which` (POSIX) / `where` (Windows), no shell. `undefined` if absent. */
|
|
48
|
+
export declare function whichBinary(command: string, platform: NodeJS.Platform): string | undefined;
|
|
49
|
+
/** The real binary-resolution IO deps (existence check + PATH lookup). */
|
|
50
|
+
export declare const defaultBinaryDeps: BinaryResolveDeps;
|
|
51
|
+
//# sourceMappingURL=process-exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-exec.d.ts","sourceRoot":"","sources":["../src/process-exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CA8ChF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAe/E;AAED,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,GAAG,SAAS,CAc1F;AAED,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,EAAE,iBAG/B,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The substrate's SINGLE subprocess/IO boundary (ADR-0090 §11).
|
|
3
|
+
*
|
|
4
|
+
* `execFile` / `execFileSync` ONLY — NEVER `exec` with a shell string. Args are
|
|
5
|
+
* descriptor-controlled arrays, so a scanner argument can never be interpreted as
|
|
6
|
+
* a shell metacharacter. This module wraps the three IO primitives the substrate
|
|
7
|
+
* needs — PATH lookup (`which`/`where`), the scanner process runner (timeout +
|
|
8
|
+
* output cap), and the version probe — plus the real {@link BinaryResolveDeps}.
|
|
9
|
+
*
|
|
10
|
+
* It is the one file excluded from unit coverage: it runs a real external binary
|
|
11
|
+
* and is exercised end-to-end by each adapter's worker E2E (ADR-0090 D6 Tier 2).
|
|
12
|
+
* Every decision it feeds (resolution, exit modeling, ingest) is pure and covered
|
|
13
|
+
* directly.
|
|
14
|
+
*/
|
|
15
|
+
import { execFile, execFileSync } from 'node:child_process';
|
|
16
|
+
import { existsSync } from 'node:fs';
|
|
17
|
+
/**
|
|
18
|
+
* Run a scanner binary with `execFile` (no shell), capturing stdout/stderr and
|
|
19
|
+
* the exit code EVEN on a nonzero exit (a scanner signalling findings via exit
|
|
20
|
+
* code is not an error here — {@link interpretExit} classifies it). Rejects only
|
|
21
|
+
* on a spawn failure (e.g. `ENOENT`); a timeout resolves with `timedOut: true`.
|
|
22
|
+
*/
|
|
23
|
+
export function runScannerProcess(input) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
execFile(input.command, [...input.args], {
|
|
26
|
+
cwd: input.cwd,
|
|
27
|
+
timeout: input.timeoutMs,
|
|
28
|
+
maxBuffer: input.maxBuffer,
|
|
29
|
+
encoding: 'utf8',
|
|
30
|
+
windowsHide: true,
|
|
31
|
+
}, (error, stdout, stderr) => {
|
|
32
|
+
const out = typeof stdout === 'string' ? stdout : String(stdout ?? '');
|
|
33
|
+
const err = typeof stderr === 'string' ? stderr : String(stderr ?? '');
|
|
34
|
+
if (error === null) {
|
|
35
|
+
resolve({ code: 0, stdout: out, stderr: err, timedOut: false });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const failure = error;
|
|
39
|
+
if (failure.killed === true || failure.signal === 'SIGTERM') {
|
|
40
|
+
resolve({ code: -1, stdout: out, stderr: err, timedOut: true });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// A string `code` (`ENOENT`/`EACCES`) is a spawn failure, not an exit code.
|
|
44
|
+
if (typeof failure.code === 'string') {
|
|
45
|
+
reject(error instanceof Error
|
|
46
|
+
? error
|
|
47
|
+
: new Error(`scanner '${input.command}' failed to spawn (${failure.code})`));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
resolve({
|
|
51
|
+
code: typeof failure.code === 'number' ? failure.code : 1,
|
|
52
|
+
stdout: out,
|
|
53
|
+
stderr: err,
|
|
54
|
+
timedOut: false,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Probe a binary's version (`execFileSync`, no shell). Returns `undefined` on any failure. */
|
|
60
|
+
export function probeBinaryVersion(input) {
|
|
61
|
+
try {
|
|
62
|
+
const stdout = execFileSync(input.path, [...input.versionArgs], {
|
|
63
|
+
encoding: 'utf8',
|
|
64
|
+
timeout: input.timeoutMs,
|
|
65
|
+
windowsHide: true,
|
|
66
|
+
});
|
|
67
|
+
const text = typeof stdout === 'string' ? stdout : String(stdout);
|
|
68
|
+
return (input.parse ?? ((s) => s.trim()))(text);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// intentionally silent: a version probe failure (binary missing, slow, or
|
|
72
|
+
// a non-version output) is a normal "version unknown" — surfaced as
|
|
73
|
+
// undefined, which doctor renders. No logger is available in this pure seam.
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** PATH lookup via `which` (POSIX) / `where` (Windows), no shell. `undefined` if absent. */
|
|
78
|
+
export function whichBinary(command, platform) {
|
|
79
|
+
const finder = platform === 'win32' ? 'where' : 'which';
|
|
80
|
+
try {
|
|
81
|
+
const out = execFileSync(finder, [command], { encoding: 'utf8', windowsHide: true });
|
|
82
|
+
return String(out)
|
|
83
|
+
.split(/\r?\n/)
|
|
84
|
+
.map((line) => line.trim())
|
|
85
|
+
.find((line) => line.length > 0);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// intentionally silent: a `which`/`where` miss means "not on PATH" — a normal
|
|
89
|
+
// resolution outcome surfaced as undefined (the resolver then reports a
|
|
90
|
+
// not-found with an install hint). No logger in this pure IO seam.
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** The real binary-resolution IO deps (existence check + PATH lookup). */
|
|
95
|
+
export const defaultBinaryDeps = {
|
|
96
|
+
existsSync: (path) => existsSync(path),
|
|
97
|
+
which: (command, platform) => whichBinary(command, platform),
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=process-exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-exec.js","sourceRoot":"","sources":["../src/process-exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Provenance stamping (ADR-0090 §8, ADR-0092 review bar item 4).
|
|
3
|
+
*
|
|
4
|
+
* Every normalized signal carries, on `metadata.provenance`, the facts a
|
|
5
|
+
* reviewer/operator needs to trust the finding: the tool, the adapter package,
|
|
6
|
+
* the resolved binary version + path, the exact args, and the config file path.
|
|
7
|
+
* Pure — returns a new `Signal`; the matched secret is NEVER part of provenance.
|
|
8
|
+
*/
|
|
9
|
+
import type { AdapterProvenance } from './types.js';
|
|
10
|
+
import type { Signal } from '@opensip-cli/core';
|
|
11
|
+
/**
|
|
12
|
+
* Return a copy of `signal` with {@link AdapterProvenance} merged under
|
|
13
|
+
* `metadata.provenance`. Undefined provenance fields are dropped so the bag stays
|
|
14
|
+
* compact and deterministic.
|
|
15
|
+
*/
|
|
16
|
+
export declare function stampProvenance(signal: Signal, provenance: AdapterProvenance): Signal;
|
|
17
|
+
/** Stamp provenance across a batch of signals. */
|
|
18
|
+
export declare function stampProvenanceAll(signals: readonly Signal[], provenance: AdapterProvenance): readonly Signal[];
|
|
19
|
+
//# sourceMappingURL=provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,GAAG,MAAM,CAYrF;AAED,kDAAkD;AAClD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,UAAU,EAAE,iBAAiB,GAC5B,SAAS,MAAM,EAAE,CAEnB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Provenance stamping (ADR-0090 §8, ADR-0092 review bar item 4).
|
|
3
|
+
*
|
|
4
|
+
* Every normalized signal carries, on `metadata.provenance`, the facts a
|
|
5
|
+
* reviewer/operator needs to trust the finding: the tool, the adapter package,
|
|
6
|
+
* the resolved binary version + path, the exact args, and the config file path.
|
|
7
|
+
* Pure — returns a new `Signal`; the matched secret is NEVER part of provenance.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Return a copy of `signal` with {@link AdapterProvenance} merged under
|
|
11
|
+
* `metadata.provenance`. Undefined provenance fields are dropped so the bag stays
|
|
12
|
+
* compact and deterministic.
|
|
13
|
+
*/
|
|
14
|
+
export function stampProvenance(signal, provenance) {
|
|
15
|
+
const bag = {
|
|
16
|
+
tool: provenance.tool,
|
|
17
|
+
binaryPath: provenance.binaryPath,
|
|
18
|
+
args: [...provenance.args],
|
|
19
|
+
...(provenance.adapterPackage === undefined
|
|
20
|
+
? {}
|
|
21
|
+
: { adapterPackage: provenance.adapterPackage }),
|
|
22
|
+
...(provenance.binaryVersion === undefined ? {} : { binaryVersion: provenance.binaryVersion }),
|
|
23
|
+
...(provenance.configPath === undefined ? {} : { configPath: provenance.configPath }),
|
|
24
|
+
};
|
|
25
|
+
return { ...signal, metadata: { ...signal.metadata, provenance: bag } };
|
|
26
|
+
}
|
|
27
|
+
/** Stamp provenance across a batch of signals. */
|
|
28
|
+
export function stampProvenanceAll(signals, provenance) {
|
|
29
|
+
return signals.map((signal) => stampProvenance(signal, provenance));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=provenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.js","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,UAA6B;IAC3E,MAAM,GAAG,GAA4B;QACnC,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC;QAC1B,GAAG,CAAC,UAAU,CAAC,cAAc,KAAK,SAAS;YACzC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,cAAc,EAAE,UAAU,CAAC,cAAc,EAAE,CAAC;QAClD,GAAG,CAAC,UAAU,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,CAAC,aAAa,EAAE,CAAC;QAC9F,GAAG,CAAC,UAAU,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,CAAC;KACtF,CAAC;IACF,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,CAAC;AAC1E,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,kBAAkB,CAChC,OAA0B,EAC1B,UAA6B;IAE7B,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AACtE,CAAC"}
|
package/dist/redact.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Secret redaction for secret-scanner parsers (ADR-0091 / ADR-0092).
|
|
3
|
+
*
|
|
4
|
+
* A secret scanner (gitleaks) captures the live credential (`Secret`) and the
|
|
5
|
+
* surrounding match region (`Match`). Neither may EVER reach `Signal.message`,
|
|
6
|
+
* `Signal.metadata` in raw form, or any egress payload. A parser stores only a
|
|
7
|
+
* {@link redactSecret} PREVIEW (or {@link secretHash}) so a finding stays
|
|
8
|
+
* identifiable without leaking the value.
|
|
9
|
+
*
|
|
10
|
+
* Pure functions; `redactSecret` provably never returns the raw string.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Mask a secret to a short, non-reversible PREVIEW: the first 4 characters plus
|
|
14
|
+
* an ellipsis (`'AKIA…'`). A value of length `<= 4` collapses to just `'…'`, so
|
|
15
|
+
* the full raw secret is NEVER returned. Empty/undefined → `''`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function redactSecret(raw: string | undefined | null): string;
|
|
18
|
+
/**
|
|
19
|
+
* A stable, non-reversible identity for a secret: the first 12 hex chars of its
|
|
20
|
+
* SHA-256. Useful when two findings must be told apart without storing any part
|
|
21
|
+
* of the value. Empty/undefined → `''`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function secretHash(raw: string | undefined | null): string;
|
|
24
|
+
//# sourceMappingURL=redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAMnE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAGjE"}
|
package/dist/redact.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Secret redaction for secret-scanner parsers (ADR-0091 / ADR-0092).
|
|
3
|
+
*
|
|
4
|
+
* A secret scanner (gitleaks) captures the live credential (`Secret`) and the
|
|
5
|
+
* surrounding match region (`Match`). Neither may EVER reach `Signal.message`,
|
|
6
|
+
* `Signal.metadata` in raw form, or any egress payload. A parser stores only a
|
|
7
|
+
* {@link redactSecret} PREVIEW (or {@link secretHash}) so a finding stays
|
|
8
|
+
* identifiable without leaking the value.
|
|
9
|
+
*
|
|
10
|
+
* Pure functions; `redactSecret` provably never returns the raw string.
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
/**
|
|
14
|
+
* Mask a secret to a short, non-reversible PREVIEW: the first 4 characters plus
|
|
15
|
+
* an ellipsis (`'AKIA…'`). A value of length `<= 4` collapses to just `'…'`, so
|
|
16
|
+
* the full raw secret is NEVER returned. Empty/undefined → `''`.
|
|
17
|
+
*/
|
|
18
|
+
export function redactSecret(raw) {
|
|
19
|
+
if (raw === undefined || raw === null)
|
|
20
|
+
return '';
|
|
21
|
+
const s = String(raw);
|
|
22
|
+
if (s.length === 0)
|
|
23
|
+
return '';
|
|
24
|
+
if (s.length <= 4)
|
|
25
|
+
return '…';
|
|
26
|
+
return `${s.slice(0, 4)}…`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A stable, non-reversible identity for a secret: the first 12 hex chars of its
|
|
30
|
+
* SHA-256. Useful when two findings must be told apart without storing any part
|
|
31
|
+
* of the value. Empty/undefined → `''`.
|
|
32
|
+
*/
|
|
33
|
+
export function secretHash(raw) {
|
|
34
|
+
if (raw === undefined || raw === null || raw.length === 0)
|
|
35
|
+
return '';
|
|
36
|
+
return createHash('sha256').update(String(raw)).digest('hex').slice(0, 12);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAA8B;IACzD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACjD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC9B,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,GAA8B;IACvD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Build the {@link AdapterRunContext} from a `ToolCliContext`
|
|
3
|
+
* (ADR-0090, Phase-0 decision 8).
|
|
4
|
+
*
|
|
5
|
+
* Layer-legal: paths come from core `resolveProjectPaths`, NOT a `cli` import.
|
|
6
|
+
* `scan` REQUIRES a project — a config-less/project-agnostic run has no targeting
|
|
7
|
+
* root to scan, so a missing `projectContext` is a `ConfigurationError` (exit 2),
|
|
8
|
+
* mirroring MCP's datastore-unavailable handling (Phase-0 decision 2).
|
|
9
|
+
*/
|
|
10
|
+
import type { AdapterRunContext, ResolvedBinary } from './types.js';
|
|
11
|
+
import type { ToolCliContext } from '@opensip-cli/core';
|
|
12
|
+
export interface BuildRunContextInput {
|
|
13
|
+
readonly cli: ToolCliContext;
|
|
14
|
+
readonly tool: string;
|
|
15
|
+
readonly adapterPackage?: string;
|
|
16
|
+
readonly binary: ResolvedBinary;
|
|
17
|
+
readonly config: Readonly<Record<string, unknown>>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Construct the per-run {@link AdapterRunContext}. Throws `ConfigurationError`
|
|
21
|
+
* when no project scope is present (scan needs a targeting root).
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildAdapterRunContext(input: BuildRunContextInput): AdapterRunContext;
|
|
24
|
+
//# sourceMappingURL=run-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-context.d.ts","sourceRoot":"","sources":["../src/run-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,GAAG,iBAAiB,CA2BrF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Build the {@link AdapterRunContext} from a `ToolCliContext`
|
|
3
|
+
* (ADR-0090, Phase-0 decision 8).
|
|
4
|
+
*
|
|
5
|
+
* Layer-legal: paths come from core `resolveProjectPaths`, NOT a `cli` import.
|
|
6
|
+
* `scan` REQUIRES a project — a config-less/project-agnostic run has no targeting
|
|
7
|
+
* root to scan, so a missing `projectContext` is a `ConfigurationError` (exit 2),
|
|
8
|
+
* mirroring MCP's datastore-unavailable handling (Phase-0 decision 2).
|
|
9
|
+
*/
|
|
10
|
+
import { ConfigurationError, resolveProjectPaths } from '@opensip-cli/core';
|
|
11
|
+
import { resolveScannerArtifactPath } from './artifact-path.js';
|
|
12
|
+
/**
|
|
13
|
+
* Construct the per-run {@link AdapterRunContext}. Throws `ConfigurationError`
|
|
14
|
+
* when no project scope is present (scan needs a targeting root).
|
|
15
|
+
*/
|
|
16
|
+
export function buildAdapterRunContext(input) {
|
|
17
|
+
const project = input.cli.scope.projectContext;
|
|
18
|
+
if (project === undefined) {
|
|
19
|
+
throw new ConfigurationError(`Cannot run '${input.tool}': no opensip-cli project found here. ` +
|
|
20
|
+
`Run 'opensip ${input.tool}' from inside an opensip-cli project (a directory with opensip-cli.config.yml).`, { code: 'ADAPTER.SCAN.NO_PROJECT' });
|
|
21
|
+
}
|
|
22
|
+
const projectPaths = resolveProjectPaths(project.projectRoot);
|
|
23
|
+
const runId = input.cli.scope.runId;
|
|
24
|
+
return {
|
|
25
|
+
tool: input.tool,
|
|
26
|
+
adapterPackage: input.adapterPackage,
|
|
27
|
+
projectRoot: project.projectRoot,
|
|
28
|
+
runId,
|
|
29
|
+
logger: input.cli.logger,
|
|
30
|
+
config: input.config,
|
|
31
|
+
binary: input.binary,
|
|
32
|
+
configPath: project.configPath,
|
|
33
|
+
artifactPath: (name) => resolveScannerArtifactPath({ artifactDir: projectPaths.artifactDir, runId }, input.tool, name),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=run-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-context.js","sourceRoot":"","sources":["../src/run-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE5E,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAahE;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAA2B;IAChE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC;IAC/C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,kBAAkB,CAC1B,eAAe,KAAK,CAAC,IAAI,wCAAwC;YAC/D,gBAAgB,KAAK,CAAC,IAAI,iFAAiF,EAC7G,EAAE,IAAI,EAAE,yBAAyB,EAAE,CACpC,CAAC;IACJ,CAAC;IACD,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;IACpC,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,KAAK;QACL,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CACrB,0BAA0B,CACxB,EAAE,WAAW,EAAE,YAAY,CAAC,WAAW,EAAE,KAAK,EAAE,EAChD,KAAK,CAAC,IAAI,EACV,IAAI,CACL;KACJ,CAAC;AACJ,CAAC"}
|