Files

96 lines
2.9 KiB
TypeScript
Raw Permalink Normal View History

import {attest, AttestOptions} from './attest.js'
import {getIDTokenClaims} from './oidc.js'
import type {Attestation, Predicate} from './shared.types.js'
2024-02-17 19:14:10 -08:00
const SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1'
2024-06-05 14:36:23 -07:00
const GITHUB_BUILD_TYPE = 'https://actions.github.io/buildtypes/workflow/v1'
2024-02-17 19:14:10 -08:00
export type AttestProvenanceOptions = Omit<
AttestOptions,
'predicate' | 'predicateType'
2024-03-21 19:25:36 -07:00
> & {
issuer?: string
}
2024-02-17 19:14:10 -08:00
/**
* Builds an SLSA (Supply Chain Levels for Software Artifacts) provenance
* predicate using the GitHub Actions Workflow build type.
* https://slsa.dev/spec/v1.0/provenance
* https://github.com/slsa-framework/github-actions-buildtypes/tree/main/workflow/v1
2024-03-21 19:25:36 -07:00
* @param issuer - URL for the OIDC issuer. Defaults to the GitHub Actions token
* issuer.
2024-02-17 19:14:10 -08:00
* @returns The SLSA provenance predicate.
*/
2024-03-21 19:25:36 -07:00
export const buildSLSAProvenancePredicate = async (
issuer?: string
2024-03-21 19:25:36 -07:00
): Promise<Predicate> => {
const serverURL = process.env.GITHUB_SERVER_URL
const claims = await getIDTokenClaims(issuer)
2024-02-17 19:14:10 -08:00
// Split just the path and ref from the workflow string.
// owner/repo/.github/workflows/main.yml@main =>
// .github/workflows/main.yml, main
const [workflowPath] = claims.workflow_ref
2024-03-21 19:25:36 -07:00
.replace(`${claims.repository}/`, '')
2024-02-17 19:14:10 -08:00
.split('@')
return {
type: SLSA_PREDICATE_V1_TYPE,
params: {
buildDefinition: {
buildType: GITHUB_BUILD_TYPE,
externalParameters: {
workflow: {
ref: claims.ref,
2024-03-21 19:25:36 -07:00
repository: `${serverURL}/${claims.repository}`,
2024-02-17 19:14:10 -08:00
path: workflowPath
}
},
internalParameters: {
github: {
2024-03-21 19:25:36 -07:00
event_name: claims.event_name,
repository_id: claims.repository_id,
2024-06-05 14:36:23 -07:00
repository_owner_id: claims.repository_owner_id,
runner_environment: claims.runner_environment
2024-02-17 19:14:10 -08:00
}
},
resolvedDependencies: [
{
2024-03-21 19:25:36 -07:00
uri: `git+${serverURL}/${claims.repository}@${claims.ref}`,
2024-02-17 19:14:10 -08:00
digest: {
2024-03-21 19:25:36 -07:00
gitCommit: claims.sha
2024-02-17 19:14:10 -08:00
}
}
]
},
runDetails: {
builder: {
2024-06-05 14:36:23 -07:00
id: `${serverURL}/${claims.job_workflow_ref}`
2024-02-17 19:14:10 -08:00
},
metadata: {
2024-03-21 19:25:36 -07:00
invocationId: `${serverURL}/${claims.repository}/actions/runs/${claims.run_id}/attempts/${claims.run_attempt}`
2024-02-17 19:14:10 -08:00
}
}
}
}
}
/**
* Attests the build provenance of the provided subject. Generates the SLSA
* build provenance predicate, assembles it into an in-toto statement, and
* attests it.
*
* @param options - The options for attesting the provenance.
* @returns A promise that resolves to the attestation.
*/
export async function attestProvenance(
options: AttestProvenanceOptions
): Promise<Attestation> {
2024-03-21 19:25:36 -07:00
const predicate = await buildSLSAProvenancePredicate(options.issuer)
2024-02-17 19:14:10 -08:00
return attest({
...options,
predicateType: predicate.type,
predicate: predicate.params
})
}