@enwithai/product-check 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +4 -4
- package/CHANGELOG.md +28 -0
- package/README.md +29 -16
- package/SECURITY.md +5 -1
- package/dashboard/assurance.css +71 -0
- package/dashboard/assurance.js +21 -0
- package/dist/assurance/adapters.d.ts +32 -0
- package/dist/assurance/adapters.js +76 -0
- package/dist/assurance/approvals.d.ts +34 -0
- package/dist/assurance/approvals.js +133 -0
- package/dist/assurance/cli.d.ts +13 -0
- package/dist/assurance/cli.js +863 -0
- package/dist/assurance/dashboard.d.ts +18 -0
- package/dist/assurance/dashboard.js +267 -0
- package/dist/assurance/diff.d.ts +20 -0
- package/dist/assurance/diff.js +99 -0
- package/dist/assurance/evidence-log.d.ts +20 -0
- package/dist/assurance/evidence-log.js +100 -0
- package/dist/assurance/fix-packets.d.ts +12 -0
- package/dist/assurance/fix-packets.js +178 -0
- package/dist/assurance/gate.d.ts +37 -0
- package/dist/assurance/gate.js +324 -0
- package/dist/assurance/lanes.d.ts +61 -0
- package/dist/assurance/lanes.js +380 -0
- package/dist/assurance/policy.d.ts +10 -0
- package/dist/assurance/policy.js +85 -0
- package/dist/assurance/proof.d.ts +35 -0
- package/dist/assurance/proof.js +146 -0
- package/dist/assurance/register.d.ts +41 -0
- package/dist/assurance/register.js +480 -0
- package/dist/assurance/render-md.d.ts +17 -0
- package/dist/assurance/render-md.js +212 -0
- package/dist/assurance/repo-state.d.ts +15 -0
- package/dist/assurance/repo-state.js +79 -0
- package/dist/assurance/review.d.ts +52 -0
- package/dist/assurance/review.js +408 -0
- package/dist/assurance/risk.d.ts +21 -0
- package/dist/assurance/risk.js +184 -0
- package/dist/assurance/types.d.ts +573 -0
- package/dist/assurance/types.js +6 -0
- package/dist/assurance/util.d.ts +24 -0
- package/dist/assurance/util.js +89 -0
- package/dist/assurance/verification.d.ts +30 -0
- package/dist/assurance/verification.js +212 -0
- package/dist/assurance/verify.d.ts +26 -0
- package/dist/assurance/verify.js +304 -0
- package/dist/cli/run.js +8 -1
- package/dist/exec/process.d.ts +27 -0
- package/dist/exec/process.js +175 -0
- package/dist/reporting/markdown.d.ts +1 -0
- package/dist/reporting/markdown.js +1 -1
- package/dist/schema/registry.js +9 -0
- package/package.json +1 -1
- package/schemas/approval.schema.json +98 -0
- package/schemas/assurance-common.schema.json +291 -0
- package/schemas/assurance-policy.schema.json +185 -0
- package/schemas/change-review.schema.json +711 -0
- package/schemas/closure-register.schema.json +499 -0
- package/schemas/evidence-record.schema.json +94 -0
- package/schemas/fix-packet.schema.json +272 -0
- package/schemas/release-gate.schema.json +308 -0
- package/schemas/reviewer-findings.schema.json +49 -0
- package/schemas/verification-run.schema.json +199 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 EnWithAI Technologies
|
|
3
|
+
/**
|
|
4
|
+
* The ONLY module in src/ that starts processes (tests enforce this). Two narrow entry points:
|
|
5
|
+
*
|
|
6
|
+
* - `gitRead`: read-only git plumbing (rev-parse, diff, show, status, log, merge-base) with a fixed, hardened
|
|
7
|
+
* argument prefix: no hooks, no fsmonitor, no external diff or textconv drivers, no pager, no system config, no
|
|
8
|
+
* prompts. Revisions are validated so a value can never be read as an option.
|
|
9
|
+
* - `execCommand`: runs one operator-declared command (argv, never a shell) for author-side proof (VC-P1) and
|
|
10
|
+
* closure verification (VC-P5). Callers must have checked that execution was authorized for this invocation
|
|
11
|
+
* (`--allow-exec`); this module refuses otherwise. The environment is reduced to an allowlist, output is capped,
|
|
12
|
+
* digested and redacted, and nothing is retried.
|
|
13
|
+
*
|
|
14
|
+
* The scan pipeline (`run`, `retest`, `scan`, `spec`) never calls this module.
|
|
15
|
+
*/
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
17
|
+
import { devNull } from "node:os";
|
|
18
|
+
import { resolve, sep } from "node:path";
|
|
19
|
+
import { safetyError } from "../errors.js";
|
|
20
|
+
import { sha256 } from "../util/hash.js";
|
|
21
|
+
import { redact } from "../util/redact.js";
|
|
22
|
+
import { stableId } from "../util/hash.js";
|
|
23
|
+
const GIT_SUBCOMMANDS = new Set(["rev-parse", "diff", "show", "status", "log", "merge-base"]);
|
|
24
|
+
const GIT_PREFIX = [
|
|
25
|
+
"-c",
|
|
26
|
+
"core.fsmonitor=false",
|
|
27
|
+
"-c",
|
|
28
|
+
`core.hooksPath=${devNull}`,
|
|
29
|
+
"-c",
|
|
30
|
+
"diff.external=",
|
|
31
|
+
"-c",
|
|
32
|
+
"core.pager=cat",
|
|
33
|
+
"-c",
|
|
34
|
+
"protocol.allow=never",
|
|
35
|
+
"--no-pager",
|
|
36
|
+
];
|
|
37
|
+
/** Runs a read-only git subcommand in `repo`. */
|
|
38
|
+
export function gitRead(repo, args, maxBuffer = 64 * 1024 * 1024) {
|
|
39
|
+
const sub = args[0];
|
|
40
|
+
if (sub === undefined || !GIT_SUBCOMMANDS.has(sub))
|
|
41
|
+
throw safetyError(`git ${sub ?? ""} is not a read-only subcommand.`);
|
|
42
|
+
const extra = sub === "diff" || sub === "show" ? ["--no-ext-diff", "--no-textconv", "--no-color"] : [];
|
|
43
|
+
const r = spawnSync("git", [...GIT_PREFIX, sub, ...extra, ...args.slice(1)], {
|
|
44
|
+
cwd: repo,
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
maxBuffer,
|
|
47
|
+
shell: false,
|
|
48
|
+
windowsHide: true,
|
|
49
|
+
env: {
|
|
50
|
+
PATH: process.env["PATH"] ?? "",
|
|
51
|
+
HOME: process.env["HOME"] ?? "",
|
|
52
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
53
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
54
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
55
|
+
LC_ALL: "C",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
ok: r.status === 0 && !r.error,
|
|
60
|
+
stdout: r.stdout ?? "",
|
|
61
|
+
stderr: r.stderr ?? r.error?.message ?? "",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** A NOT_TESTED/BLOCKED execution record that ran nothing. */
|
|
65
|
+
function notRun(input, status, reason) {
|
|
66
|
+
return {
|
|
67
|
+
execution_id: stableId("EXE", [
|
|
68
|
+
input.spec.id,
|
|
69
|
+
input.spec.argv.join(" "),
|
|
70
|
+
input.commitSha,
|
|
71
|
+
status,
|
|
72
|
+
reason,
|
|
73
|
+
]),
|
|
74
|
+
command_id: input.spec.id,
|
|
75
|
+
purpose: input.spec.purpose,
|
|
76
|
+
argv: input.spec.argv,
|
|
77
|
+
cwd: input.spec.cwd,
|
|
78
|
+
status,
|
|
79
|
+
exit_code: null,
|
|
80
|
+
signal: null,
|
|
81
|
+
duration_ms: 0,
|
|
82
|
+
started_at: input.now().toISOString(),
|
|
83
|
+
environment_id: input.environmentId,
|
|
84
|
+
commit_sha: input.commitSha,
|
|
85
|
+
stdout_digest: null,
|
|
86
|
+
stderr_digest: null,
|
|
87
|
+
output_bytes: 0,
|
|
88
|
+
output_truncated: false,
|
|
89
|
+
excerpt: null,
|
|
90
|
+
reason,
|
|
91
|
+
evidence_id: null,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** Executes one declared command (argv, no shell). Never throws for command failures. */
|
|
95
|
+
export function execCommand(input) {
|
|
96
|
+
const { spec } = input;
|
|
97
|
+
if (!input.permission.allowed)
|
|
98
|
+
return notRun(input, "NOT_TESTED", input.permission.reason);
|
|
99
|
+
if (spec.argv.length === 0 || spec.argv[0] === "")
|
|
100
|
+
return notRun(input, "BLOCKED", "Empty command.");
|
|
101
|
+
const root = resolve(input.repo);
|
|
102
|
+
const cwd = resolve(root, spec.cwd);
|
|
103
|
+
if (cwd !== root && !cwd.startsWith(root + sep))
|
|
104
|
+
return notRun(input, "BLOCKED", `Working directory ${spec.cwd} is outside the repository.`);
|
|
105
|
+
const env = { CI: "1", PRODUCT_CHECK_VERIFY: "1" };
|
|
106
|
+
for (const name of input.envAllowlist) {
|
|
107
|
+
const v = process.env[name];
|
|
108
|
+
if (v !== undefined)
|
|
109
|
+
env[name] = v;
|
|
110
|
+
}
|
|
111
|
+
const started = input.now();
|
|
112
|
+
const t0 = performance.now();
|
|
113
|
+
const r = spawnSync(spec.argv[0], spec.argv.slice(1), {
|
|
114
|
+
cwd,
|
|
115
|
+
env,
|
|
116
|
+
shell: false,
|
|
117
|
+
windowsHide: true,
|
|
118
|
+
timeout: spec.timeout_ms,
|
|
119
|
+
maxBuffer: input.maxOutputBytes,
|
|
120
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
121
|
+
});
|
|
122
|
+
const duration = Math.round(performance.now() - t0);
|
|
123
|
+
const stdout = r.stdout ?? Buffer.alloc(0);
|
|
124
|
+
const stderr = r.stderr ?? Buffer.alloc(0);
|
|
125
|
+
const err = r.error;
|
|
126
|
+
const truncated = err?.code === "ENOBUFS";
|
|
127
|
+
const timedOut = err?.code === "ETIMEDOUT" || (r.signal === "SIGTERM" && duration >= spec.timeout_ms);
|
|
128
|
+
const spawnFailed = err !== undefined && !truncated && !timedOut;
|
|
129
|
+
const combined = `${stdout.toString("utf8")}\n${stderr.toString("utf8")}`.trim();
|
|
130
|
+
const excerpt = combined ? redact(combined.slice(-400)) : null;
|
|
131
|
+
let status;
|
|
132
|
+
let reason = null;
|
|
133
|
+
if (spawnFailed) {
|
|
134
|
+
status = "BLOCKED";
|
|
135
|
+
reason = `Could not start ${spec.argv[0]}: ${err.code ?? err.message}`;
|
|
136
|
+
}
|
|
137
|
+
else if (timedOut) {
|
|
138
|
+
status = "FAIL";
|
|
139
|
+
reason = `Timed out after ${spec.timeout_ms} ms.`;
|
|
140
|
+
}
|
|
141
|
+
else if (truncated) {
|
|
142
|
+
status = "FAIL";
|
|
143
|
+
reason = `Output exceeded ${input.maxOutputBytes} bytes; the run was stopped.`;
|
|
144
|
+
}
|
|
145
|
+
else
|
|
146
|
+
status = r.status === 0 ? "PASS" : "FAIL";
|
|
147
|
+
return {
|
|
148
|
+
execution_id: stableId("EXE", [
|
|
149
|
+
spec.id,
|
|
150
|
+
spec.argv.join(" "),
|
|
151
|
+
input.commitSha,
|
|
152
|
+
started.toISOString(),
|
|
153
|
+
sha256(stdout),
|
|
154
|
+
sha256(stderr),
|
|
155
|
+
]),
|
|
156
|
+
command_id: spec.id,
|
|
157
|
+
purpose: spec.purpose,
|
|
158
|
+
argv: spec.argv,
|
|
159
|
+
cwd: spec.cwd,
|
|
160
|
+
status,
|
|
161
|
+
exit_code: r.status,
|
|
162
|
+
signal: r.signal ?? null,
|
|
163
|
+
duration_ms: duration,
|
|
164
|
+
started_at: started.toISOString(),
|
|
165
|
+
environment_id: input.environmentId,
|
|
166
|
+
commit_sha: input.commitSha,
|
|
167
|
+
stdout_digest: sha256(stdout),
|
|
168
|
+
stderr_digest: sha256(stderr),
|
|
169
|
+
output_bytes: stdout.length + stderr.length,
|
|
170
|
+
output_truncated: truncated,
|
|
171
|
+
excerpt,
|
|
172
|
+
reason,
|
|
173
|
+
evidence_id: null,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -9,6 +9,7 @@ import { PILLARS } from "../types.js";
|
|
|
9
9
|
export interface RenderOptions {
|
|
10
10
|
branding: boolean;
|
|
11
11
|
}
|
|
12
|
+
export declare const BRANDING = "Generated by Product Check, an EnWithAI Engineering Commons tool. JSON is the source of truth; this file is a rendering.";
|
|
12
13
|
/** One-line score text for a pillar. */
|
|
13
14
|
export declare function scoreText(r: Results, p: (typeof PILLARS)[number]): string;
|
|
14
15
|
/** The assessment report. */
|
|
@@ -4,7 +4,7 @@ import { TIER_INFO } from "../validation/tiers.js";
|
|
|
4
4
|
import { code, md } from "./escape.js";
|
|
5
5
|
const pct = (n) => `${Math.round(n * 100)}%`;
|
|
6
6
|
const PILLAR_LABEL = { customer: "Customer", technology: "Technology", trust: "Trust" };
|
|
7
|
-
const BRANDING = "Generated by Product Check, an EnWithAI Engineering Commons tool. JSON is the source of truth; this file is a rendering.";
|
|
7
|
+
export const BRANDING = "Generated by Product Check, an EnWithAI Engineering Commons tool. JSON is the source of truth; this file is a rendering.";
|
|
8
8
|
/** One-line score text for a pillar. */
|
|
9
9
|
export function scoreText(r, p) {
|
|
10
10
|
const s = r.scorecard.pillars[p];
|
package/dist/schema/registry.js
CHANGED
|
@@ -17,6 +17,15 @@ export const KIND_SCHEMA = {
|
|
|
17
17
|
"release-verdict": "release-verdict.schema.json",
|
|
18
18
|
observations: "observations.schema.json",
|
|
19
19
|
"as-built-spec": "as-built-spec.schema.json",
|
|
20
|
+
"assurance-policy": "assurance-policy.schema.json",
|
|
21
|
+
"evidence-record": "evidence-record.schema.json",
|
|
22
|
+
"change-review": "change-review.schema.json",
|
|
23
|
+
approval: "approval.schema.json",
|
|
24
|
+
"verification-run": "verification-run.schema.json",
|
|
25
|
+
"fix-packet": "fix-packet.schema.json",
|
|
26
|
+
"closure-register": "closure-register.schema.json",
|
|
27
|
+
"release-gate": "release-gate.schema.json",
|
|
28
|
+
"reviewer-findings": "reviewer-findings.schema.json",
|
|
20
29
|
};
|
|
21
30
|
let cached = null;
|
|
22
31
|
/** Lazily loads the bundled schemas. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enwithai/product-check",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Evidence-led product assurance and software blueprinting: scan a repository and/or an authorized deployed app, validate Customer, Technology and Trust controls with evidence, and get findings, a remediation blueprint, a readiness verdict and a local dashboard. Local-first; JSON is the source of truth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "EnWithAI Technologies",
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/enwithai/product-check/schemas/approval.schema.json",
|
|
4
|
+
"title": "Product Check approval",
|
|
5
|
+
"description": "A signed accountable decision. Counts only when the Ed25519 signature verifies against an approver key in the policy, the approver is human, the scope matches and reviewed_sha is the current commit.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"schema_version",
|
|
10
|
+
"kind",
|
|
11
|
+
"approval_id",
|
|
12
|
+
"approver",
|
|
13
|
+
"scope",
|
|
14
|
+
"reviewed_sha",
|
|
15
|
+
"decision",
|
|
16
|
+
"comment",
|
|
17
|
+
"created_at",
|
|
18
|
+
"signature"
|
|
19
|
+
],
|
|
20
|
+
"properties": {
|
|
21
|
+
"schema_version": {
|
|
22
|
+
"$ref": "assurance-common.schema.json#/$defs/schema_version"
|
|
23
|
+
},
|
|
24
|
+
"kind": {
|
|
25
|
+
"const": "approval"
|
|
26
|
+
},
|
|
27
|
+
"approval_id": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"pattern": "^AP-[0-9A-F]{12}$"
|
|
30
|
+
},
|
|
31
|
+
"approver": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"required": ["id", "type"],
|
|
35
|
+
"properties": {
|
|
36
|
+
"id": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"minLength": 1
|
|
39
|
+
},
|
|
40
|
+
"type": {
|
|
41
|
+
"enum": ["human", "bot"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"scope": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"additionalProperties": false,
|
|
48
|
+
"required": ["type", "ref"],
|
|
49
|
+
"properties": {
|
|
50
|
+
"type": {
|
|
51
|
+
"enum": ["change", "closure", "risk-acceptance", "false-positive", "release", "continue"]
|
|
52
|
+
},
|
|
53
|
+
"ref": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"minLength": 1
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"reviewed_sha": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"minLength": 7
|
|
62
|
+
},
|
|
63
|
+
"decision": {
|
|
64
|
+
"enum": ["approve", "reject"]
|
|
65
|
+
},
|
|
66
|
+
"comment": {
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
"created_at": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"format": "date-time"
|
|
72
|
+
},
|
|
73
|
+
"signature": {
|
|
74
|
+
"oneOf": [
|
|
75
|
+
{
|
|
76
|
+
"type": "null"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"type": "object",
|
|
80
|
+
"additionalProperties": false,
|
|
81
|
+
"required": ["algorithm", "key_id", "value"],
|
|
82
|
+
"properties": {
|
|
83
|
+
"algorithm": {
|
|
84
|
+
"const": "ed25519"
|
|
85
|
+
},
|
|
86
|
+
"key_id": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"pattern": "^[0-9a-f]{16}$"
|
|
89
|
+
},
|
|
90
|
+
"value": {
|
|
91
|
+
"type": "string"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/enwithai/product-check/schemas/assurance-common.schema.json",
|
|
4
|
+
"title": "Product Check assurance shared definitions",
|
|
5
|
+
"description": "Enumerations and shapes shared by the CT3 v3.2 review and closure artifacts.",
|
|
6
|
+
"$defs": {
|
|
7
|
+
"schema_version": {
|
|
8
|
+
"const": "0.1.0"
|
|
9
|
+
},
|
|
10
|
+
"methodology_version": {
|
|
11
|
+
"const": "CT3 v3.2"
|
|
12
|
+
},
|
|
13
|
+
"verification_state": {
|
|
14
|
+
"enum": [
|
|
15
|
+
"SUSPECTED",
|
|
16
|
+
"EVIDENCE_COLLECTED",
|
|
17
|
+
"REPRODUCED",
|
|
18
|
+
"CORROBORATED",
|
|
19
|
+
"CONFIRMED",
|
|
20
|
+
"DISPROVEN",
|
|
21
|
+
"NEEDS_HUMAN_TRIAGE"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"closure_state": {
|
|
25
|
+
"enum": [
|
|
26
|
+
"OPEN",
|
|
27
|
+
"ACKNOWLEDGED",
|
|
28
|
+
"IN_PROGRESS",
|
|
29
|
+
"FIX_PROPOSED",
|
|
30
|
+
"RETEST_REQUIRED",
|
|
31
|
+
"VERIFIED_FIXED",
|
|
32
|
+
"CLOSED",
|
|
33
|
+
"REJECTED_FALSE_POSITIVE",
|
|
34
|
+
"ACCEPTED_RISK",
|
|
35
|
+
"DEFERRED",
|
|
36
|
+
"REGRESSED"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"risk_level": {
|
|
40
|
+
"enum": ["LOW", "MEDIUM", "HIGH", "UNDETERMINED"]
|
|
41
|
+
},
|
|
42
|
+
"gate_status": {
|
|
43
|
+
"enum": ["PASS", "CONDITIONAL", "BLOCK", "NOT_EVALUATED"]
|
|
44
|
+
},
|
|
45
|
+
"release_decision": {
|
|
46
|
+
"enum": ["GO", "CONDITIONAL_GO", "NO_GO", "UNKNOWN"]
|
|
47
|
+
},
|
|
48
|
+
"fix_status": {
|
|
49
|
+
"enum": ["PROPOSED", "IMPLEMENTED_UNVERIFIED", "VERIFIED", "REGRESSED"]
|
|
50
|
+
},
|
|
51
|
+
"lane": {
|
|
52
|
+
"enum": ["spec-outcome", "correctness", "impact", "trust-security", "experience", "operations"]
|
|
53
|
+
},
|
|
54
|
+
"phase": {
|
|
55
|
+
"enum": ["VC-P0", "VC-P1", "VC-P2", "VC-P3", "VC-P4", "VC-P5", "VC-P6", "VC-P7"]
|
|
56
|
+
},
|
|
57
|
+
"actor": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"required": ["id", "type"],
|
|
61
|
+
"properties": {
|
|
62
|
+
"id": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1
|
|
65
|
+
},
|
|
66
|
+
"type": {
|
|
67
|
+
"enum": ["human", "agent", "system"]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"command_spec": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"additionalProperties": false,
|
|
74
|
+
"required": ["id", "argv", "purpose", "timeout_ms", "cwd", "source"],
|
|
75
|
+
"properties": {
|
|
76
|
+
"id": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"minLength": 1
|
|
79
|
+
},
|
|
80
|
+
"argv": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": {
|
|
83
|
+
"type": "string"
|
|
84
|
+
},
|
|
85
|
+
"minItems": 1
|
|
86
|
+
},
|
|
87
|
+
"purpose": {
|
|
88
|
+
"enum": ["build", "typecheck", "lint", "test", "smoke", "reproduction", "regression", "other"]
|
|
89
|
+
},
|
|
90
|
+
"timeout_ms": {
|
|
91
|
+
"type": "integer",
|
|
92
|
+
"minimum": 1
|
|
93
|
+
},
|
|
94
|
+
"cwd": {
|
|
95
|
+
"type": "string"
|
|
96
|
+
},
|
|
97
|
+
"source": {
|
|
98
|
+
"enum": ["policy", "fix", "corpus"]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"environment": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"required": ["environment_id", "runner", "lockfile_digest"],
|
|
106
|
+
"properties": {
|
|
107
|
+
"environment_id": {
|
|
108
|
+
"type": "string"
|
|
109
|
+
},
|
|
110
|
+
"runner": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"additionalProperties": false,
|
|
113
|
+
"required": ["platform", "arch", "node_version", "tool_version"],
|
|
114
|
+
"properties": {
|
|
115
|
+
"platform": {
|
|
116
|
+
"type": "string"
|
|
117
|
+
},
|
|
118
|
+
"arch": {
|
|
119
|
+
"type": "string"
|
|
120
|
+
},
|
|
121
|
+
"node_version": {
|
|
122
|
+
"type": "string"
|
|
123
|
+
},
|
|
124
|
+
"tool_version": {
|
|
125
|
+
"type": "string"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"lockfile_digest": {
|
|
130
|
+
"type": ["string", "null"],
|
|
131
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"execution": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"additionalProperties": false,
|
|
138
|
+
"required": [
|
|
139
|
+
"execution_id",
|
|
140
|
+
"command_id",
|
|
141
|
+
"purpose",
|
|
142
|
+
"argv",
|
|
143
|
+
"cwd",
|
|
144
|
+
"status",
|
|
145
|
+
"exit_code",
|
|
146
|
+
"signal",
|
|
147
|
+
"duration_ms",
|
|
148
|
+
"started_at",
|
|
149
|
+
"environment_id",
|
|
150
|
+
"commit_sha",
|
|
151
|
+
"stdout_digest",
|
|
152
|
+
"stderr_digest",
|
|
153
|
+
"output_bytes",
|
|
154
|
+
"output_truncated",
|
|
155
|
+
"excerpt",
|
|
156
|
+
"reason",
|
|
157
|
+
"evidence_id"
|
|
158
|
+
],
|
|
159
|
+
"properties": {
|
|
160
|
+
"execution_id": {
|
|
161
|
+
"type": "string"
|
|
162
|
+
},
|
|
163
|
+
"command_id": {
|
|
164
|
+
"type": "string"
|
|
165
|
+
},
|
|
166
|
+
"purpose": {
|
|
167
|
+
"enum": ["build", "typecheck", "lint", "test", "smoke", "reproduction", "regression", "other"]
|
|
168
|
+
},
|
|
169
|
+
"argv": {
|
|
170
|
+
"type": "array",
|
|
171
|
+
"items": {
|
|
172
|
+
"type": "string"
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"cwd": {
|
|
176
|
+
"type": "string"
|
|
177
|
+
},
|
|
178
|
+
"status": {
|
|
179
|
+
"$ref": "common.schema.json#/$defs/validation_status"
|
|
180
|
+
},
|
|
181
|
+
"exit_code": {
|
|
182
|
+
"type": ["integer", "null"]
|
|
183
|
+
},
|
|
184
|
+
"signal": {
|
|
185
|
+
"type": ["string", "null"]
|
|
186
|
+
},
|
|
187
|
+
"duration_ms": {
|
|
188
|
+
"type": "integer",
|
|
189
|
+
"minimum": 0
|
|
190
|
+
},
|
|
191
|
+
"started_at": {
|
|
192
|
+
"type": "string",
|
|
193
|
+
"format": "date-time"
|
|
194
|
+
},
|
|
195
|
+
"environment_id": {
|
|
196
|
+
"type": "string"
|
|
197
|
+
},
|
|
198
|
+
"commit_sha": {
|
|
199
|
+
"type": ["string", "null"],
|
|
200
|
+
"pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
|
|
201
|
+
},
|
|
202
|
+
"stdout_digest": {
|
|
203
|
+
"type": ["string", "null"],
|
|
204
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
205
|
+
},
|
|
206
|
+
"stderr_digest": {
|
|
207
|
+
"type": ["string", "null"],
|
|
208
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
209
|
+
},
|
|
210
|
+
"output_bytes": {
|
|
211
|
+
"type": "integer",
|
|
212
|
+
"minimum": 0
|
|
213
|
+
},
|
|
214
|
+
"output_truncated": {
|
|
215
|
+
"type": "boolean"
|
|
216
|
+
},
|
|
217
|
+
"excerpt": {
|
|
218
|
+
"type": ["string", "null"],
|
|
219
|
+
"maxLength": 400
|
|
220
|
+
},
|
|
221
|
+
"reason": {
|
|
222
|
+
"type": ["string", "null"]
|
|
223
|
+
},
|
|
224
|
+
"evidence_id": {
|
|
225
|
+
"type": ["string", "null"]
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
"check": {
|
|
230
|
+
"type": "object",
|
|
231
|
+
"additionalProperties": false,
|
|
232
|
+
"required": ["check", "result", "detail"],
|
|
233
|
+
"properties": {
|
|
234
|
+
"check": {
|
|
235
|
+
"type": "string"
|
|
236
|
+
},
|
|
237
|
+
"result": {
|
|
238
|
+
"enum": ["pass", "fail", "not-run"]
|
|
239
|
+
},
|
|
240
|
+
"detail": {
|
|
241
|
+
"type": "string"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"verification": {
|
|
246
|
+
"type": "object",
|
|
247
|
+
"additionalProperties": false,
|
|
248
|
+
"required": ["state", "checks"],
|
|
249
|
+
"properties": {
|
|
250
|
+
"state": {
|
|
251
|
+
"$ref": "assurance-common.schema.json#/$defs/verification_state"
|
|
252
|
+
},
|
|
253
|
+
"checks": {
|
|
254
|
+
"type": "array",
|
|
255
|
+
"items": {
|
|
256
|
+
"$ref": "assurance-common.schema.json#/$defs/check"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"approval_evaluation": {
|
|
262
|
+
"type": "object",
|
|
263
|
+
"additionalProperties": false,
|
|
264
|
+
"required": ["approval_id", "approver_id", "status", "reason"],
|
|
265
|
+
"properties": {
|
|
266
|
+
"approval_id": {
|
|
267
|
+
"type": "string"
|
|
268
|
+
},
|
|
269
|
+
"approver_id": {
|
|
270
|
+
"type": "string"
|
|
271
|
+
},
|
|
272
|
+
"status": {
|
|
273
|
+
"enum": [
|
|
274
|
+
"VALID",
|
|
275
|
+
"STALE",
|
|
276
|
+
"INVALID_SIGNATURE",
|
|
277
|
+
"UNSIGNED",
|
|
278
|
+
"UNKNOWN_APPROVER",
|
|
279
|
+
"NOT_HUMAN",
|
|
280
|
+
"SELF_APPROVAL",
|
|
281
|
+
"WRONG_SCOPE",
|
|
282
|
+
"REJECTED"
|
|
283
|
+
]
|
|
284
|
+
},
|
|
285
|
+
"reason": {
|
|
286
|
+
"type": "string"
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|