diff --git a/.gitignore b/.gitignore index 47fb503..aba0b21 100644 --- a/.gitignore +++ b/.gitignore @@ -101,3 +101,5 @@ __tests__/runner/* .idea .vscode *.code-workspace + +packages/attest/dist diff --git a/packages/attest/dist/attest.d.ts b/packages/attest/dist/attest.d.ts deleted file mode 100644 index 9bf1957..0000000 --- a/packages/attest/dist/attest.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { SignOptions } from './sign'; -import type { Attestation } from './shared.types'; -type AttestBaseOptions = SignOptions & { - subjectName: string; - subjectDigest: Record; - token: string; - skipWrite?: boolean; -}; -export type AttestOptions = AttestBaseOptions & { - predicateType: string; - predicate: object; -}; -export type AttestProvenanceOptions = AttestBaseOptions; -export declare function attest(options: AttestOptions): Promise; -export declare function attestProvenance(options: AttestProvenanceOptions): Promise; -export {}; diff --git a/packages/attest/dist/attest.js b/packages/attest/dist/attest.js deleted file mode 100644 index 5025c63..0000000 --- a/packages/attest/dist/attest.js +++ /dev/null @@ -1,62 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.attestProvenance = exports.attest = void 0; -const bundle_1 = require("@sigstore/bundle"); -const provenance_1 = require("./provenance"); -const sign_1 = require("./sign"); -const store_1 = require("./store"); -const assert_1 = __importDefault(require("assert")); -const crypto_1 = require("crypto"); -const INTOTO_PAYLOAD_TYPE = 'application/vnd.in-toto+json'; -const INTOTO_STATEMENT_V1_TYPE = 'https://in-toto.io/Statement/v1'; -async function attest(options) { - const subject = { - name: options.subjectName, - digest: options.subjectDigest - }; - const statement = { - _type: INTOTO_STATEMENT_V1_TYPE, - subject: [subject], - predicateType: options.predicateType, - predicate: options.predicate - }; - // Sign the provenance statement - const payload = { - body: Buffer.from(JSON.stringify(statement)), - type: INTOTO_PAYLOAD_TYPE - }; - const bundle = await (0, sign_1.signPayload)(payload, options); - // Store the attestation - let attestationID; - if (options.skipWrite !== true) { - attestationID = await (0, store_1.writeAttestation)((0, bundle_1.bundleToJSON)(bundle), options.token); - } - return toAttestation(bundle, attestationID); -} -exports.attest = attest; -async function attestProvenance(options) { - const predicate = (0, provenance_1.generateProvenancePredicate)(process.env); - return attest({ - ...options, - predicateType: predicate.type, - predicate: predicate.params - }); -} -exports.attestProvenance = attestProvenance; -function toAttestation(bundle, attestationID) { - // Extract the signing certificate from the bundle - (0, assert_1.default)(bundle.verificationMaterial.content.$case === 'x509CertificateChain', 'Bundle must contain an x509 certificate chain'); - const signingCert = new crypto_1.X509Certificate(bundle.verificationMaterial.content.x509CertificateChain.certificates[0].rawBytes); - // Determine if we can provide a link to the transparency log - const tlogEntries = bundle.verificationMaterial.tlogEntries; - const tlogID = tlogEntries.length > 0 ? tlogEntries[0].logIndex : undefined; - return { - bundle: (0, bundle_1.bundleToJSON)(bundle), - certificate: signingCert.toString(), - tlogID, - attestationID - }; -} diff --git a/packages/attest/dist/index.d.ts b/packages/attest/dist/index.d.ts deleted file mode 100644 index 3a82348..0000000 --- a/packages/attest/dist/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { AttestOptions, AttestProvenanceOptions, attest, attestProvenance } from './attest'; -export { generateProvenancePredicate } from './provenance'; -export { generateSBOMPredicate } from './sbom'; -export type { Attestation, Predicate, Subject, SBOM } from './shared.types'; diff --git a/packages/attest/dist/index.js b/packages/attest/dist/index.js deleted file mode 100644 index fa967f9..0000000 --- a/packages/attest/dist/index.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.generateSBOMPredicate = exports.generateProvenancePredicate = exports.attestProvenance = exports.attest = void 0; -var attest_1 = require("./attest"); -Object.defineProperty(exports, "attest", { enumerable: true, get: function () { return attest_1.attest; } }); -Object.defineProperty(exports, "attestProvenance", { enumerable: true, get: function () { return attest_1.attestProvenance; } }); -var provenance_1 = require("./provenance"); -Object.defineProperty(exports, "generateProvenancePredicate", { enumerable: true, get: function () { return provenance_1.generateProvenancePredicate; } }); -var sbom_1 = require("./sbom"); -Object.defineProperty(exports, "generateSBOMPredicate", { enumerable: true, get: function () { return sbom_1.generateSBOMPredicate; } }); diff --git a/packages/attest/dist/provenance.d.ts b/packages/attest/dist/provenance.d.ts deleted file mode 100644 index 836e6e3..0000000 --- a/packages/attest/dist/provenance.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/// -/// -/// -/// -/// -import type { Predicate, Subject } from './shared.types'; -export declare const SLSA_PREDICATE_V1_TYPE = "https://slsa.dev/provenance/v1"; -export declare const generateProvenancePredicate: (env: NodeJS.ProcessEnv) => Predicate; -export declare const generateProvenance: (subject: Subject, env: NodeJS.ProcessEnv) => object; diff --git a/packages/attest/dist/provenance.js b/packages/attest/dist/provenance.js deleted file mode 100644 index 1f1fe32..0000000 --- a/packages/attest/dist/provenance.js +++ /dev/null @@ -1,65 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.generateProvenance = exports.generateProvenancePredicate = exports.SLSA_PREDICATE_V1_TYPE = void 0; -const INTOTO_STATEMENT_V1_TYPE = 'https://in-toto.io/Statement/v1'; -exports.SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1'; -const GITHUB_BUILDER_ID_PREFIX = 'https://github.com/actions/runner'; -const GITHUB_BUILD_TYPE = 'https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1'; -const generateProvenancePredicate = (env) => { - const workflow = env.GITHUB_WORKFLOW_REF || /* istanbul ignore next */ ''; - // Split just the path and ref from the workflow string. - // owner/repo/.github/workflows/main.yml@main => - // .github/workflows/main.yml, main - const [workflowPath, workflowRef] = workflow - .replace(`${env.GITHUB_REPOSITORY}/`, '') - .split('@'); - return { - type: exports.SLSA_PREDICATE_V1_TYPE, - params: { - buildDefinition: { - buildType: GITHUB_BUILD_TYPE, - externalParameters: { - workflow: { - ref: workflowRef, - repository: `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}`, - path: workflowPath - } - }, - internalParameters: { - github: { - event_name: env.GITHUB_EVENT_NAME, - repository_id: env.GITHUB_REPOSITORY_ID, - repository_owner_id: env.GITHUB_REPOSITORY_OWNER_ID - } - }, - resolvedDependencies: [ - { - uri: `git+${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}@${env.GITHUB_REF}`, - digest: { - gitCommit: env.GITHUB_SHA - } - } - ] - }, - runDetails: { - builder: { - id: `${GITHUB_BUILDER_ID_PREFIX}/${env.RUNNER_ENVIRONMENT}` - }, - metadata: { - invocationId: `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}/attempts/${env.GITHUB_RUN_ATTEMPT}` - } - } - } - }; -}; -exports.generateProvenancePredicate = generateProvenancePredicate; -const generateProvenance = (subject, env) => { - const predicate = (0, exports.generateProvenancePredicate)(env); - return { - _type: INTOTO_STATEMENT_V1_TYPE, - subject: [subject], - predicateType: predicate.type, - predicate: predicate.params - }; -}; -exports.generateProvenance = generateProvenance; diff --git a/packages/attest/dist/sbom.d.ts b/packages/attest/dist/sbom.d.ts deleted file mode 100644 index 911db62..0000000 --- a/packages/attest/dist/sbom.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import type { SBOM, Predicate } from './shared.types'; -export declare const generateSBOMPredicate: (sbom: SBOM) => Predicate; diff --git a/packages/attest/dist/sbom.js b/packages/attest/dist/sbom.js deleted file mode 100644 index a684c0a..0000000 --- a/packages/attest/dist/sbom.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.generateSBOMPredicate = void 0; -const generateSBOMPredicate = (sbom) => { - if (sbom.type === 'spdx') { - return generateSPDXIntoto(sbom.object); - } - if (sbom.type === 'cyclonedx') { - return generateCycloneDXIntoto(sbom.object); - } - throw new Error('Unsupported SBOM format'); -}; -exports.generateSBOMPredicate = generateSBOMPredicate; -// ref: https://github.com/in-toto/attestation/blob/main/spec/predicates/spdx.md -const generateSPDXIntoto = (sbom) => { - const spdxVersion = sbom?.['spdxVersion']; - if (!spdxVersion) { - throw new Error('Cannot find spdxVersion in the SBOM'); - } - const version = spdxVersion.split('-')[1]; - return { - type: `https://spdx.dev/Document/v${version}`, - params: sbom - }; -}; -// ref: https://github.com/in-toto/attestation/blob/main/spec/predicates/cyclonedx.md -const generateCycloneDXIntoto = (sbom) => { - return { - type: 'https://cyclonedx.org/bom', - params: sbom - }; -}; diff --git a/packages/attest/dist/shared.types.d.ts b/packages/attest/dist/shared.types.d.ts deleted file mode 100644 index f173587..0000000 --- a/packages/attest/dist/shared.types.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { SerializedBundle } from '@sigstore/bundle'; -export type Subject = { - name: string; - digest: Record; -}; -export type Predicate = { - type: string; - params: object; -}; -export type Attestation = { - bundle: SerializedBundle; - certificate: string; - tlogID?: string; - attestationID?: string; -}; -export type SBOM = { - type: 'spdx' | 'cyclonedx'; - object: object; -}; diff --git a/packages/attest/dist/shared.types.js b/packages/attest/dist/shared.types.js deleted file mode 100644 index c8ad2e5..0000000 --- a/packages/attest/dist/shared.types.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/packages/attest/dist/sign.d.ts b/packages/attest/dist/sign.d.ts deleted file mode 100644 index eaaf5e8..0000000 --- a/packages/attest/dist/sign.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -/// -import { Bundle } from '@sigstore/bundle'; -import { IdentityProvider } from '@sigstore/sign'; -export type Payload = { - body: Buffer; - type: string; -}; -export type SignOptions = { - fulcioURL: string; - rekorURL?: string; - tsaServerURL?: string; - identityProvider?: IdentityProvider; - timeout?: number; - retry?: number; -}; -export declare const signPayload: (payload: Payload, options: SignOptions) => Promise; diff --git a/packages/attest/dist/sign.js b/packages/attest/dist/sign.js deleted file mode 100644 index 92cf173..0000000 --- a/packages/attest/dist/sign.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.signPayload = void 0; -const sign_1 = require("@sigstore/sign"); -const OIDC_AUDIENCE = 'sigstore'; -const DEFAULT_TIMEOUT = 10000; -const DEFAULT_RETRIES = 3; -// Signs the provided payload with Sigstore. -const signPayload = async (payload, options) => { - const artifact = { - data: payload.body, - type: payload.type - }; - // Sign the artifact and build the bundle - return initBundleBuilder(options).create(artifact); -}; -exports.signPayload = signPayload; -// Assembles the Sigstore bundle builder with the appropriate options -const initBundleBuilder = (opts) => { - const identityProvider = opts.identityProvider || new sign_1.CIContextProvider(OIDC_AUDIENCE); - const timeout = opts.timeout || DEFAULT_TIMEOUT; - const retry = opts.retry || DEFAULT_RETRIES; - const witnesses = []; - const signer = new sign_1.FulcioSigner({ - identityProvider: identityProvider, - fulcioBaseURL: opts.fulcioURL, - timeout: timeout, - retry: retry - }); - if (opts.rekorURL) { - witnesses.push(new sign_1.RekorWitness({ - rekorBaseURL: opts.rekorURL, - entryType: 'dsse', - timeout: timeout, - retry: retry - })); - } - if (opts.tsaServerURL) { - witnesses.push(new sign_1.TSAWitness({ - tsaBaseURL: opts.tsaServerURL, - timeout: timeout, - retry: retry - })); - } - return new sign_1.DSSEBundleBuilder({ signer, witnesses }); -}; diff --git a/packages/attest/dist/store.d.ts b/packages/attest/dist/store.d.ts deleted file mode 100644 index 93ef5d6..0000000 --- a/packages/attest/dist/store.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare const writeAttestation: (attestation: unknown, token: string) => Promise; diff --git a/packages/attest/dist/store.js b/packages/attest/dist/store.js deleted file mode 100644 index 49c0f3b..0000000 --- a/packages/attest/dist/store.js +++ /dev/null @@ -1,51 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.writeAttestation = void 0; -const github = __importStar(require("@actions/github")); -const make_fetch_happen_1 = __importDefault(require("make-fetch-happen")); -const CREATE_ATTESTATION_REQUEST = 'POST /repos/{owner}/{repo}/attestations'; -// Upload the attestation to the repository's attestations endpoint. Returns the -// ID of the uploaded attestation. -const writeAttestation = async (attestation, token) => { - const octokit = github.getOctokit(token, { request: { fetch: make_fetch_happen_1.default } }); - try { - const response = await octokit.request(CREATE_ATTESTATION_REQUEST, { - owner: github.context.repo.owner, - repo: github.context.repo.repo, - data: { bundle: attestation } - }); - return response.data?.id; - } - catch (err) { - /* istanbul ignore next */ - const message = err instanceof Error ? err.message : err; - throw new Error(`Failed to persist attestation: ${message}`); - } -}; -exports.writeAttestation = writeAttestation;