@codyswann/lisa 4.73.7 → 4.73.9
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/all/copy-overwrite/scripts/lib/github-attestation-recovery.mjs +48 -35
- package/all/copy-overwrite/scripts/lib/npm-update-hosted-gate.mjs +15 -1
- package/all/copy-overwrite/scripts/lib/npm-update-hosted-hook.mjs +56 -27
- package/all/copy-overwrite/scripts/lib/npm-update-invariants.mjs +78 -1
- package/all/copy-overwrite/scripts/lib/npm-update-native-process.mjs +29 -10
- package/all/copy-overwrite/scripts/lisa-automation-provenance.mjs +86 -37
- package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +11 -8
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +14 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.js +7 -7
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
verifyAssignable,
|
|
9
9
|
} from "./github-attestation-provider.mjs";
|
|
10
10
|
import { boundedSpawnSync } from "./bounded-spawn.mjs";
|
|
11
|
+
import { withProvenancePhase } from "./npm-update-invariants.mjs";
|
|
11
12
|
import {
|
|
12
13
|
requireProof,
|
|
13
14
|
verifiedRole,
|
|
@@ -251,45 +252,57 @@ export function verifyRecoveryOrigin(
|
|
|
251
252
|
scope,
|
|
252
253
|
execute = boundedSpawnSync
|
|
253
254
|
) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
255
|
+
withProvenancePhase("recovery-local", () => {
|
|
256
|
+
validateRecovery(recovery, descriptor, digest, policy);
|
|
257
|
+
for (const key of [
|
|
258
|
+
"npmPolicySha256",
|
|
259
|
+
"proposalKey",
|
|
260
|
+
"leafBodySha256",
|
|
261
|
+
"commit",
|
|
262
|
+
])
|
|
263
|
+
requireProof(
|
|
264
|
+
recovery[key] === scope[key],
|
|
265
|
+
`current recovery ${key} differs`
|
|
266
|
+
);
|
|
261
267
|
requireProof(
|
|
262
|
-
|
|
263
|
-
|
|
268
|
+
process.env.GITHUB_RUN_ID === recovery.runId &&
|
|
269
|
+
process.env.GITHUB_RUN_ATTEMPT === recovery.runAttempt,
|
|
270
|
+
"recovery replayed into different current invocation"
|
|
264
271
|
);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
"recovery replayed into different current invocation"
|
|
272
|
+
});
|
|
273
|
+
const origin = withProvenancePhase("historical-provider", () =>
|
|
274
|
+
verifyHistoricalProvider(policy, descriptor, execute)
|
|
269
275
|
);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
withProvenancePhase("historical-signature", () =>
|
|
277
|
+
verifyHistoricalDescriptor(
|
|
278
|
+
policy,
|
|
279
|
+
descriptor,
|
|
280
|
+
paths,
|
|
281
|
+
digest,
|
|
282
|
+
origin.run,
|
|
283
|
+
origin.claim,
|
|
284
|
+
execute
|
|
285
|
+
)
|
|
279
286
|
);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
287
|
+
withProvenancePhase("recovery-signature", () =>
|
|
288
|
+
verifyRecoveryAttestation(
|
|
289
|
+
policy,
|
|
290
|
+
recovery,
|
|
291
|
+
paths,
|
|
292
|
+
scope.recoverySha256,
|
|
293
|
+
execute
|
|
294
|
+
)
|
|
286
295
|
);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
296
|
+
withProvenancePhase("recovery-provider", () =>
|
|
297
|
+
verifyRecoveryProvider(
|
|
298
|
+
policy,
|
|
299
|
+
descriptor,
|
|
300
|
+
recovery,
|
|
301
|
+
scope.maintainer,
|
|
302
|
+
execute
|
|
303
|
+
)
|
|
304
|
+
);
|
|
305
|
+
withProvenancePhase("assignable", () =>
|
|
306
|
+
verifyAssignable(policy, scope.maintainer, execute)
|
|
293
307
|
);
|
|
294
|
-
verifyAssignable(policy, scope.maintainer, execute);
|
|
295
308
|
}
|
|
@@ -17,7 +17,10 @@ import { affirmRuntimeProfile } from "./npm-update-rails-runtime-contract.mjs";
|
|
|
17
17
|
import { prepareRailsTools } from "./npm-update-rails-tools.mjs";
|
|
18
18
|
import { openRailsMysqlRuntime } from "./npm-update-rails-mysql.mjs";
|
|
19
19
|
import { installOriginalManager } from "./npm-update-hook-installation.mjs";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
withStage,
|
|
22
|
+
inheritProvenanceFailure,
|
|
23
|
+
} from "./npm-update-invariants.mjs";
|
|
21
24
|
|
|
22
25
|
const INSTALL_STAGE = "gate-install";
|
|
23
26
|
const VALIDATE_STAGE = "gate-validate";
|
|
@@ -66,6 +69,17 @@ function nativeStep(context, env, command, args, maximum = 3_145_728) {
|
|
|
66
69
|
env,
|
|
67
70
|
timeout: remainingGateTime(context.deadline),
|
|
68
71
|
maximum,
|
|
72
|
+
}).catch(error => {
|
|
73
|
+
if (
|
|
74
|
+
command === "node" &&
|
|
75
|
+
env.LISA_NPM_HOOK_ROLE === "commit" &&
|
|
76
|
+
typeof env.HOME === "string" &&
|
|
77
|
+
args.length === 2 &&
|
|
78
|
+
args[0] === "scripts/lisa-automation-provenance.mjs" &&
|
|
79
|
+
args[1] === join(env.HOME, "message")
|
|
80
|
+
)
|
|
81
|
+
inheritProvenanceFailure(error);
|
|
82
|
+
throw error;
|
|
69
83
|
});
|
|
70
84
|
}
|
|
71
85
|
|
|
@@ -9,6 +9,11 @@ import { realpathSync, lstatSync } from "node:fs";
|
|
|
9
9
|
import { resolve, join, basename } from "node:path";
|
|
10
10
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
11
|
import { binaryDigest } from "./npm-update-isolation.mjs";
|
|
12
|
+
import {
|
|
13
|
+
withProvenancePhase,
|
|
14
|
+
recordProvenancePhase,
|
|
15
|
+
provenanceFailure,
|
|
16
|
+
} from "./npm-update-invariants.mjs";
|
|
12
17
|
|
|
13
18
|
const ENTRY = fileURLToPath(import.meta.url);
|
|
14
19
|
const PRELOAD = fileURLToPath(
|
|
@@ -111,55 +116,79 @@ export function tokenFreeHookEnvironment(source) {
|
|
|
111
116
|
return env;
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
/** Input consumes the same fixed bound and one original deadline observation before native execution. */
|
|
120
|
+
async function originalHookInput(context) {
|
|
121
|
+
const chunks = [];
|
|
122
|
+
let size = 0;
|
|
123
|
+
try {
|
|
124
|
+
for await (const chunk of process.stdin) {
|
|
125
|
+
size += chunk.length;
|
|
126
|
+
required(size <= 4_194_304, "original hook stdin exceeded");
|
|
127
|
+
chunks.push(chunk);
|
|
128
|
+
}
|
|
129
|
+
const timeout = Math.min(1_800_000, context.deadline - Date.now());
|
|
130
|
+
required(timeout > 0, "original hook phase expired");
|
|
131
|
+
return { input: Buffer.concat(chunks), timeout };
|
|
132
|
+
} catch (error) {
|
|
133
|
+
recordProvenancePhase(error, "gateway-input");
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
114
138
|
async function main() {
|
|
115
139
|
const args = process.argv.slice(2);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
140
|
+
withProvenancePhase("gateway-context", () =>
|
|
141
|
+
required(
|
|
142
|
+
args.length >= 3 && args[0] === "--context" && args[2] === "--",
|
|
143
|
+
"invalid hosted Node gateway"
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
const context = withProvenancePhase("gateway-context", () =>
|
|
147
|
+
hostedHookContext(args[1])
|
|
119
148
|
);
|
|
120
|
-
const context = hostedHookContext(args[1]);
|
|
121
149
|
const original = args.slice(3);
|
|
122
|
-
const entry =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
!
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
150
|
+
const entry = withProvenancePhase("gateway-entry", () =>
|
|
151
|
+
hookEntry(context, original, realpathSync(process.cwd()))
|
|
152
|
+
);
|
|
153
|
+
withProvenancePhase("gateway-entry", () => {
|
|
154
|
+
if (entry)
|
|
155
|
+
required(
|
|
156
|
+
!original.some(arg =>
|
|
157
|
+
/^(?:-r|--require|--import|--(?:experimental-)?loader)(?:=|$)/.test(
|
|
158
|
+
arg
|
|
159
|
+
)
|
|
160
|
+
) &&
|
|
161
|
+
!process.env.NODE_OPTIONS &&
|
|
162
|
+
!process.env.NODE_PATH,
|
|
163
|
+
"unqualified original canonical hook loader"
|
|
164
|
+
);
|
|
165
|
+
});
|
|
132
166
|
const env = tokenFreeHookEnvironment(process.env);
|
|
133
167
|
if (entry) env.LISA_NPM_HOSTED_HOOK_CONTEXT = context.file;
|
|
134
168
|
else delete env.LISA_NPM_HOSTED_HOOK_CONTEXT;
|
|
135
|
-
const
|
|
136
|
-
let size = 0;
|
|
137
|
-
for await (const chunk of process.stdin) {
|
|
138
|
-
size += chunk.length;
|
|
139
|
-
required(size <= 4_194_304, "original hook stdin exceeded");
|
|
140
|
-
chunks.push(chunk);
|
|
141
|
-
}
|
|
142
|
-
const timeout = Math.min(1_800_000, context.deadline - Date.now());
|
|
143
|
-
required(timeout > 0, "original hook phase expired");
|
|
169
|
+
const { input, timeout } = await originalHookInput(context);
|
|
144
170
|
const result = await runProcess(
|
|
145
171
|
context.native.node.path,
|
|
146
172
|
entry ? ["--import", pathToFileURL(PRELOAD).href, ...original] : original,
|
|
147
173
|
{
|
|
148
174
|
cwd: process.cwd(),
|
|
149
175
|
env,
|
|
150
|
-
input
|
|
176
|
+
input,
|
|
151
177
|
timeout,
|
|
152
178
|
maximum: 8_388_608,
|
|
153
179
|
allowed: Array.from({ length: 256 }, (_, code) => code),
|
|
154
180
|
}
|
|
155
|
-
)
|
|
181
|
+
).catch(error => {
|
|
182
|
+
recordProvenancePhase(error, "gateway-child");
|
|
183
|
+
throw error;
|
|
184
|
+
});
|
|
156
185
|
process.stdout.write(result.stdout);
|
|
157
186
|
process.stderr.write(result.stderr);
|
|
158
187
|
process.exitCode = result.code;
|
|
159
188
|
}
|
|
160
189
|
|
|
161
190
|
if (process.argv[1] && resolve(process.argv[1]) === ENTRY)
|
|
162
|
-
main().catch(
|
|
163
|
-
process.stderr.write(
|
|
191
|
+
main().catch(error => {
|
|
192
|
+
process.stderr.write(`${provenanceFailure(error)}\n`);
|
|
164
193
|
process.exitCode = 1;
|
|
165
194
|
});
|
|
@@ -29,6 +29,73 @@ const stages = new Set([
|
|
|
29
29
|
"gate-output",
|
|
30
30
|
]);
|
|
31
31
|
const diagnostics = new WeakMap();
|
|
32
|
+
const capturedErrors = new WeakMap();
|
|
33
|
+
const provenancePhases = new Set([
|
|
34
|
+
"local-proof",
|
|
35
|
+
"configuration",
|
|
36
|
+
"verifier",
|
|
37
|
+
"canonical-context",
|
|
38
|
+
"recovery-local",
|
|
39
|
+
"historical-provider",
|
|
40
|
+
"historical-signature",
|
|
41
|
+
"recovery-signature",
|
|
42
|
+
"recovery-provider",
|
|
43
|
+
"assignable",
|
|
44
|
+
"ordinary-signature",
|
|
45
|
+
"ordinary-provider",
|
|
46
|
+
"final-proof",
|
|
47
|
+
"gateway-context",
|
|
48
|
+
"gateway-entry",
|
|
49
|
+
"gateway-input",
|
|
50
|
+
"gateway-child",
|
|
51
|
+
]);
|
|
52
|
+
const provenancePrefix =
|
|
53
|
+
"Invalid automation provenance: required proof, policy or provider evidence failed";
|
|
54
|
+
|
|
55
|
+
/** A synchronous verifier keeps its original exception and innermost source-selected phase. */
|
|
56
|
+
export function withProvenancePhase(phase, operation) {
|
|
57
|
+
required(provenancePhases.has(phase), "invalid provenance diagnostic phase");
|
|
58
|
+
try {
|
|
59
|
+
return operation();
|
|
60
|
+
} catch (error) {
|
|
61
|
+
recordProvenancePhase(error, phase);
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Shared sync/async catches never inspect exception properties, including hostile getters. */
|
|
67
|
+
export function recordProvenancePhase(error, phase) {
|
|
68
|
+
required(provenancePhases.has(phase), "invalid provenance diagnostic phase");
|
|
69
|
+
if (
|
|
70
|
+
error !== null &&
|
|
71
|
+
(typeof error === "object" || typeof error === "function")
|
|
72
|
+
) {
|
|
73
|
+
const prior = diagnostics.get(error) ?? {};
|
|
74
|
+
diagnostics.set(error, { ...prior, provenance: prior.provenance ?? phase });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Canonical CLI and gateway failures expose one closed label, never provider or exception text. */
|
|
79
|
+
export function provenanceFailure(error) {
|
|
80
|
+
return `${provenancePrefix} (phase=${diagnostics.get(error)?.provenance ?? "unknown"})`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Only a complete fixed diagnostic from an observed native exit-one can add a phase, never authority. */
|
|
84
|
+
export function inheritProvenanceFailure(error) {
|
|
85
|
+
const facts = diagnostics.get(error);
|
|
86
|
+
const bytes = capturedErrors.get(error);
|
|
87
|
+
if (
|
|
88
|
+
facts?.native !== "exit" ||
|
|
89
|
+
facts.status !== 1 ||
|
|
90
|
+
!bytes ||
|
|
91
|
+
bytes.length > 256
|
|
92
|
+
)
|
|
93
|
+
return;
|
|
94
|
+
const text = bytes.toString("utf8");
|
|
95
|
+
for (const phase of provenancePhases)
|
|
96
|
+
if (text === `${provenancePrefix} (phase=${phase})\n`)
|
|
97
|
+
recordProvenancePhase(error, phase);
|
|
98
|
+
}
|
|
32
99
|
const signals = new Set([
|
|
33
100
|
"SIGTERM",
|
|
34
101
|
"SIGINT",
|
|
@@ -81,7 +148,16 @@ export async function withStage(stage, operation) {
|
|
|
81
148
|
}
|
|
82
149
|
|
|
83
150
|
/** Only native capture registers observed close facts; exception properties never establish provenance. */
|
|
84
|
-
export function recordNativeFailure(
|
|
151
|
+
export function recordNativeFailure(
|
|
152
|
+
error,
|
|
153
|
+
category,
|
|
154
|
+
status,
|
|
155
|
+
signal,
|
|
156
|
+
errno,
|
|
157
|
+
stderr
|
|
158
|
+
) {
|
|
159
|
+
if (Buffer.isBuffer(stderr) && stderr.length <= 256)
|
|
160
|
+
capturedErrors.set(error, Buffer.from(stderr));
|
|
85
161
|
diagnostics.set(error, {
|
|
86
162
|
...diagnostics.get(error),
|
|
87
163
|
native: categories.has(category) ? category : "unknown",
|
|
@@ -101,6 +177,7 @@ export function publicFailure(error) {
|
|
|
101
177
|
if (facts.signal) parts.push(`signal=${facts.signal}`);
|
|
102
178
|
if (facts.errno) parts.push(`errno=${facts.errno}`);
|
|
103
179
|
}
|
|
180
|
+
if (facts?.provenance) parts.push(`provenance=${facts.provenance}`);
|
|
104
181
|
return `npm updater failed: required policy, authorization, installation, gate or exact publication evidence was unavailable (${parts.join(" ")})`;
|
|
105
182
|
}
|
|
106
183
|
|
|
@@ -95,6 +95,29 @@ export function runProcess(
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/** Retain the original native error and captured bytes while registering only observed close facts. */
|
|
99
|
+
function observedFailure(error, observation) {
|
|
100
|
+
const {
|
|
101
|
+
chunks,
|
|
102
|
+
errors,
|
|
103
|
+
command,
|
|
104
|
+
code,
|
|
105
|
+
nativeCompleted,
|
|
106
|
+
category,
|
|
107
|
+
signal,
|
|
108
|
+
errno,
|
|
109
|
+
} = observation;
|
|
110
|
+
const stderr = Buffer.concat(errors);
|
|
111
|
+
recordNativeFailure(error, category, code, signal, errno, stderr);
|
|
112
|
+
return Object.assign(error, {
|
|
113
|
+
stdout: Buffer.concat(chunks),
|
|
114
|
+
stderr,
|
|
115
|
+
command,
|
|
116
|
+
code,
|
|
117
|
+
nativeCompleted,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
98
121
|
/** Native close, not cancellation delivery, completes an owned child. */
|
|
99
122
|
function captureProcess(
|
|
100
123
|
child,
|
|
@@ -145,20 +168,16 @@ function captureProcess(
|
|
|
145
168
|
if (failure || signal || !allowed.includes(code)) {
|
|
146
169
|
const error =
|
|
147
170
|
failure ?? new Error(`npm updater: child failed (${code ?? signal})`);
|
|
148
|
-
recordNativeFailure(
|
|
149
|
-
error,
|
|
150
|
-
category ?? (signal ? "signal" : "exit"),
|
|
151
|
-
code,
|
|
152
|
-
signal,
|
|
153
|
-
errno
|
|
154
|
-
);
|
|
155
171
|
reject(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
172
|
+
observedFailure(error, {
|
|
173
|
+
chunks,
|
|
174
|
+
errors,
|
|
159
175
|
command,
|
|
160
176
|
code,
|
|
161
177
|
nativeCompleted: !failure && !signal,
|
|
178
|
+
category: category ?? (signal ? "signal" : "exit"),
|
|
179
|
+
signal,
|
|
180
|
+
errno,
|
|
162
181
|
})
|
|
163
182
|
);
|
|
164
183
|
return;
|
|
@@ -7,6 +7,10 @@ import {
|
|
|
7
7
|
verifyRecoveryOrigin,
|
|
8
8
|
} from "./lib/github-attestation-recovery.mjs";
|
|
9
9
|
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
10
|
+
import {
|
|
11
|
+
withProvenancePhase,
|
|
12
|
+
provenanceFailure,
|
|
13
|
+
} from "./lib/npm-update-invariants.mjs";
|
|
10
14
|
import {
|
|
11
15
|
assertPinnedVerifier,
|
|
12
16
|
requireProof,
|
|
@@ -29,6 +33,8 @@ import {
|
|
|
29
33
|
} from "./lib/automation-provenance-local.mjs";
|
|
30
34
|
export { canonicalJson } from "./lib/automation-provenance-contract.mjs";
|
|
31
35
|
|
|
36
|
+
const LOCAL_PROOF = "local-proof";
|
|
37
|
+
|
|
32
38
|
/** A present but malformed reference is a failure, even with ordinary AI trailers. */
|
|
33
39
|
export function automationReference(message) {
|
|
34
40
|
const lines = message
|
|
@@ -88,14 +94,8 @@ function recoveryPermission(
|
|
|
88
94
|
);
|
|
89
95
|
}
|
|
90
96
|
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
const messageBytes = privateBytes(messageFile, 65_536, false);
|
|
94
|
-
const message = messageBytes.toString("utf8");
|
|
95
|
-
const reference = automationReference(message);
|
|
96
|
-
if (!reference) return false;
|
|
97
|
-
const { config, policy } = trustedConfiguration(git);
|
|
98
|
-
const { paths, optional, bytes, bundleBytes, descriptor } = proofSnapshot();
|
|
97
|
+
/** Keep unchanged local authorization together without obscuring the canonical phase. */
|
|
98
|
+
function verifyLocalProposal(descriptor, messageBytes, reference, policy) {
|
|
99
99
|
localDescriptor(descriptor, messageBytes, reference, policy);
|
|
100
100
|
npmProposal(descriptor, git);
|
|
101
101
|
requireProof(
|
|
@@ -110,30 +110,18 @@ export function verifyAutomationProvenance(messageFile) {
|
|
|
110
110
|
),
|
|
111
111
|
"deterministic proposal key differs"
|
|
112
112
|
);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
context,
|
|
126
|
-
config,
|
|
127
|
-
bytes,
|
|
128
|
-
paths,
|
|
129
|
-
recoveryBytes,
|
|
130
|
-
recoveryBundle,
|
|
131
|
-
messageBytes
|
|
132
|
-
);
|
|
133
|
-
} else {
|
|
134
|
-
verifyDescriptorAttestation(policy, descriptor, paths, sha256(bytes));
|
|
135
|
-
verifyCurrentProvider(policy, descriptor);
|
|
136
|
-
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Final rereads preserve the original mutation checks after all provider verification. */
|
|
116
|
+
function verifyFinalSnapshot(
|
|
117
|
+
paths,
|
|
118
|
+
bundleBytes,
|
|
119
|
+
bytes,
|
|
120
|
+
descriptor,
|
|
121
|
+
messageFile,
|
|
122
|
+
reference,
|
|
123
|
+
policy
|
|
124
|
+
) {
|
|
137
125
|
requireProof(
|
|
138
126
|
privateBytes(paths.bundle, 1_048_576).equals(bundleBytes),
|
|
139
127
|
"origin bundle changed during verification"
|
|
@@ -148,17 +136,78 @@ export function verifyAutomationProvenance(messageFile) {
|
|
|
148
136
|
reference,
|
|
149
137
|
policy
|
|
150
138
|
);
|
|
151
|
-
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Verify fixed private proof against the final message, tree, binding and provider. */
|
|
142
|
+
export function verifyAutomationProvenance(messageFile) {
|
|
143
|
+
const messageBytes = withProvenancePhase(LOCAL_PROOF, () =>
|
|
144
|
+
privateBytes(messageFile, 65_536, false)
|
|
145
|
+
);
|
|
146
|
+
const message = messageBytes.toString("utf8");
|
|
147
|
+
const reference = withProvenancePhase(LOCAL_PROOF, () =>
|
|
148
|
+
automationReference(message)
|
|
149
|
+
);
|
|
150
|
+
if (!reference) return false;
|
|
151
|
+
const { config, policy } = withProvenancePhase("configuration", () =>
|
|
152
|
+
trustedConfiguration(git)
|
|
153
|
+
);
|
|
154
|
+
const { paths, optional, bytes, bundleBytes, descriptor } =
|
|
155
|
+
withProvenancePhase(LOCAL_PROOF, proofSnapshot);
|
|
156
|
+
withProvenancePhase(LOCAL_PROOF, () =>
|
|
157
|
+
verifyLocalProposal(descriptor, messageBytes, reference, policy)
|
|
158
|
+
);
|
|
159
|
+
withProvenancePhase("verifier", () => assertPinnedVerifier(policy));
|
|
160
|
+
const context = withProvenancePhase("canonical-context", () =>
|
|
161
|
+
canonicalContext(message, config, policy, descriptor)
|
|
162
|
+
);
|
|
163
|
+
return withProvenancePhase("final-proof", () => {
|
|
164
|
+
const recoveryBytes = optional[0]
|
|
165
|
+
? privateBytes(paths.recovery, 65_536)
|
|
166
|
+
: null;
|
|
167
|
+
const recoveryBundle = optional[0]
|
|
168
|
+
? privateBytes(paths.recoveryBundle, 1_048_576)
|
|
169
|
+
: null;
|
|
170
|
+
if (recoveryBytes) {
|
|
171
|
+
withProvenancePhase("recovery-local", () =>
|
|
172
|
+
recoveryPermission(
|
|
173
|
+
policy,
|
|
174
|
+
descriptor,
|
|
175
|
+
context,
|
|
176
|
+
config,
|
|
177
|
+
bytes,
|
|
178
|
+
paths,
|
|
179
|
+
recoveryBytes,
|
|
180
|
+
recoveryBundle,
|
|
181
|
+
messageBytes
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
} else {
|
|
185
|
+
withProvenancePhase("ordinary-signature", () =>
|
|
186
|
+
verifyDescriptorAttestation(policy, descriptor, paths, sha256(bytes))
|
|
187
|
+
);
|
|
188
|
+
withProvenancePhase("ordinary-provider", () =>
|
|
189
|
+
verifyCurrentProvider(policy, descriptor)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
verifyFinalSnapshot(
|
|
193
|
+
paths,
|
|
194
|
+
bundleBytes,
|
|
195
|
+
bytes,
|
|
196
|
+
descriptor,
|
|
197
|
+
messageFile,
|
|
198
|
+
reference,
|
|
199
|
+
policy
|
|
200
|
+
);
|
|
201
|
+
return true;
|
|
202
|
+
});
|
|
152
203
|
}
|
|
153
204
|
|
|
154
205
|
if (invokedAsScript(import.meta.url)) {
|
|
155
206
|
try {
|
|
156
207
|
const verified = verifyAutomationProvenance(process.argv[2]);
|
|
157
208
|
process.exitCode = verified ? 0 : 10;
|
|
158
|
-
} catch {
|
|
159
|
-
console.error(
|
|
160
|
-
"Invalid automation provenance: required proof, policy or provider evidence failed"
|
|
161
|
-
);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(provenanceFailure(error));
|
|
162
211
|
process.exitCode = 1;
|
|
163
212
|
}
|
|
164
213
|
}
|
|
@@ -92,12 +92,13 @@
|
|
|
92
92
|
"children": []
|
|
93
93
|
},
|
|
94
94
|
"lib/github-attestation-recovery.mjs": {
|
|
95
|
-
"sha256": "
|
|
95
|
+
"sha256": "aa97fbc52f58aba1cc93e105a9ee7a32a0ae184259c10ab55e23589b593207f9",
|
|
96
96
|
"staticImports": [
|
|
97
97
|
"lib/automation-provenance-local.mjs",
|
|
98
98
|
"lib/bounded-spawn.mjs",
|
|
99
99
|
"lib/github-attestation-provider.mjs",
|
|
100
|
-
"lib/github-attestation-verifier.mjs"
|
|
100
|
+
"lib/github-attestation-verifier.mjs",
|
|
101
|
+
"lib/npm-update-invariants.mjs"
|
|
101
102
|
],
|
|
102
103
|
"dynamicImports": [],
|
|
103
104
|
"children": []
|
|
@@ -588,7 +589,7 @@
|
|
|
588
589
|
"children": []
|
|
589
590
|
},
|
|
590
591
|
"lib/npm-update-hosted-gate.mjs": {
|
|
591
|
-
"sha256": "
|
|
592
|
+
"sha256": "b9ced911733ab9c6afd06c9451bb9d9cc9363e0a6fd1059ef659aba0107065cc",
|
|
592
593
|
"staticImports": [
|
|
593
594
|
"lib/npm-update-bun.mjs",
|
|
594
595
|
"lib/npm-update-contract.mjs",
|
|
@@ -608,9 +609,10 @@
|
|
|
608
609
|
"children": []
|
|
609
610
|
},
|
|
610
611
|
"lib/npm-update-hosted-hook.mjs": {
|
|
611
|
-
"sha256": "
|
|
612
|
+
"sha256": "0e9cf3bf55b9b979d22553f01ad4d17c0a85058b2b87657bd3126b9034e9f8c1",
|
|
612
613
|
"staticImports": [
|
|
613
614
|
"lib/npm-update-contract.mjs",
|
|
615
|
+
"lib/npm-update-invariants.mjs",
|
|
614
616
|
"lib/npm-update-isolation.mjs",
|
|
615
617
|
"lib/npm-update-orchestrator.mjs",
|
|
616
618
|
"lib/npm-update-process.mjs",
|
|
@@ -642,7 +644,7 @@
|
|
|
642
644
|
]
|
|
643
645
|
},
|
|
644
646
|
"lib/npm-update-invariants.mjs": {
|
|
645
|
-
"sha256": "
|
|
647
|
+
"sha256": "e91f3f85bcce92eba8fb0a6a544a9ef3d60985828e6b8e5265e3d44e42bfe09b",
|
|
646
648
|
"staticImports": [],
|
|
647
649
|
"dynamicImports": [],
|
|
648
650
|
"children": []
|
|
@@ -681,7 +683,7 @@
|
|
|
681
683
|
"children": []
|
|
682
684
|
},
|
|
683
685
|
"lib/npm-update-native-process.mjs": {
|
|
684
|
-
"sha256": "
|
|
686
|
+
"sha256": "1dec7d3392289203a99db38db1501a6282d436e779c720250aaf8fbfdd18bd16",
|
|
685
687
|
"staticImports": [
|
|
686
688
|
"lib/npm-update-contract.mjs",
|
|
687
689
|
"lib/npm-update-invariants.mjs",
|
|
@@ -1124,13 +1126,14 @@
|
|
|
1124
1126
|
"children": []
|
|
1125
1127
|
},
|
|
1126
1128
|
"lisa-automation-provenance.mjs": {
|
|
1127
|
-
"sha256": "
|
|
1129
|
+
"sha256": "d3d2ad9769b47ac988ff2bb4514ad37901cf5de854cf536cf1acf38fab9f94a0",
|
|
1128
1130
|
"staticImports": [
|
|
1129
1131
|
"lib/automation-provenance-contract.mjs",
|
|
1130
1132
|
"lib/automation-provenance-local.mjs",
|
|
1131
1133
|
"lib/github-attestation-recovery.mjs",
|
|
1132
1134
|
"lib/github-attestation-verifier.mjs",
|
|
1133
|
-
"lib/invoked-as-script.mjs"
|
|
1135
|
+
"lib/invoked-as-script.mjs",
|
|
1136
|
+
"lib/npm-update-invariants.mjs"
|
|
1134
1137
|
],
|
|
1135
1138
|
"dynamicImports": [],
|
|
1136
1139
|
"children": []
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAi+DjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAo4DjC,CAAC"}
|