From eb8ed6b6873d7695dbe4544423e00df32d6be404 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:44:35 +0100 Subject: [PATCH] cosign(install): use sigstore module to verify signature Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- package.json | 2 + src/cosign/install.ts | 37 ++++++++----- yarn.lock | 124 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 149 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index f24e1f0..70557a9 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,8 @@ "@octokit/plugin-rest-endpoint-methods": "^10.4.1", "@sigstore/bundle": "^4.0.0", "@sigstore/sign": "^4.0.1", + "@sigstore/tuf": "^4.0.0", + "@sigstore/verify": "^3.0.0", "async-retry": "^1.3.3", "csv-parse": "^6.1.0", "gunzip-maybe": "^1.4.2", diff --git a/src/cosign/install.ts b/src/cosign/install.ts index c9134b7..4a512e2 100644 --- a/src/cosign/install.ts +++ b/src/cosign/install.ts @@ -19,6 +19,9 @@ import os from 'os'; import path from 'path'; import * as core from '@actions/core'; import * as tc from '@actions/tool-cache'; +import {bundleFromJSON, SerializedBundle} from '@sigstore/bundle'; +import * as tuf from '@sigstore/tuf'; +import {toSignedEntity, toTrustMaterial, Verifier} from '@sigstore/verify'; import * as semver from 'semver'; import * as util from 'util'; @@ -188,24 +191,32 @@ export class Install { } private async verifySignature(cosignBinPath: string, downloadURL: string): Promise { - const cosignBootstrapPath = path.join(Context.tmpDir(), `cosign-bootstrap${os.platform() == 'win32' ? '.exe' : ''}`); - fs.copyFileSync(cosignBinPath, cosignBootstrapPath); - fs.chmodSync(cosignBootstrapPath, '0755'); - const bundleURL = `${downloadURL}.sigstore.json`; core.info(`Downloading keyless verification bundle at ${bundleURL}`); const bundlePath = await tc.downloadTool(bundleURL, undefined, this.githubToken); core.debug(`Install.verifySignature bundlePath: ${bundlePath}`); - core.info(`Verifying cosign binary signature with keyless verification bundle`); - // prettier-ignore - await Exec.exec(cosignBootstrapPath, [ - 'verify-blob', - '--certificate-identity', 'keyless@projectsigstore.iam.gserviceaccount.com', - '--certificate-oidc-issuer', 'https://accounts.google.com', - '--bundle', bundlePath, - cosignBinPath - ]); + core.info(`Verifying keyless verification bundle signature`); + const parsedBundle = JSON.parse(fs.readFileSync(bundlePath, 'utf-8')) as SerializedBundle; + const bundle = bundleFromJSON(parsedBundle); + + core.info(`Fetching Sigstore TUF trusted root metadata`); + const trustedRoot = await tuf.getTrustedRoot(); + const trustMaterial = toTrustMaterial(trustedRoot); + + try { + core.info(`Verifying cosign binary signature`); + const signedEntity = toSignedEntity(bundle, fs.readFileSync(cosignBinPath)); + const verifier = new Verifier(trustMaterial); + const signer = verifier.verify(signedEntity, { + subjectAlternativeName: 'keyless@projectsigstore.iam.gserviceaccount.com', + extensions: {issuer: 'https://accounts.google.com'} + }); + core.debug(`Install.verifySignature signer: ${JSON.stringify(signer)}`); + core.info(`Cosign binary signature verified!`); + } catch (err) { + throw new Error(`Failed to verify cosign binary signature: ${err}`); + } } private filename(): string { diff --git a/yarn.lock b/yarn.lock index 678cddb..ccd32b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1185,6 +1185,8 @@ __metadata: "@sigstore/bundle": "npm:^4.0.0" "@sigstore/rekor-types": "npm:^3.0.0" "@sigstore/sign": "npm:^4.0.1" + "@sigstore/tuf": "npm:^4.0.0" + "@sigstore/verify": "npm:^3.0.0" "@types/gunzip-maybe": "npm:^1.4.3" "@types/he": "npm:^1.2.3" "@types/js-yaml": "npm:^4.0.9" @@ -2273,6 +2275,27 @@ __metadata: languageName: node linkType: hard +"@sigstore/tuf@npm:^4.0.0": + version: 4.0.0 + resolution: "@sigstore/tuf@npm:4.0.0" + dependencies: + "@sigstore/protobuf-specs": "npm:^0.5.0" + tuf-js: "npm:^4.0.0" + checksum: 10/8f47a0bc814a8ee1ef59bc90eb7954e0bb33734a913c77c04bdbf08fce2622d406feb0b243191154453a046224fcc512e916c1c919563fab902070b66837ad5e + languageName: node + linkType: hard + +"@sigstore/verify@npm:^3.0.0": + version: 3.0.0 + resolution: "@sigstore/verify@npm:3.0.0" + dependencies: + "@sigstore/bundle": "npm:^4.0.0" + "@sigstore/core": "npm:^3.0.0" + "@sigstore/protobuf-specs": "npm:^0.5.0" + checksum: 10/c5b4891f42586a4c68fb22f127f19dd16b0bda0388ae8a40727cedd2443919006df3ec1ac4d6c3bd2786cff4c3f8d987135e87979262790e718bcc53e8a3a6c1 + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.34.0": version: 0.34.41 resolution: "@sinclair/typebox@npm:0.34.41" @@ -2333,6 +2356,23 @@ __metadata: languageName: node linkType: hard +"@tufjs/canonical-json@npm:2.0.0": + version: 2.0.0 + resolution: "@tufjs/canonical-json@npm:2.0.0" + checksum: 10/cc719a1d0d0ae1aa1ba551a82c87dcbefac088e433c03a3d8a1d547ea721350e47dab4ab5b0fca40d5c7ab1f4882e72edc39c9eae15bf47c45c43bcb6ee39f4f + languageName: node + linkType: hard + +"@tufjs/models@npm:4.0.0": + version: 4.0.0 + resolution: "@tufjs/models@npm:4.0.0" + dependencies: + "@tufjs/canonical-json": "npm:2.0.0" + minimatch: "npm:^9.0.5" + checksum: 10/1b8d119b4144018d92237aa0dfcf4ac85ee609dd0062d15817736cfd0d0d594761e9179dd7b580894a6e7f67dd06d4421f16534756b66441c8838e8644e77632 + languageName: node + linkType: hard + "@tybys/wasm-util@npm:^0.10.0": version: 0.10.1 resolution: "@tybys/wasm-util@npm:0.10.1" @@ -3947,6 +3987,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10/9ada3434ea2993800bd9a1e320bd4aa7af69659fb51cca685d390949434bc0a8873c21ed7c9b852af6f2455a55c6d050aa3937d52b3c69f796dab666f762acad + languageName: node + linkType: hard + "dedent@npm:^1.6.0": version: 1.7.0 resolution: "dedent@npm:1.7.0" @@ -7062,6 +7114,25 @@ __metadata: languageName: node linkType: hard +"make-fetch-happen@npm:^15.0.0": + version: 15.0.3 + resolution: "make-fetch-happen@npm:15.0.3" + dependencies: + "@npmcli/agent": "npm:^4.0.0" + cacache: "npm:^20.0.1" + http-cache-semantics: "npm:^4.1.1" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^5.0.0" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + negotiator: "npm:^1.0.0" + proc-log: "npm:^6.0.0" + promise-retry: "npm:^2.0.1" + ssri: "npm:^13.0.0" + checksum: 10/78da4fc1df83cb596e2bae25aa0653b8a9c6cbdd6674a104894e03be3acfcd08c70b78f06ef6407fbd6b173f6a60672480d78641e693d05eb71c09c13ee35278 + languageName: node + linkType: hard + "make-fetch-happen@npm:^15.0.2": version: 15.0.2 resolution: "make-fetch-happen@npm:15.0.2" @@ -7175,6 +7246,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.5": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348 + languageName: node + linkType: hard + "minimist@npm:^1.2.0, minimist@npm:^1.2.6": version: 1.2.7 resolution: "minimist@npm:1.2.7" @@ -7237,6 +7317,21 @@ __metadata: languageName: node linkType: hard +"minipass-fetch@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass-fetch@npm:5.0.0" + dependencies: + encoding: "npm:^0.1.13" + minipass: "npm:^7.0.3" + minipass-sized: "npm:^1.0.3" + minizlib: "npm:^3.0.1" + dependenciesMeta: + encoding: + optional: true + checksum: 10/4fb7dca630a64e6970a8211dade505bfe260d0b8d60beb348dcdfb95fe35ef91d977b29963929c9017ae0805686aa3f413107dc6bc5deac9b9e26b0b41c3b86c + languageName: node + linkType: hard + "minipass-flush@npm:^1.0.5": version: 1.0.5 resolution: "minipass-flush@npm:1.0.5" @@ -7347,7 +7442,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -7879,6 +7974,13 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^6.0.0": + version: 6.1.0 + resolution: "proc-log@npm:6.1.0" + checksum: 10/9033f30f168ed5a0991b773d0c50ff88384c4738e9a0a67d341de36bf7293771eed648ab6a0562f62276da12fde91f3bbfc75ffff6e71ad49aafd74fc646be66 + languageName: node + linkType: hard + "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -8552,6 +8654,15 @@ __metadata: languageName: node linkType: hard +"ssri@npm:^13.0.0": + version: 13.0.0 + resolution: "ssri@npm:13.0.0" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10/fd59bfedf0659c1b83f6e15459162da021f08ec0f5834dd9163296f8b77ee82f9656aa1d415c3d3848484293e0e6aefdd482e863e52ddb53d520bb73da1eeec1 + languageName: node + linkType: hard + "ssri@npm:^9.0.0": version: 9.0.1 resolution: "ssri@npm:9.0.1" @@ -9067,6 +9178,17 @@ __metadata: languageName: node linkType: hard +"tuf-js@npm:^4.0.0": + version: 4.0.0 + resolution: "tuf-js@npm:4.0.0" + dependencies: + "@tufjs/models": "npm:4.0.0" + debug: "npm:^4.4.1" + make-fetch-happen: "npm:^15.0.0" + checksum: 10/7de216e39578f7abd449b2eaed7977b9e99f3b66bcc7ff24f4f4a4a4bcca032a1c180e2a3fd20019ed820d898010fcd9f2654446c87dbf93a9b13f163bb99422 + languageName: node + linkType: hard + "tunnel@npm:^0.0.6": version: 0.0.6 resolution: "tunnel@npm:0.0.6"