@openthink/stamp 2.0.2 → 2.1.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/dist/{chunk-4PFD2DSY.js → chunk-MJULVH4B.js} +2 -2
- package/dist/{chunk-4PFD2DSY.js.map → chunk-MJULVH4B.js.map} +1 -1
- package/dist/hooks/post-receive.cjs +10 -6
- package/dist/hooks/post-receive.cjs.map +1 -1
- package/dist/hooks/pre-receive.cjs +25 -48
- package/dist/hooks/pre-receive.cjs.map +1 -1
- package/dist/index.js +111 -72
- package/dist/index.js.map +1 -1
- package/dist/server/stamp-review.cjs +8399 -7460
- package/dist/server/stamp-review.cjs.map +1 -1
- package/dist/{ui-P5DRAT3P.js → ui-67BDQPER.js} +2 -2
- package/package.json +1 -1
- /package/dist/{ui-P5DRAT3P.js.map → ui-67BDQPER.js.map} +0 -0
|
@@ -7341,6 +7341,7 @@ __export(pre_receive_exports, {
|
|
|
7341
7341
|
verifyV4Approvals: () => verifyV4Approvals,
|
|
7342
7342
|
verifyV4Checks: () => verifyV4Checks,
|
|
7343
7343
|
verifyV4DiffHash: () => verifyV4DiffHash,
|
|
7344
|
+
verifyV4ManifestSnapshot: () => verifyV4ManifestSnapshot,
|
|
7344
7345
|
verifyV4MergeStructure: () => verifyV4MergeStructure,
|
|
7345
7346
|
verifyV4OuterSignature: () => verifyV4OuterSignature,
|
|
7346
7347
|
verifyV4SignerTrust: () => verifyV4SignerTrust,
|
|
@@ -7380,7 +7381,7 @@ function parseCommitAttestation(commitMessage) {
|
|
|
7380
7381
|
}
|
|
7381
7382
|
|
|
7382
7383
|
// src/lib/attestationV4.ts
|
|
7383
|
-
var MIN_ACCEPTED_V4_SCHEMA_VERSION =
|
|
7384
|
+
var MIN_ACCEPTED_V4_SCHEMA_VERSION = 5;
|
|
7384
7385
|
var MAX_V4_ENVELOPE_BYTES = 64 * 1024;
|
|
7385
7386
|
function sortKeysDeep(value) {
|
|
7386
7387
|
if (value === null || typeof value !== "object") return value;
|
|
@@ -7663,6 +7664,11 @@ var COMMIT_PHASES_V4 = [
|
|
|
7663
7664
|
{ name: "verifyV4TargetBranch", fn: verifyV4TargetBranch },
|
|
7664
7665
|
{ name: "verifyV4SignerTrust", fn: verifyV4SignerTrust },
|
|
7665
7666
|
{ name: "verifyV4OuterSignature", fn: verifyV4OuterSignature },
|
|
7667
|
+
// AGT-370: envelope-level manifest snapshot binding (lifted from
|
|
7668
|
+
// the per-approval slot in v4). Runs once before the per-approval
|
|
7669
|
+
// loop in verifyV4ApprovalSignatures — a single check replaces the
|
|
7670
|
+
// N-checks per envelope the v4 verifier did.
|
|
7671
|
+
{ name: "verifyV4ManifestSnapshot", fn: verifyV4ManifestSnapshot },
|
|
7666
7672
|
{ name: "verifyV4Approvals", fn: verifyV4Approvals },
|
|
7667
7673
|
{ name: "verifyV4DiffHash", fn: verifyV4DiffHash },
|
|
7668
7674
|
{ name: "verifyV4ApprovalSignatures", fn: verifyV4ApprovalSignatures },
|
|
@@ -7795,10 +7801,19 @@ function verifyV4DiffHash(input) {
|
|
|
7795
7801
|
}
|
|
7796
7802
|
return { ok: true };
|
|
7797
7803
|
}
|
|
7804
|
+
function verifyV4ManifestSnapshot(input) {
|
|
7805
|
+
const { sha, payload, manifest } = input;
|
|
7806
|
+
const computed = snapshotSha256(manifest);
|
|
7807
|
+
if (payload.manifest_snapshot_sha256 !== computed) {
|
|
7808
|
+
return {
|
|
7809
|
+
ok: false,
|
|
7810
|
+
reason: `commit ${sha.slice(0, 8)}: v4 manifest_snapshot_sha256 (${payload.manifest_snapshot_sha256.slice(0, 16)}\u2026) does not match the manifest at base ${payload.base_sha.slice(0, 8)} (${computed.slice(0, 16)}\u2026). The envelope was signed against a different snapshot of the trust set than the one committed at the merge base. Re-run \`stamp merge\` (or \`stamp attest\`) so the outer signature binds to the current manifest.`
|
|
7811
|
+
};
|
|
7812
|
+
}
|
|
7813
|
+
return { ok: true };
|
|
7814
|
+
}
|
|
7798
7815
|
function verifyV4ApprovalSignatures(input) {
|
|
7799
7816
|
const { sha, payload, manifest, pubkeyByFingerprint } = input;
|
|
7800
|
-
const manifestSnapshot = snapshotSha256(manifest);
|
|
7801
|
-
const reviewerDefs = readReviewerDefsAtRef(payload.base_sha);
|
|
7802
7817
|
for (const entry of payload.approvals) {
|
|
7803
7818
|
const a = entry.approval;
|
|
7804
7819
|
const reviewerLabel = `"${a.reviewer}"`;
|
|
@@ -7820,12 +7835,6 @@ function verifyV4ApprovalSignatures(input) {
|
|
|
7820
7835
|
reason: `commit ${sha.slice(0, 8)}: v4 approval ${reviewerLabel}: server_attestation.server_key_id (${entry.server_attestation.server_key_id}) does not match inner approval.server_key_id (${a.server_key_id}). The inner signed payload is authoritative; one of the two was tampered with after signing.`
|
|
7821
7836
|
};
|
|
7822
7837
|
}
|
|
7823
|
-
if (a.trusted_keys_snapshot_sha256 !== manifestSnapshot) {
|
|
7824
|
-
return {
|
|
7825
|
-
ok: false,
|
|
7826
|
-
reason: `commit ${sha.slice(0, 8)}: v4 approval ${reviewerLabel}: trusted_keys_snapshot_sha256 (${a.trusted_keys_snapshot_sha256.slice(0, 16)}\u2026) does not match the manifest at base ${payload.base_sha.slice(0, 8)} (${manifestSnapshot.slice(0, 16)}\u2026). The server signed against a different snapshot of the trust set than the one committed at the merge base.`
|
|
7827
|
-
};
|
|
7828
|
-
}
|
|
7829
7838
|
const caps = resolveCapability(manifest, a.server_key_id);
|
|
7830
7839
|
if (caps === null) {
|
|
7831
7840
|
return {
|
|
@@ -7865,29 +7874,6 @@ function verifyV4ApprovalSignatures(input) {
|
|
|
7865
7874
|
reason: `commit ${sha.slice(0, 8)}: v4 approval ${reviewerLabel}: server signature does not verify against ${a.server_key_id} over canonical approval bytes`
|
|
7866
7875
|
};
|
|
7867
7876
|
}
|
|
7868
|
-
const def = reviewerDefs[a.reviewer];
|
|
7869
|
-
if (!def) {
|
|
7870
|
-
return {
|
|
7871
|
-
ok: false,
|
|
7872
|
-
reason: `commit ${sha.slice(0, 8)}: v4 approval ${reviewerLabel}: reviewer is not defined in .stamp/config.yml at base ${payload.base_sha.slice(0, 8)}`
|
|
7873
|
-
};
|
|
7874
|
-
}
|
|
7875
|
-
let promptText;
|
|
7876
|
-
try {
|
|
7877
|
-
promptText = run(["show", `${payload.base_sha}:${def.prompt}`]);
|
|
7878
|
-
} catch {
|
|
7879
|
-
return {
|
|
7880
|
-
ok: false,
|
|
7881
|
-
reason: `commit ${sha.slice(0, 8)}: v4 approval ${reviewerLabel}: prompt "${def.prompt}" is unreadable at base ${payload.base_sha.slice(0, 8)}`
|
|
7882
|
-
};
|
|
7883
|
-
}
|
|
7884
|
-
const recomputedPromptSha = hashPromptBytes(Buffer.from(promptText, "utf8"));
|
|
7885
|
-
if (recomputedPromptSha !== a.prompt_sha256) {
|
|
7886
|
-
return {
|
|
7887
|
-
ok: false,
|
|
7888
|
-
reason: `commit ${sha.slice(0, 8)}: v4 approval ${reviewerLabel}: prompt_sha256 mismatch \u2014 server signed ${a.prompt_sha256.slice(0, 12)}\u2026 but prompt file at base hashes to ${recomputedPromptSha.slice(0, 12)}\u2026. The reviewer prompt the server reviewed differs from the one in the merge-base tree.`
|
|
7889
|
-
};
|
|
7890
|
-
}
|
|
7891
7877
|
}
|
|
7892
7878
|
return { ok: true };
|
|
7893
7879
|
}
|
|
@@ -8053,22 +8039,6 @@ function readPubkeyMapAt(ref) {
|
|
|
8053
8039
|
}
|
|
8054
8040
|
return buildPubkeyMap(names, (relPath) => run(["show", `${ref}:${relPath}`]));
|
|
8055
8041
|
}
|
|
8056
|
-
function readReviewerDefsAtRef(ref) {
|
|
8057
|
-
let yaml;
|
|
8058
|
-
try {
|
|
8059
|
-
yaml = run(["show", `${ref}:.stamp/config.yml`]);
|
|
8060
|
-
} catch {
|
|
8061
|
-
return {};
|
|
8062
|
-
}
|
|
8063
|
-
const defs = readReviewersFromYaml(yaml);
|
|
8064
|
-
const out = {};
|
|
8065
|
-
for (const [name, def] of Object.entries(defs)) {
|
|
8066
|
-
if (def && typeof def.prompt === "string") {
|
|
8067
|
-
out[name] = { prompt: def.prompt };
|
|
8068
|
-
}
|
|
8069
|
-
}
|
|
8070
|
-
return out;
|
|
8071
|
-
}
|
|
8072
8042
|
function readChangedFilesAtRef(baseSha, headSha) {
|
|
8073
8043
|
let out;
|
|
8074
8044
|
try {
|
|
@@ -8457,6 +8427,12 @@ function verifyReviewerHashesAtMergeBase(input) {
|
|
|
8457
8427
|
reason: `${prefix} reviewer "${approval.reviewer}" not defined in .stamp/config.yml at merge-base`
|
|
8458
8428
|
};
|
|
8459
8429
|
}
|
|
8430
|
+
if (def.prompt === void 0) {
|
|
8431
|
+
return {
|
|
8432
|
+
ok: false,
|
|
8433
|
+
reason: `${prefix} reviewer "${approval.reviewer}" has no \`prompt:\` in .stamp/config.yml at merge-base; v3 attestation references prompt_sha256 but the producer flow for server-bundled prompts is v4 (server-attested). The attestation envelope and the config shape are inconsistent.`
|
|
8434
|
+
};
|
|
8435
|
+
}
|
|
8460
8436
|
let promptBytes;
|
|
8461
8437
|
try {
|
|
8462
8438
|
promptBytes = run2(["show", `${baseSha}:${def.prompt}`]);
|
|
@@ -8715,6 +8691,7 @@ if (isMainModule()) {
|
|
|
8715
8691
|
verifyV4Approvals,
|
|
8716
8692
|
verifyV4Checks,
|
|
8717
8693
|
verifyV4DiffHash,
|
|
8694
|
+
verifyV4ManifestSnapshot,
|
|
8718
8695
|
verifyV4MergeStructure,
|
|
8719
8696
|
verifyV4OuterSignature,
|
|
8720
8697
|
verifyV4SignerTrust,
|