mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(release): trust extended-stable npm provenance (#112301)
This commit is contained in:
@@ -295,6 +295,12 @@ function resolveNpmProvenanceVerificationPolicy(
|
||||
const workflow = statement.predicate?.buildDefinition?.externalParameters?.workflow;
|
||||
const workflowRef = workflow?.ref;
|
||||
const expectedReleaseRef = `refs/heads/release/${parsedVersion.baseVersion}`;
|
||||
// A month's final patch >=33 releases stay on its canonical .33 maintenance branch.
|
||||
const isExpectedExtendedStableRef =
|
||||
parsedVersion.channel === "stable" &&
|
||||
parsedVersion.correctionNumber === undefined &&
|
||||
parsedVersion.patch >= 33 &&
|
||||
workflowRef === `refs/heads/extended-stable/${parsedVersion.year}.${parsedVersion.month}.33`;
|
||||
const protectedReleasePublishMatch =
|
||||
/^refs\/tags\/release-publish\/([a-f0-9]{12})-[1-9][0-9]*$/u.exec(workflowRef ?? "");
|
||||
let protectedReleasePublishTrusted = false;
|
||||
@@ -325,6 +331,7 @@ function resolveNpmProvenanceVerificationPolicy(
|
||||
const isTrustedRef =
|
||||
workflowRef === "refs/heads/main" ||
|
||||
workflowRef === expectedReleaseRef ||
|
||||
isExpectedExtendedStableRef ||
|
||||
protectedReleasePublishTrusted ||
|
||||
(parsedVersion.channel === "alpha" &&
|
||||
/^refs\/heads\/tideclaw\/alpha\/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{4}Z$/u.test(
|
||||
|
||||
@@ -98,10 +98,10 @@ describe("npm registry provenance verification", () => {
|
||||
const packageName = "openclaw";
|
||||
const version = "2026.3.23";
|
||||
const integrity = `sha512-${Buffer.from("registry integrity", "utf8").toString("base64")}`;
|
||||
const provenancePayload = {
|
||||
const buildProvenancePayload = (releaseVersion: string, workflowRef: string) => ({
|
||||
subject: [
|
||||
{
|
||||
name: `pkg:npm/${packageName}@${version}`,
|
||||
name: `pkg:npm/${packageName}@${releaseVersion}`,
|
||||
digest: {
|
||||
sha512: Buffer.from(integrity.slice("sha512-".length), "base64").toString("hex"),
|
||||
},
|
||||
@@ -113,7 +113,7 @@ describe("npm registry provenance verification", () => {
|
||||
workflow: {
|
||||
repository: "https://github.com/openclaw/openclaw",
|
||||
path: ".github/workflows/openclaw-npm-release.yml",
|
||||
ref: "refs/heads/release/2026.3.23",
|
||||
ref: workflowRef,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -123,7 +123,8 @@ describe("npm registry provenance verification", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
const provenancePayload = buildProvenancePayload(version, "refs/heads/release/2026.3.23");
|
||||
|
||||
it("fetches npm registry JSON with bounded response handling", async () => {
|
||||
const fetchImpl = vi.fn(async (_url: string, init?: RequestInit) => {
|
||||
@@ -362,6 +363,87 @@ describe("npm registry provenance verification", () => {
|
||||
).rejects.toThrow("does not match");
|
||||
});
|
||||
|
||||
it.each([
|
||||
["2026.6.33", "refs/heads/extended-stable/2026.6.33"],
|
||||
["2026.6.34", "refs/heads/extended-stable/2026.6.33"],
|
||||
])(
|
||||
"trusts canonical extended-stable provenance for %s",
|
||||
async (extendedStableVersion, workflowRef) => {
|
||||
let verificationPolicy:
|
||||
| {
|
||||
certificateIdentityURI: string;
|
||||
certificateIssuer: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
await expect(
|
||||
verifyNpmProvenanceAttestation({
|
||||
packageName,
|
||||
version: extendedStableVersion,
|
||||
integrity,
|
||||
attestations: [
|
||||
{
|
||||
predicateType: "https://slsa.dev/provenance/v1",
|
||||
bundle: {
|
||||
dsseEnvelope: {
|
||||
payload: Buffer.from(
|
||||
JSON.stringify(buildProvenancePayload(extendedStableVersion, workflowRef)),
|
||||
"utf8",
|
||||
).toString("base64"),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
verifyBundle: async (_bundle, policy) => {
|
||||
verificationPolicy = policy;
|
||||
},
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
expect(verificationPolicy).toEqual({
|
||||
certificateIssuer: "https://token.actions.githubusercontent.com",
|
||||
certificateIdentityURI: `https://github.com/openclaw/openclaw/.github/workflows/openclaw-npm-release.yml@${workflowRef}`,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
["later patch on a noncanonical branch", "2026.6.34", "refs/heads/extended-stable/2026.6.34"],
|
||||
["patch below 33", "2026.6.32", "refs/heads/extended-stable/2026.6.33"],
|
||||
["correction suffix", "2026.6.33-1", "refs/heads/extended-stable/2026.6.33"],
|
||||
])(
|
||||
"rejects extended-stable provenance for %s",
|
||||
async (_label, extendedStableVersion, workflowRef) => {
|
||||
let verificationCalls = 0;
|
||||
|
||||
await expect(
|
||||
verifyNpmProvenanceAttestation({
|
||||
packageName,
|
||||
version: extendedStableVersion,
|
||||
integrity,
|
||||
attestations: [
|
||||
{
|
||||
predicateType: "https://slsa.dev/provenance/v1",
|
||||
bundle: {
|
||||
dsseEnvelope: {
|
||||
payload: Buffer.from(
|
||||
JSON.stringify(buildProvenancePayload(extendedStableVersion, workflowRef)),
|
||||
"utf8",
|
||||
).toString("base64"),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
verifyBundle: async () => {
|
||||
verificationCalls += 1;
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
`does not bind ${extendedStableVersion} to the trusted OpenClaw GitHub release workflow`,
|
||||
);
|
||||
expect(verificationCalls).toBe(0);
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects matching provenance from an untrusted source before Sigstore verification", async () => {
|
||||
let verificationCalls = 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user