@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,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-payload — the adapter-owned, dashboard-shaped session detail blob
|
|
3
|
+
* (A2: an adapter session must carry `summary` + grouped `checks[]` so the HTML
|
|
4
|
+
* report renders a secret/vuln scan's findings instead of falsely "clean").
|
|
5
|
+
*
|
|
6
|
+
* Proves the grouped shape AND the secret-hygiene invariant: every field is
|
|
7
|
+
* copied from an already-redacted signal, and `metadata` is narrowed to JSON
|
|
8
|
+
* scalars (the nested provenance object is dropped) — so NO raw credential reaches
|
|
9
|
+
* the persisted payload.
|
|
10
|
+
*/
|
|
11
|
+
import { createSignal } from '@opensip-cli/core';
|
|
12
|
+
import { describe, expect, it } from 'vitest';
|
|
13
|
+
import { buildAdapterSessionPayload } from '../session-payload.js';
|
|
14
|
+
/** A `high`-severity (error-rung) secret signal carrying ONLY a redacted preview. */
|
|
15
|
+
function leakSignal(overrides = {}) {
|
|
16
|
+
return createSignal({
|
|
17
|
+
source: 'gitleaks',
|
|
18
|
+
category: 'security',
|
|
19
|
+
severity: 'high',
|
|
20
|
+
ruleId: 'aws-access-token',
|
|
21
|
+
message: 'AWS Access Key',
|
|
22
|
+
code: { file: 'config/prod.env', line: 12, column: 19 },
|
|
23
|
+
// The provenance object + a redacted scalar preview — the raw secret is NEVER
|
|
24
|
+
// present (the ingest parser masked it). The provenance object must be dropped.
|
|
25
|
+
metadata: {
|
|
26
|
+
secretPreview: 'AKIA…',
|
|
27
|
+
entropy: 3.65,
|
|
28
|
+
provenance: { tool: 'gitleaks', adapterPackage: '@opensip-cli/tool-gitleaks' },
|
|
29
|
+
},
|
|
30
|
+
...overrides,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
describe('buildAdapterSessionPayload', () => {
|
|
34
|
+
it('groups signals by ruleId into checks[] with a populated summary (NOT clean)', () => {
|
|
35
|
+
const payload = buildAdapterSessionPayload([
|
|
36
|
+
leakSignal(),
|
|
37
|
+
leakSignal({ ruleId: 'aws-access-token', code: { file: 'a.env', line: 3 } }),
|
|
38
|
+
leakSignal({
|
|
39
|
+
ruleId: 'stripe-token',
|
|
40
|
+
message: 'Stripe Token',
|
|
41
|
+
code: { file: 'b.ts', line: 9 },
|
|
42
|
+
}),
|
|
43
|
+
]);
|
|
44
|
+
expect(payload.__version).toBe(1);
|
|
45
|
+
// Two distinct rules → two checks; the aws rule grouped both occurrences.
|
|
46
|
+
expect(payload.checks).toHaveLength(2);
|
|
47
|
+
const aws = payload.checks.find((c) => c.checkSlug === 'aws-access-token');
|
|
48
|
+
expect(aws?.violationCount).toBe(2);
|
|
49
|
+
expect(aws?.findings).toHaveLength(2);
|
|
50
|
+
expect(aws?.passed).toBe(false); // error-rung findings ⇒ the rule failed
|
|
51
|
+
// The summary the dashboard reads for its clean/dirty decision — NOT clean.
|
|
52
|
+
expect(payload.summary).toEqual({
|
|
53
|
+
total: 2, // rules that fired
|
|
54
|
+
passed: 0,
|
|
55
|
+
failed: 2,
|
|
56
|
+
errors: 3, // high-severity signal count
|
|
57
|
+
warnings: 0,
|
|
58
|
+
});
|
|
59
|
+
// The dashboard's `clean = errors === 0 && warnings === 0` is therefore FALSE.
|
|
60
|
+
expect(payload.summary.errors === 0 && payload.summary.warnings === 0).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
it('collapses 4-level severity to the dashboard 2-level bucket and marks a warning rule passed', () => {
|
|
63
|
+
const payload = buildAdapterSessionPayload([
|
|
64
|
+
leakSignal({ ruleId: 'low-rule', severity: 'low', message: 'minor' }),
|
|
65
|
+
]);
|
|
66
|
+
const check = payload.checks[0];
|
|
67
|
+
expect(check?.findings[0]?.severity).toBe('warning');
|
|
68
|
+
// A rule with only warning-rung findings does NOT fail (fit/graph semantics).
|
|
69
|
+
expect(check?.passed).toBe(true);
|
|
70
|
+
expect(payload.summary).toMatchObject({ errors: 0, warnings: 1, passed: 1, failed: 0 });
|
|
71
|
+
});
|
|
72
|
+
it('an empty scan yields zero checks and a zeroed summary', () => {
|
|
73
|
+
const payload = buildAdapterSessionPayload([]);
|
|
74
|
+
expect(payload.checks).toHaveLength(0);
|
|
75
|
+
expect(payload.summary).toEqual({ total: 0, passed: 0, failed: 0, errors: 0, warnings: 0 });
|
|
76
|
+
});
|
|
77
|
+
it('carries per-finding file/line/column/suggestion and projects metadata to scalars', () => {
|
|
78
|
+
const payload = buildAdapterSessionPayload([
|
|
79
|
+
leakSignal({ suggestion: 'rotate the credential' }),
|
|
80
|
+
]);
|
|
81
|
+
const finding = payload.checks[0]?.findings[0];
|
|
82
|
+
expect(finding?.filePath).toBe('config/prod.env');
|
|
83
|
+
expect(finding?.line).toBe(12);
|
|
84
|
+
expect(finding?.column).toBe(19);
|
|
85
|
+
expect(finding?.suggestion).toBe('rotate the credential');
|
|
86
|
+
// Scalar metadata survives; the nested provenance OBJECT is dropped.
|
|
87
|
+
expect(finding?.metadata).toEqual({ secretPreview: 'AKIA…', entropy: 3.65 });
|
|
88
|
+
expect((finding?.metadata).provenance).toBeUndefined();
|
|
89
|
+
});
|
|
90
|
+
it('omits absent optional fields and keeps structured repair guidance', () => {
|
|
91
|
+
// A "bare" signal (no code ⇒ no file/line/column, no scalar metadata, no
|
|
92
|
+
// suggestion) and a repair-carrying signal — exercises both arms of every
|
|
93
|
+
// optional spread and the empty-metadata path.
|
|
94
|
+
const bare = createSignal({
|
|
95
|
+
source: 'osv-scanner',
|
|
96
|
+
severity: 'high',
|
|
97
|
+
ruleId: 'r',
|
|
98
|
+
message: 'm',
|
|
99
|
+
});
|
|
100
|
+
const withRepair = createSignal({
|
|
101
|
+
source: 'osv-scanner',
|
|
102
|
+
severity: 'high',
|
|
103
|
+
ruleId: 'r2',
|
|
104
|
+
message: 'm2',
|
|
105
|
+
repair: { repairKind: 'manual', autofixable: false },
|
|
106
|
+
});
|
|
107
|
+
const payload = buildAdapterSessionPayload([bare, withRepair]);
|
|
108
|
+
const bareFinding = payload.checks.find((c) => c.checkSlug === 'r')?.findings[0];
|
|
109
|
+
expect(bareFinding?.filePath).toBe('');
|
|
110
|
+
expect(bareFinding?.line).toBeUndefined();
|
|
111
|
+
expect(bareFinding?.column).toBeUndefined();
|
|
112
|
+
expect(bareFinding?.suggestion).toBeUndefined();
|
|
113
|
+
expect(bareFinding?.metadata).toBeUndefined();
|
|
114
|
+
const repairFinding = payload.checks.find((c) => c.checkSlug === 'r2')?.findings[0];
|
|
115
|
+
expect(repairFinding?.repair).toEqual({ repairKind: 'manual', autofixable: false });
|
|
116
|
+
});
|
|
117
|
+
it('NEVER lets a raw secret or the Match key reach the serialized payload (redaction proof)', () => {
|
|
118
|
+
// The signal carries ONLY a masked preview (redaction happens at ingest, before
|
|
119
|
+
// the envelope/payload build). Prove the serialized blob the host persists holds
|
|
120
|
+
// no raw credential bytes and no `Match` field.
|
|
121
|
+
const RAW_SECRET = 'AKIAIOSFODNN7EXAMPLE';
|
|
122
|
+
const payload = buildAdapterSessionPayload([leakSignal()]);
|
|
123
|
+
const blob = JSON.stringify(payload);
|
|
124
|
+
expect(blob).not.toContain(RAW_SECRET);
|
|
125
|
+
expect(blob).not.toContain('"Match"');
|
|
126
|
+
expect(blob).not.toContain('"Secret"');
|
|
127
|
+
// The masked preview IS present (the finding stays identifiable).
|
|
128
|
+
expect(blob).toContain('AKIA…');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
//# sourceMappingURL=session-payload.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-payload.test.js","sourceRoot":"","sources":["../../src/__tests__/session-payload.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"severity-map.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/severity-map.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { cvssToSeverity, parseCvss, sarifLevelToSeverity, withNativeSeverity, } from '../severity-map.js';
|
|
3
|
+
describe('cvssToSeverity (FIRST/NVD v3 bands)', () => {
|
|
4
|
+
it('maps the band boundaries', () => {
|
|
5
|
+
expect(cvssToSeverity(9)).toBe('critical');
|
|
6
|
+
expect(cvssToSeverity(9.8)).toBe('critical');
|
|
7
|
+
expect(cvssToSeverity(10)).toBe('critical');
|
|
8
|
+
expect(cvssToSeverity(8.9)).toBe('high');
|
|
9
|
+
expect(cvssToSeverity(7)).toBe('high');
|
|
10
|
+
expect(cvssToSeverity(6.9)).toBe('medium');
|
|
11
|
+
expect(cvssToSeverity(4)).toBe('medium');
|
|
12
|
+
expect(cvssToSeverity(3.9)).toBe('low');
|
|
13
|
+
expect(cvssToSeverity(0.1)).toBe('low');
|
|
14
|
+
});
|
|
15
|
+
it('treats 0 / negative / non-finite as low', () => {
|
|
16
|
+
expect(cvssToSeverity(0)).toBe('low');
|
|
17
|
+
expect(cvssToSeverity(-1)).toBe('low');
|
|
18
|
+
expect(cvssToSeverity(Number.NaN)).toBe('low');
|
|
19
|
+
// Non-finite is treated defensively as low (not a real CVSS score).
|
|
20
|
+
expect(cvssToSeverity(Number.POSITIVE_INFINITY)).toBe('low');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('parseCvss', () => {
|
|
24
|
+
it('reads numbers and numeric strings', () => {
|
|
25
|
+
expect(parseCvss(7.5)).toBe(7.5);
|
|
26
|
+
expect(parseCvss('9.8')).toBe(9.8);
|
|
27
|
+
expect(parseCvss(' 4.0 ')).toBe(4);
|
|
28
|
+
});
|
|
29
|
+
it('rejects vectors, empty, and non-numeric', () => {
|
|
30
|
+
expect(parseCvss('CVSS:3.1/AV:N/AC:L')).toBeUndefined();
|
|
31
|
+
expect(parseCvss('')).toBeUndefined();
|
|
32
|
+
expect(parseCvss(' ')).toBeUndefined();
|
|
33
|
+
expect(parseCvss('not-a-number')).toBeUndefined();
|
|
34
|
+
expect(parseCvss(undefined)).toBeUndefined();
|
|
35
|
+
expect(parseCvss(Number.NaN)).toBeUndefined();
|
|
36
|
+
expect(parseCvss({})).toBeUndefined();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('sarifLevelToSeverity (lossy fallback)', () => {
|
|
40
|
+
it('maps error to high (never critical), warning to medium, note/none to low', () => {
|
|
41
|
+
expect(sarifLevelToSeverity('error')).toBe('high');
|
|
42
|
+
expect(sarifLevelToSeverity('warning')).toBe('medium');
|
|
43
|
+
expect(sarifLevelToSeverity('note')).toBe('low');
|
|
44
|
+
expect(sarifLevelToSeverity('none')).toBe('low');
|
|
45
|
+
});
|
|
46
|
+
it('defaults an absent/unknown level to the warning rung (medium)', () => {
|
|
47
|
+
expect(sarifLevelToSeverity(undefined)).toBe('medium');
|
|
48
|
+
expect(sarifLevelToSeverity('bogus')).toBe('medium');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('withNativeSeverity', () => {
|
|
52
|
+
it('records the raw label and null for missing', () => {
|
|
53
|
+
expect(withNativeSeverity({ a: 1 }, 'HIGH')).toEqual({ a: 1, nativeSeverity: 'HIGH' });
|
|
54
|
+
expect(withNativeSeverity({}, undefined)).toEqual({ nativeSeverity: null });
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=severity-map.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"severity-map.test.js","sourceRoot":"","sources":["../../src/__tests__/severity-map.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A reusable, framework-agnostic acceptance harness (ADR-0090
|
|
3
|
+
* §4.12). Given a frozen golden (the scanner's native output) it runs the
|
|
4
|
+
* substrate's parse → normalize → envelope path and returns the produced signals
|
|
5
|
+
* + the built `SignalEnvelope`, so an adapter's test asserts on *data* instead of
|
|
6
|
+
* re-scaffolding the pipeline. The real worker E2E lives in each adapter's tests
|
|
7
|
+
* (D6 Tier 2); this covers the in-process normalization tier (D6 Tier 1).
|
|
8
|
+
*
|
|
9
|
+
* Pure: no test framework, no IO, deterministic `runId`/`createdAt`.
|
|
10
|
+
*/
|
|
11
|
+
import type { AdapterRunContext, FingerprintStrategyChoice, ParsedScannerOutput, ScannerOutputKind } from './types.js';
|
|
12
|
+
import type { SignalEnvelope } from '@opensip-cli/contracts';
|
|
13
|
+
import type { Signal } from '@opensip-cli/core';
|
|
14
|
+
/** A golden case to drive through the normalization pipeline. */
|
|
15
|
+
export interface AcceptanceFixture {
|
|
16
|
+
readonly tool: string;
|
|
17
|
+
readonly kind: ScannerOutputKind;
|
|
18
|
+
/** The scanner's native output bytes (a frozen golden). */
|
|
19
|
+
readonly raw: string;
|
|
20
|
+
/** Required for JSON/stdout fixtures; SARIF fixtures use the shared `ingestSarif`. */
|
|
21
|
+
readonly parse?: (raw: ParsedScannerOutput, ctx: AdapterRunContext) => readonly Signal[];
|
|
22
|
+
readonly fingerprintStrategy?: FingerprintStrategyChoice;
|
|
23
|
+
/** Optional partial context for a JSON/stdout `parse` (defaults are filled in). */
|
|
24
|
+
readonly ctx?: Partial<AdapterRunContext>;
|
|
25
|
+
}
|
|
26
|
+
/** The normalized output of an acceptance case. */
|
|
27
|
+
export interface AcceptanceResult {
|
|
28
|
+
readonly signals: readonly Signal[];
|
|
29
|
+
readonly envelope: SignalEnvelope;
|
|
30
|
+
}
|
|
31
|
+
/** The stable, comparable shape of a signal for golden assertions. */
|
|
32
|
+
export interface SignalShape {
|
|
33
|
+
readonly ruleId: string;
|
|
34
|
+
readonly severity: string;
|
|
35
|
+
readonly message: string;
|
|
36
|
+
readonly file: string;
|
|
37
|
+
readonly line?: number;
|
|
38
|
+
readonly column?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Project a signal to its stable comparison shape (drops ids/timestamps/metadata). */
|
|
41
|
+
export declare function normalizedSignalShape(signal: Signal): SignalShape;
|
|
42
|
+
/**
|
|
43
|
+
* Run a golden fixture through the substrate pipeline. Returns the normalized
|
|
44
|
+
* signals and a deterministic envelope (host fallback policy, message-hash
|
|
45
|
+
* fingerprints by default).
|
|
46
|
+
*/
|
|
47
|
+
export declare function runAcceptanceCase(fixture: AcceptanceFixture): AcceptanceResult;
|
|
48
|
+
//# sourceMappingURL=acceptance-harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acceptance-harness.d.ts","sourceRoot":"","sources":["../src/acceptance-harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,OAAO,KAAK,EACV,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,EAAE,GAAG,EAAE,iBAAiB,KAAK,SAAS,MAAM,EAAE,CAAC;IACzF,QAAQ,CAAC,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;IACzD,mFAAmF;IACnF,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC3C;AAED,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;CACnC;AAED,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CASjE;AAuBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CA8B9E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A reusable, framework-agnostic acceptance harness (ADR-0090
|
|
3
|
+
* §4.12). Given a frozen golden (the scanner's native output) it runs the
|
|
4
|
+
* substrate's parse → normalize → envelope path and returns the produced signals
|
|
5
|
+
* + the built `SignalEnvelope`, so an adapter's test asserts on *data* instead of
|
|
6
|
+
* re-scaffolding the pipeline. The real worker E2E lives in each adapter's tests
|
|
7
|
+
* (D6 Tier 2); this covers the in-process normalization tier (D6 Tier 1).
|
|
8
|
+
*
|
|
9
|
+
* Pure: no test framework, no IO, deterministic `runId`/`createdAt`.
|
|
10
|
+
*/
|
|
11
|
+
import { buildSignalEnvelope } from '@opensip-cli/contracts';
|
|
12
|
+
import { resolveFingerprintStrategy } from './fingerprint.js';
|
|
13
|
+
import { safeParseJson } from './ingest-json.js';
|
|
14
|
+
import { ingestSarif } from './ingest-sarif.js';
|
|
15
|
+
/** Project a signal to its stable comparison shape (drops ids/timestamps/metadata). */
|
|
16
|
+
export function normalizedSignalShape(signal) {
|
|
17
|
+
return {
|
|
18
|
+
ruleId: signal.ruleId,
|
|
19
|
+
severity: signal.severity,
|
|
20
|
+
message: signal.message,
|
|
21
|
+
file: signal.filePath,
|
|
22
|
+
...(signal.line === undefined ? {} : { line: signal.line }),
|
|
23
|
+
...(signal.column === undefined ? {} : { column: signal.column }),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** A single shared no-op used for every level of the harness's stub logger. */
|
|
27
|
+
const noop = () => undefined;
|
|
28
|
+
function defaultCtx(fixture) {
|
|
29
|
+
return {
|
|
30
|
+
tool: fixture.tool,
|
|
31
|
+
projectRoot: '/acceptance',
|
|
32
|
+
runId: 'acceptance-run',
|
|
33
|
+
logger: {
|
|
34
|
+
info: noop,
|
|
35
|
+
warn: noop,
|
|
36
|
+
error: noop,
|
|
37
|
+
debug: noop,
|
|
38
|
+
},
|
|
39
|
+
config: {},
|
|
40
|
+
binary: { path: `/usr/bin/${fixture.tool}`, layer: 'path' },
|
|
41
|
+
artifactPath: (name) => `/acceptance/acceptance-run/${name}`,
|
|
42
|
+
...fixture.ctx,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Run a golden fixture through the substrate pipeline. Returns the normalized
|
|
47
|
+
* signals and a deterministic envelope (host fallback policy, message-hash
|
|
48
|
+
* fingerprints by default).
|
|
49
|
+
*/
|
|
50
|
+
export function runAcceptanceCase(fixture) {
|
|
51
|
+
const ctx = defaultCtx(fixture);
|
|
52
|
+
let signals;
|
|
53
|
+
if (fixture.kind === 'sarif') {
|
|
54
|
+
const parsed = safeParseJson(fixture.raw);
|
|
55
|
+
signals = parsed.ok ? ingestSarif(parsed.value, { source: fixture.tool }) : [];
|
|
56
|
+
}
|
|
57
|
+
else if (fixture.parse === undefined) {
|
|
58
|
+
signals = [];
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const json = fixture.kind === 'json' ? safeParseJson(fixture.raw) : undefined;
|
|
62
|
+
signals = fixture.parse({ kind: fixture.kind, raw: fixture.raw, ...(json?.ok === true ? { json: json.value } : {}) }, ctx);
|
|
63
|
+
}
|
|
64
|
+
const envelope = buildSignalEnvelope({
|
|
65
|
+
tool: fixture.tool,
|
|
66
|
+
runId: ctx.runId,
|
|
67
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
68
|
+
units: [
|
|
69
|
+
{ slug: 'scan', passed: signals.length === 0, violationCount: signals.length, durationMs: 0 },
|
|
70
|
+
],
|
|
71
|
+
signals,
|
|
72
|
+
policy: { failOnErrors: 1, failOnWarnings: 0 },
|
|
73
|
+
runFaulted: false,
|
|
74
|
+
fingerprintStrategy: resolveFingerprintStrategy(fixture.fingerprintStrategy),
|
|
75
|
+
});
|
|
76
|
+
return { signals, envelope };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=acceptance-harness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acceptance-harness.js","sourceRoot":"","sources":["../src/acceptance-harness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The DEFAULT namespaced config contribution every adapter claims
|
|
3
|
+
* (ADR-0090 §4.3 / ADR-0023 / ADR-0054 M4-E).
|
|
4
|
+
*
|
|
5
|
+
* Without a claimed namespace `cli.scope.toolConfig?.[<tool>]` is ALWAYS
|
|
6
|
+
* `undefined`, so (1) the documented `binaries.<tool>.path` operator pin is dead,
|
|
7
|
+
* (2) the per-tool verdict policy keys (`failOnErrors`/`failOnWarnings`/
|
|
8
|
+
* `failOnDegraded`) are non-configurable, and (3) an operator adding a `<tool>:`
|
|
9
|
+
* block to `opensip-cli.config.yml` hits the ADR-0043 unclaimed-namespace
|
|
10
|
+
* rejection and BRICKS every project command. So an adapter claims its namespace
|
|
11
|
+
* by default — on BOTH the runtime (the Zod schema the WORKER deep pass runs) and
|
|
12
|
+
* the static manifest (the JSON-Schema descriptor the HOST coarse pass validates
|
|
13
|
+
* against pre-fork, since the host never imports the tool's Zod — ADR-0054 M4-E).
|
|
14
|
+
*
|
|
15
|
+
* Two halves, kept in lock-step:
|
|
16
|
+
* - {@link defaultAdapterConfigSchema} — the runtime Zod. The DEEP pass
|
|
17
|
+
* (`runDeepConfigPass`, worker-side) runs THIS raw schema (it is NOT the
|
|
18
|
+
* gate-key-decorated one the host composes), so the gate keys MUST be declared
|
|
19
|
+
* here or a `<tool>: { failOnWarnings: … }` block would fail the deep pass.
|
|
20
|
+
* - {@link defaultAdapterConfigManifest} — the serializable JSON-Schema
|
|
21
|
+
* descriptor. Declares ONLY `binaries` (coarse — host validates structure
|
|
22
|
+
* pre-fork); the host folds in the reserved gate keys via
|
|
23
|
+
* `decorateToolConfigDeclarationsWithGateKeys` exactly as it does for a bundled
|
|
24
|
+
* tool, so the gate thresholds are configurable like any bundled tool.
|
|
25
|
+
*
|
|
26
|
+
* `binaries` is a `{ <tool>: { path } }` map (the resolver reads
|
|
27
|
+
* `binaries[<tool>].path`, mirroring `binaries.<tool>.path` in the docs).
|
|
28
|
+
*/
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
import type { ToolConfigManifestDescriptor } from '@opensip-cli/core';
|
|
31
|
+
/** The author-facing config slot (namespace is derived from identity by `defineTool`). */
|
|
32
|
+
export interface AdapterConfigContribution {
|
|
33
|
+
/** The namespace Zod schema — a strict object claiming `binaries` + the gate keys. */
|
|
34
|
+
readonly schema: z.ZodType;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The runtime Zod schema the default adapter config claims for its namespace:
|
|
38
|
+
* the operator binary pin (`binaries.<tool>.path`) PLUS the three reserved
|
|
39
|
+
* verdict-policy keys. Strict, so a typo inside the block is rejected by the
|
|
40
|
+
* worker deep pass. The gate keys are declared HERE (unlike a bundled tool, which
|
|
41
|
+
* leans on the host decorator) because the worker deep pass runs this raw schema —
|
|
42
|
+
* the decorated one only governs the host pre-fork pass.
|
|
43
|
+
*/
|
|
44
|
+
export declare function defaultAdapterConfigSchema(): z.ZodType;
|
|
45
|
+
/** The default config contribution forwarded to `defineTool` when a spec omits `config`. */
|
|
46
|
+
export declare function defaultAdapterConfig(): AdapterConfigContribution;
|
|
47
|
+
/**
|
|
48
|
+
* The serializable JSON-Schema descriptor the host coarse pass validates an
|
|
49
|
+
* installed adapter's `<tool>:` block against BEFORE forking (it never imports
|
|
50
|
+
* the tool's Zod — ADR-0054 M4-E). Declares ONLY the `binaries` block as a coarse
|
|
51
|
+
* object; the reserved gate keys are folded in host-side by
|
|
52
|
+
* `decorateToolConfigDeclarationsWithGateKeys`, so they are NOT (and must not be)
|
|
53
|
+
* duplicated here — that keeps the gate-key min(0) policy single-sourced in the
|
|
54
|
+
* decorator, exactly as for a bundled tool. The deep, value-level validation of
|
|
55
|
+
* `binaries.<tool>.path` runs worker-side via {@link defaultAdapterConfigSchema}.
|
|
56
|
+
*/
|
|
57
|
+
export declare function defaultAdapterConfigManifest(namespace: string): ToolConfigManifestDescriptor;
|
|
58
|
+
//# sourceMappingURL=adapter-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-config.d.ts","sourceRoot":"","sources":["../src/adapter-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAoB,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AAExF,0FAA0F;AAC1F,MAAM,WAAW,yBAAyB;IACxC,sFAAsF;IACtF,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,IAAI,CAAC,CAAC,OAAO,CAWtD;AAED,4FAA4F;AAC5F,wBAAgB,oBAAoB,IAAI,yBAAyB,CAEhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,4BAA4B,CAQ5F"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The DEFAULT namespaced config contribution every adapter claims
|
|
3
|
+
* (ADR-0090 §4.3 / ADR-0023 / ADR-0054 M4-E).
|
|
4
|
+
*
|
|
5
|
+
* Without a claimed namespace `cli.scope.toolConfig?.[<tool>]` is ALWAYS
|
|
6
|
+
* `undefined`, so (1) the documented `binaries.<tool>.path` operator pin is dead,
|
|
7
|
+
* (2) the per-tool verdict policy keys (`failOnErrors`/`failOnWarnings`/
|
|
8
|
+
* `failOnDegraded`) are non-configurable, and (3) an operator adding a `<tool>:`
|
|
9
|
+
* block to `opensip-cli.config.yml` hits the ADR-0043 unclaimed-namespace
|
|
10
|
+
* rejection and BRICKS every project command. So an adapter claims its namespace
|
|
11
|
+
* by default — on BOTH the runtime (the Zod schema the WORKER deep pass runs) and
|
|
12
|
+
* the static manifest (the JSON-Schema descriptor the HOST coarse pass validates
|
|
13
|
+
* against pre-fork, since the host never imports the tool's Zod — ADR-0054 M4-E).
|
|
14
|
+
*
|
|
15
|
+
* Two halves, kept in lock-step:
|
|
16
|
+
* - {@link defaultAdapterConfigSchema} — the runtime Zod. The DEEP pass
|
|
17
|
+
* (`runDeepConfigPass`, worker-side) runs THIS raw schema (it is NOT the
|
|
18
|
+
* gate-key-decorated one the host composes), so the gate keys MUST be declared
|
|
19
|
+
* here or a `<tool>: { failOnWarnings: … }` block would fail the deep pass.
|
|
20
|
+
* - {@link defaultAdapterConfigManifest} — the serializable JSON-Schema
|
|
21
|
+
* descriptor. Declares ONLY `binaries` (coarse — host validates structure
|
|
22
|
+
* pre-fork); the host folds in the reserved gate keys via
|
|
23
|
+
* `decorateToolConfigDeclarationsWithGateKeys` exactly as it does for a bundled
|
|
24
|
+
* tool, so the gate thresholds are configurable like any bundled tool.
|
|
25
|
+
*
|
|
26
|
+
* `binaries` is a `{ <tool>: { path } }` map (the resolver reads
|
|
27
|
+
* `binaries[<tool>].path`, mirroring `binaries.<tool>.path` in the docs).
|
|
28
|
+
*/
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
/**
|
|
31
|
+
* The runtime Zod schema the default adapter config claims for its namespace:
|
|
32
|
+
* the operator binary pin (`binaries.<tool>.path`) PLUS the three reserved
|
|
33
|
+
* verdict-policy keys. Strict, so a typo inside the block is rejected by the
|
|
34
|
+
* worker deep pass. The gate keys are declared HERE (unlike a bundled tool, which
|
|
35
|
+
* leans on the host decorator) because the worker deep pass runs this raw schema —
|
|
36
|
+
* the decorated one only governs the host pre-fork pass.
|
|
37
|
+
*/
|
|
38
|
+
export function defaultAdapterConfigSchema() {
|
|
39
|
+
return z
|
|
40
|
+
.object({
|
|
41
|
+
binaries: z
|
|
42
|
+
.record(z.string(), z.object({ path: z.string().min(1).optional() }).strict())
|
|
43
|
+
.optional(),
|
|
44
|
+
failOnErrors: z.number().int().min(0).optional(),
|
|
45
|
+
failOnWarnings: z.number().int().min(0).optional(),
|
|
46
|
+
failOnDegraded: z.boolean().optional(),
|
|
47
|
+
})
|
|
48
|
+
.strict();
|
|
49
|
+
}
|
|
50
|
+
/** The default config contribution forwarded to `defineTool` when a spec omits `config`. */
|
|
51
|
+
export function defaultAdapterConfig() {
|
|
52
|
+
return { schema: defaultAdapterConfigSchema() };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The serializable JSON-Schema descriptor the host coarse pass validates an
|
|
56
|
+
* installed adapter's `<tool>:` block against BEFORE forking (it never imports
|
|
57
|
+
* the tool's Zod — ADR-0054 M4-E). Declares ONLY the `binaries` block as a coarse
|
|
58
|
+
* object; the reserved gate keys are folded in host-side by
|
|
59
|
+
* `decorateToolConfigDeclarationsWithGateKeys`, so they are NOT (and must not be)
|
|
60
|
+
* duplicated here — that keeps the gate-key min(0) policy single-sourced in the
|
|
61
|
+
* decorator, exactly as for a bundled tool. The deep, value-level validation of
|
|
62
|
+
* `binaries.<tool>.path` runs worker-side via {@link defaultAdapterConfigSchema}.
|
|
63
|
+
*/
|
|
64
|
+
export function defaultAdapterConfigManifest(namespace) {
|
|
65
|
+
const schema = {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
binaries: { type: 'object' },
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
return { namespace, schema };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=adapter-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-config.js","sourceRoot":"","sources":["../src/adapter-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B;IACxC,OAAO,CAAC;SACL,MAAM,CAAC;QACN,QAAQ,EAAE,CAAC;aACR,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;aAC7E,QAAQ,EAAE;QACb,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;QAChD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;QAClD,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;KACvC,CAAC;SACD,MAAM,EAAE,CAAC;AACd,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,oBAAoB;IAClC,OAAO,EAAE,MAAM,EAAE,0BAA0B,EAAE,EAAE,CAAC;AAClD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAAC,SAAiB;IAC5D,MAAM,MAAM,GAAqB;QAC/B,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC7B;KACF,CAAC;IACF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Manifest derivation from the built adapter `Tool` (ADR-0090
|
|
3
|
+
* §4.3 / §4.8 / ADR-0092) — the `config` namespace claim and the `requires`
|
|
4
|
+
* resource needs.
|
|
5
|
+
*
|
|
6
|
+
* An installed adapter's static `package.json#opensipTools` must mirror its
|
|
7
|
+
* runtime, and the generator (`build-tool-command-manifests.mjs`) plus a per-tool
|
|
8
|
+
* `--check` parity gate keep them in lock-step. `deriveAdapterManifestCommands`
|
|
9
|
+
* (a sibling) derives the command shells; the helpers here derive the remaining
|
|
10
|
+
* adapter-shaped manifest fields from data the substrate STAMPS on the built Tool
|
|
11
|
+
* ({@link AdapterToolMarkers}) — the substrate's own data, never a core `Tool`
|
|
12
|
+
* concept:
|
|
13
|
+
*
|
|
14
|
+
* - {@link deriveAdapterManifestRequires} forward-maps the declared `network`
|
|
15
|
+
* posture (ADR-0092) onto `opensipTools.requires`: `subprocess` + `filesystem`
|
|
16
|
+
* ALWAYS (every adapter `execFile`s a binary and reads/writes the project +
|
|
17
|
+
* artifact store), plus `network` WHEN the posture is not `local-only`. This is
|
|
18
|
+
* the §4.8 "honest labeling" mapping the `types.ts` contract claims — derived,
|
|
19
|
+
* not hand-authored, so flipping an adapter to `networked` produces a `--check`
|
|
20
|
+
* drift the gate catches.
|
|
21
|
+
* - {@link deriveAdapterConfigManifest} returns the coarse config descriptor the
|
|
22
|
+
* adapter claims (its namespace + `binaries` block) so the host pre-fork pass
|
|
23
|
+
* recognizes the namespace and an operator's `<tool>:` block no longer bricks.
|
|
24
|
+
*/
|
|
25
|
+
import type { NetworkPosture } from './types.js';
|
|
26
|
+
import type { Tool, ToolConfigManifestDescriptor, ToolResourceRequirement } from '@opensip-cli/core';
|
|
27
|
+
/**
|
|
28
|
+
* The adapter-substrate markers {@link defineExternalToolAdapter} stamps on the
|
|
29
|
+
* Tool it returns. Kept off the core `Tool` contract (an adapter concept, not a
|
|
30
|
+
* kernel one); the substrate reads them back here to derive manifest fields.
|
|
31
|
+
*/
|
|
32
|
+
export interface AdapterToolMarkers {
|
|
33
|
+
/** The declared network posture (ADR-0092), drives `requires` derivation. */
|
|
34
|
+
readonly adapterNetwork?: NetworkPosture;
|
|
35
|
+
/** The coarse config descriptor the adapter claims, or `undefined` for a custom config. */
|
|
36
|
+
readonly adapterConfigManifest?: ToolConfigManifestDescriptor;
|
|
37
|
+
}
|
|
38
|
+
/** Read the stamped network posture; defaults to `'local-only'` (the safe, no-network posture). */
|
|
39
|
+
export declare function adapterNetworkOf(tool: Tool): NetworkPosture;
|
|
40
|
+
/** Read the stamped config descriptor, or `undefined` when the adapter uses a custom config. */
|
|
41
|
+
export declare function adapterConfigManifestOf(tool: Tool): ToolConfigManifestDescriptor | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Derive `opensipTools.requires` from the adapter's network posture (ADR-0092
|
|
44
|
+
* §4.8). `subprocess` + `filesystem` are unconditional; `network` is added only
|
|
45
|
+
* when the posture is `networked`/`auth-required`. Pure — the manifest generator
|
|
46
|
+
* writes the result and the per-tool `--check` parity gate fails on drift.
|
|
47
|
+
*/
|
|
48
|
+
export declare function deriveAdapterManifestRequires(tool: Tool): readonly ToolResourceRequirement[];
|
|
49
|
+
/**
|
|
50
|
+
* Derive the static `opensipTools.config` descriptor for an adapter — the coarse
|
|
51
|
+
* namespace claim the host validates a `<tool>:` block against pre-fork. Returns
|
|
52
|
+
* `undefined` for an adapter whose config cannot be coarsely serialized (a custom
|
|
53
|
+
* `spec.config`), which defers ALL of its config validation to the worker deep
|
|
54
|
+
* pass (ADR-0054 M4-E); the generator then omits the `config` key.
|
|
55
|
+
*/
|
|
56
|
+
export declare function deriveAdapterConfigManifest(tool: Tool): ToolConfigManifestDescriptor | undefined;
|
|
57
|
+
//# sourceMappingURL=adapter-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-manifest.d.ts","sourceRoot":"","sources":["../src/adapter-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EACV,IAAI,EACJ,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,2FAA2F;IAC3F,QAAQ,CAAC,qBAAqB,CAAC,EAAE,4BAA4B,CAAC;CAC/D;AAED,mGAAmG;AACnG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,cAAc,CAE3D;AAED,gGAAgG;AAChG,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,GAAG,4BAA4B,GAAG,SAAS,CAE5F;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,uBAAuB,EAAE,CAmB5F;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,IAAI,GAAG,4BAA4B,GAAG,SAAS,CAEhG"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Manifest derivation from the built adapter `Tool` (ADR-0090
|
|
3
|
+
* §4.3 / §4.8 / ADR-0092) — the `config` namespace claim and the `requires`
|
|
4
|
+
* resource needs.
|
|
5
|
+
*
|
|
6
|
+
* An installed adapter's static `package.json#opensipTools` must mirror its
|
|
7
|
+
* runtime, and the generator (`build-tool-command-manifests.mjs`) plus a per-tool
|
|
8
|
+
* `--check` parity gate keep them in lock-step. `deriveAdapterManifestCommands`
|
|
9
|
+
* (a sibling) derives the command shells; the helpers here derive the remaining
|
|
10
|
+
* adapter-shaped manifest fields from data the substrate STAMPS on the built Tool
|
|
11
|
+
* ({@link AdapterToolMarkers}) — the substrate's own data, never a core `Tool`
|
|
12
|
+
* concept:
|
|
13
|
+
*
|
|
14
|
+
* - {@link deriveAdapterManifestRequires} forward-maps the declared `network`
|
|
15
|
+
* posture (ADR-0092) onto `opensipTools.requires`: `subprocess` + `filesystem`
|
|
16
|
+
* ALWAYS (every adapter `execFile`s a binary and reads/writes the project +
|
|
17
|
+
* artifact store), plus `network` WHEN the posture is not `local-only`. This is
|
|
18
|
+
* the §4.8 "honest labeling" mapping the `types.ts` contract claims — derived,
|
|
19
|
+
* not hand-authored, so flipping an adapter to `networked` produces a `--check`
|
|
20
|
+
* drift the gate catches.
|
|
21
|
+
* - {@link deriveAdapterConfigManifest} returns the coarse config descriptor the
|
|
22
|
+
* adapter claims (its namespace + `binaries` block) so the host pre-fork pass
|
|
23
|
+
* recognizes the namespace and an operator's `<tool>:` block no longer bricks.
|
|
24
|
+
*/
|
|
25
|
+
/** Read the stamped network posture; defaults to `'local-only'` (the safe, no-network posture). */
|
|
26
|
+
export function adapterNetworkOf(tool) {
|
|
27
|
+
return tool.adapterNetwork ?? 'local-only';
|
|
28
|
+
}
|
|
29
|
+
/** Read the stamped config descriptor, or `undefined` when the adapter uses a custom config. */
|
|
30
|
+
export function adapterConfigManifestOf(tool) {
|
|
31
|
+
return tool.adapterConfigManifest;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Derive `opensipTools.requires` from the adapter's network posture (ADR-0092
|
|
35
|
+
* §4.8). `subprocess` + `filesystem` are unconditional; `network` is added only
|
|
36
|
+
* when the posture is `networked`/`auth-required`. Pure — the manifest generator
|
|
37
|
+
* writes the result and the per-tool `--check` parity gate fails on drift.
|
|
38
|
+
*/
|
|
39
|
+
export function deriveAdapterManifestRequires(tool) {
|
|
40
|
+
const requires = [
|
|
41
|
+
{
|
|
42
|
+
resource: 'subprocess',
|
|
43
|
+
reason: `Executes the user-installed ${tool.metadata.name} binary via execFile (no shell)`,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
resource: 'filesystem',
|
|
47
|
+
reason: 'Reads the project working tree and writes the raw scan artifact under .runtime/artifacts',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
if (adapterNetworkOf(tool) !== 'local-only') {
|
|
51
|
+
requires.push({
|
|
52
|
+
resource: 'network',
|
|
53
|
+
reason: `Performs network I/O for the ${adapterNetworkOf(tool)} scanner posture`,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return requires;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Derive the static `opensipTools.config` descriptor for an adapter — the coarse
|
|
60
|
+
* namespace claim the host validates a `<tool>:` block against pre-fork. Returns
|
|
61
|
+
* `undefined` for an adapter whose config cannot be coarsely serialized (a custom
|
|
62
|
+
* `spec.config`), which defers ALL of its config validation to the worker deep
|
|
63
|
+
* pass (ADR-0054 M4-E); the generator then omits the `config` key.
|
|
64
|
+
*/
|
|
65
|
+
export function deriveAdapterConfigManifest(tool) {
|
|
66
|
+
return adapterConfigManifestOf(tool);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=adapter-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-manifest.js","sourceRoot":"","sources":["../src/adapter-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAqBH,mGAAmG;AACnG,MAAM,UAAU,gBAAgB,CAAC,IAAU;IACzC,OAAQ,IAAkC,CAAC,cAAc,IAAI,YAAY,CAAC;AAC5E,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,uBAAuB,CAAC,IAAU;IAChD,OAAQ,IAAkC,CAAC,qBAAqB,CAAC;AACnE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAAU;IACtD,MAAM,QAAQ,GAA8B;QAC1C;YACE,QAAQ,EAAE,YAAY;YACtB,MAAM,EAAE,+BAA+B,IAAI,CAAC,QAAQ,CAAC,IAAI,iCAAiC;SAC3F;QACD;YACE,QAAQ,EAAE,YAAY;YACtB,MAAM,EACJ,0FAA0F;SAC7F;KACF,CAAC;IACF,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,YAAY,EAAE,CAAC;QAC5C,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,SAAS;YACnB,MAAM,EAAE,gCAAgC,gBAAgB,CAAC,IAAI,CAAC,kBAAkB;SACjF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CAAC,IAAU;IACpD,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC"}
|