mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 22:17:00 +00:00
fix(release): complete protected publish tooling
This commit is contained in:
@@ -201,6 +201,12 @@ type NpmProvenanceStatement = {
|
||||
repository?: string;
|
||||
};
|
||||
};
|
||||
resolvedDependencies?: Array<{
|
||||
digest?: {
|
||||
gitCommit?: string;
|
||||
};
|
||||
uri?: string;
|
||||
}>;
|
||||
};
|
||||
runDetails?: {
|
||||
builder?: {
|
||||
@@ -277,6 +283,10 @@ export function verifyNpmRegistrySignatures(params: {
|
||||
function resolveNpmProvenanceVerificationPolicy(
|
||||
statement: NpmProvenanceStatement,
|
||||
version: string,
|
||||
expectedWorkflow?: {
|
||||
ref?: string;
|
||||
sha?: string;
|
||||
},
|
||||
): NpmProvenanceVerificationPolicy {
|
||||
const parsedVersion = parseReleaseVersion(version);
|
||||
if (parsedVersion === null) {
|
||||
@@ -285,9 +295,37 @@ function resolveNpmProvenanceVerificationPolicy(
|
||||
const workflow = statement.predicate?.buildDefinition?.externalParameters?.workflow;
|
||||
const workflowRef = workflow?.ref;
|
||||
const expectedReleaseRef = `refs/heads/release/${parsedVersion.baseVersion}`;
|
||||
const protectedReleasePublishMatch =
|
||||
/^refs\/tags\/release-publish\/([a-f0-9]{12})-[1-9][0-9]*$/u.exec(workflowRef ?? "");
|
||||
let protectedReleasePublishTrusted = false;
|
||||
if (protectedReleasePublishMatch) {
|
||||
const expectedRef = expectedWorkflow?.ref;
|
||||
const expectedSha = expectedWorkflow?.sha;
|
||||
if (
|
||||
expectedRef !== workflowRef ||
|
||||
!/^[a-f0-9]{40}$/u.test(expectedSha ?? "") ||
|
||||
expectedSha?.slice(0, 12) !== protectedReleasePublishMatch[1]
|
||||
) {
|
||||
throw new Error(
|
||||
"npm provenance SHA-pinned release-publish ref does not match the approved workflow ref and SHA.",
|
||||
);
|
||||
}
|
||||
const expectedDependencyUri = `git+${NPM_PROVENANCE_REPOSITORY}@${workflowRef}`;
|
||||
protectedReleasePublishTrusted =
|
||||
statement.predicate?.buildDefinition?.resolvedDependencies?.some(
|
||||
(dependency) =>
|
||||
dependency.uri === expectedDependencyUri && dependency.digest?.gitCommit === expectedSha,
|
||||
) === true;
|
||||
if (!protectedReleasePublishTrusted) {
|
||||
throw new Error(
|
||||
"npm provenance does not bind the approved SHA-pinned release-publish ref to its workflow revision.",
|
||||
);
|
||||
}
|
||||
}
|
||||
const isTrustedRef =
|
||||
workflowRef === "refs/heads/main" ||
|
||||
workflowRef === expectedReleaseRef ||
|
||||
protectedReleasePublishTrusted ||
|
||||
(parsedVersion.channel === "alpha" &&
|
||||
/^refs\/heads\/tideclaw\/alpha\/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{4}Z$/u.test(
|
||||
workflowRef ?? "",
|
||||
@@ -320,6 +358,8 @@ async function verifySigstoreNpmProvenanceBundle(
|
||||
|
||||
export async function verifyNpmProvenanceAttestation(params: {
|
||||
attestations: NpmRegistryAttestation[];
|
||||
expectedWorkflowRef?: string;
|
||||
expectedWorkflowSha?: string;
|
||||
integrity: string;
|
||||
packageName: string;
|
||||
verifyBundle?: VerifyNpmProvenanceBundle;
|
||||
@@ -353,7 +393,10 @@ export async function verifyNpmProvenanceAttestation(params: {
|
||||
) {
|
||||
let policy: NpmProvenanceVerificationPolicy;
|
||||
try {
|
||||
policy = resolveNpmProvenanceVerificationPolicy(statement, params.version);
|
||||
policy = resolveNpmProvenanceVerificationPolicy(statement, params.version, {
|
||||
ref: params.expectedWorkflowRef,
|
||||
sha: params.expectedWorkflowSha,
|
||||
});
|
||||
} catch (error) {
|
||||
policyError = error;
|
||||
continue;
|
||||
@@ -1156,6 +1199,8 @@ async function verifyPublishedRegistryProvenanceOnce(version: string): Promise<v
|
||||
version,
|
||||
integrity,
|
||||
attestations,
|
||||
expectedWorkflowRef: process.env.OPENCLAW_NPM_EXPECTED_WORKFLOW_REF,
|
||||
expectedWorkflowSha: process.env.OPENCLAW_NPM_EXPECTED_WORKFLOW_SHA,
|
||||
});
|
||||
console.log(
|
||||
`openclaw-npm-postpublish-verify: registry signature and provenance attestation verified (${version})`,
|
||||
|
||||
Reference in New Issue
Block a user