From 1c0dc52a0e50c55fd576dcf0202d07de2ba50393 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Thu, 30 Oct 2025 10:35:31 +0100 Subject: [PATCH] sigstore: always set TSA server endpoint to provide trusted timestamping Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- __tests__/sigstore/sigstore.test.itg.ts | 5 ++++- src/sigstore/sigstore.ts | 28 +++++++++++++++++-------- src/types/sigstore/sigstore.ts | 20 ++++++++++++++++++ 3 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 src/types/sigstore/sigstore.ts diff --git a/__tests__/sigstore/sigstore.test.itg.ts b/__tests__/sigstore/sigstore.test.itg.ts index 28a4ba7..f7a2103 100644 --- a/__tests__/sigstore/sigstore.test.itg.ts +++ b/__tests__/sigstore/sigstore.test.itg.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import {describe, expect, it} from '@jest/globals'; +import {describe, expect, jest, it} from '@jest/globals'; import fs from 'fs'; import * as path from 'path'; @@ -24,6 +24,9 @@ const fixturesDir = path.join(__dirname, '..', '.fixtures'); const maybe = process.env.GITHUB_ACTIONS && process.env.GITHUB_ACTIONS === 'true' && process.env.ACTIONS_ID_TOKEN_REQUEST_URL && process.env.ImageOS && process.env.ImageOS.startsWith('ubuntu') ? describe : describe.skip; +// needs current GitHub repo info +jest.unmock('@actions/github'); + maybe('signProvenanceBlobs', () => { it('single platform', async () => { const sigstore = new Sigstore(); diff --git a/src/sigstore/sigstore.ts b/src/sigstore/sigstore.ts index 8d36d1b..129f78d 100644 --- a/src/sigstore/sigstore.ts +++ b/src/sigstore/sigstore.ts @@ -18,18 +18,22 @@ import {X509Certificate} from 'crypto'; import fs from 'fs'; import path from 'path'; -import {signingEndpoints, SigstoreInstance} from '@actions/attest/lib/endpoints'; +import {Endpoints} from '@actions/attest/lib/endpoints'; import * as core from '@actions/core'; import {signPayload} from '@actions/attest/lib/sign'; import {bundleToJSON} from '@sigstore/bundle'; import {Attestation} from '@actions/attest'; import {Bundle} from '@sigstore/sign'; -import {Subject} from '../types/intoto/intoto'; +import {GitHub} from '../github'; + +import {MEDIATYPE_PAYLOAD as intotoMediatypePayload, Subject} from '../types/intoto/intoto'; +import {FULCIO_URL, REKOR_URL, SEARCH_URL, TSASERVER_URL} from '../types/sigstore/sigstore'; export interface SignProvenanceBlobsOpts { localExportDir: string; name?: string; + noTransparencyLog?: boolean; } export interface SignProvenanceBlobsResult extends Attestation { @@ -38,9 +42,6 @@ export interface SignProvenanceBlobsResult extends Attestation { } export class Sigstore { - private intotoPayloadType = 'application/vnd.in-toto+json'; - private searchSigstoreURL = 'https://search.sigstore.dev'; - public async signProvenanceBlobs(opts: SignProvenanceBlobsOpts): Promise> { const result: Record = {}; try { @@ -48,8 +49,7 @@ export class Sigstore { throw new Error('missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.'); } - const sigstoreInstance: SigstoreInstance = 'public-good'; - const endpoints = signingEndpoints(sigstoreInstance); + const endpoints = this.signingEndpoints(opts); core.info(`Using Sigstore signing endpoint: ${endpoints.fulcioURL}`); const provenanceBlobs = Sigstore.getProvenanceBlobs(opts); @@ -65,7 +65,7 @@ export class Sigstore { const bundle = await signPayload( { body: blob, - type: this.intotoPayloadType + type: intotoMediatypePayload }, endpoints ); @@ -76,7 +76,7 @@ export class Sigstore { core.info(` - ${subject.name} (${digestAlg}:${digestValue})`); } if (attest.tlogID) { - core.info(`Attestation signature uploaded to Rekor transparency log: ${this.searchSigstoreURL}?logIndex=${attest.tlogID}`); + core.info(`Attestation signature uploaded to Rekor transparency log: ${SEARCH_URL}?logIndex=${attest.tlogID}`); } core.info(`Writing Sigstore bundle to: ${bundlePath}`); fs.writeFileSync(bundlePath, JSON.stringify(attest.bundle, null, 2), { @@ -95,6 +95,16 @@ export class Sigstore { return result; } + private signingEndpoints(opts: SignProvenanceBlobsOpts): Endpoints { + const noTransparencyLog = opts.noTransparencyLog ?? GitHub.context.payload.repository?.private; + core.info(`Upload to transparency log: ${noTransparencyLog ? 'disabled' : 'enabled'}`); + return { + fulcioURL: FULCIO_URL, + rekorURL: noTransparencyLog ? undefined : REKOR_URL, + tsaServerURL: TSASERVER_URL + }; + } + private static getProvenanceBlobs(opts: SignProvenanceBlobsOpts): Record { // For single platform build const singleProvenance = path.join(opts.localExportDir, 'provenance.json'); diff --git a/src/types/sigstore/sigstore.ts b/src/types/sigstore/sigstore.ts new file mode 100644 index 0000000..3fe99a9 --- /dev/null +++ b/src/types/sigstore/sigstore.ts @@ -0,0 +1,20 @@ +/** + * Copyright 2025 actions-toolkit authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const FULCIO_URL = 'https://fulcio.sigstore.dev'; +export const REKOR_URL = 'https://rekor.sigstore.dev'; +export const TSASERVER_URL = 'https://timestamp.sigstore.dev'; +export const SEARCH_URL = 'https://search.sigstore.dev';