8000 Support single-subject attestations by AA-Turner · Pull Request #219 · actions/attest · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Support single-subject attestations #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ Attestations are saved in the JSON-serialized [Sigstore bundle][6] format.
If multiple subjects are being attested at the same time, a single attestation
will be created with references to each of the supplied subjects.

If the `single-subject-attestations` option has been set to true,
one attestation will be generated per provided subject.
All of these attestations will be written to the output file,
using the [JSON Lines][7] format (one attestation per line).

## Attestation Limits

### Subject Limits
Expand Down Expand Up @@ -320,6 +325,7 @@ jobs:
[5]: https://cli.github.com/manual/gh_attestation_verify
[6]:
https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto
[7]: https://jsonlines.org/
[8]: https://github.com/actions/toolkit/tree/main/packages/glob#patterns
[9]:
https://docs.github.com/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds
1 change: 1 addition & 0 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const defaultInputs: main.RunInputs = {
pushToRegistry: false,
showSummary: true,
githubToken: '',
singleSubjectAttestations: false,
privateSigning: false
}

Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ inputs:
The GitHub token used to make authenticated API requests.
default: ${{ github.token }}
required: false
single-subject-attestations:
description: >
If true, generate one attestation per subject. Defaults to false.
default: false
required: false
outputs:
bundle-path:
description: 'The path to the file containing the attestation bundle.'
Expand Down
2 changes: 1 addition & 1 deletion src/attest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const createAttestation = async (
}
): Promise<AttestResult> => {
// Sign provenance w/ Sigstore
const attestation = await attest({
const attestation: Attestation = await attest({
subjects,
predicateType: predicate.type,
predicate: predicate.params,
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const inputs: RunInputs = {
pushToRegistry: core.getBooleanInput('push-to-registry'),
showSummary: core.getBooleanInput('show-summary'),
githubToken: core.getInput('github-token'),
singleSubjectAttestations: core.getBooleanInput(
'single-subject-attestations'
),
// undocumented -- not part of public interface
privateSigning: ['true', 'True', 'TRUE', '1'].includes(
core.getInput('private-signing')
Expand Down
85 changes: 56 additions & 29 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
pushToRegistry: boolean
githubToken: string
showSummary: boolean
singleSubjectAttestations: boolean
// undocumented -- not part of public interface
privateSigning: boolean
}

Expand Down Expand Up @@ -65,27 +67,43 @@
const outputPath = path.join(tempDir(), ATTESTATION_FILE_NAME)
core.setOutput('bundle-path', outputPath)

const att = await createAttestation(subjects, predicate, {
const opts = {
sigstoreInstance,
pushToRegistry: inputs.pushToRegistry,
githubToken: inputs.githubToken
})
}

logAttestation(subjects, att, sigstoreInstance)
const atts: AttestResult[] = []
if (inputs.singleSubjectAttestations) {
// Generate one attestation for each subject
for (const subject of subjects) {
const att = await createAttestation([subject], predicate, opts)
atts.push(att)
}
} else {
const att = await createAttestation(subjects, predicate, opts)
atts.push(att)
}

// Write attestation bundle to output file
fs.writeFileSync(outputPath, JSON.stringify(att.bundle) + os.EOL, {
encoding: 'utf-8',
flag: 'a'
})
for (const att of atts) {
logAttestation(att, sigstoreInstance)

// Write attestation bundle to output file
fs.writeFileSync(outputPath, JSON.stringify(att.bundle) + os.EOL, {
encoding: 'utf-8',
flag: 'a'
})
}

logSubjects(subjects)

if (att.attestationID) {
core.setOutput('attestation-id', att.attestationID)
core.setOutput('attestation-url', attestationURL(att.attestationID))
if (atts[0].attestationID) {
core.setOutput('attestation-id', atts[0].attestationID)
core.setOutput('attestation-url', attestationURL(atts[0].attestationID))
}

if (inputs.showSummary) {
await logSummary(att)
await logSummary(atts)
}
} catch (err) {
// Fail the workflow run if an error occurs
Expand All @@ -110,18 +128,9 @@

// Log details about the attestation to the GitHub Actions run
const logAttestation = (
subjects: Subject[],
attestation: AttestResult,
sigstoreInstance: SigstoreInstance
): void => {
if (subjects.length === 1) {
core.info(
`Attestation created for ${subjects[0].name}@${formatSubjectDigest(subjects[0])}`
)
} else {
core.info(`Attestation created for ${subjects.length} subjects`)
}

const instanceName =
sigstoreInstance === 'public-good' ? 'Public Good' : 'GitHub'
core.startGroup(
Expand All @@ -148,20 +157,38 @@

if (attestation.attestationDigest) {
core.info(style.highlight('Attestation uploaded to registry'))
core.info(`${subjects[0].name}@${attestation.attestationDigest}`)

Check failure on line 160 in src/main.ts

View workflow job for this annotation

GitHub Actions / TypeScript Tests

Unsafe member access [0] on an `error` typed value
}
}

// Log details about attestation subjects to the GitHub Actions run
const logSubjects = (subjects: Subject[]): void => {
core.info(`Attestation created for ${subjects.length} subjects`)
for (const subject of subjects) {
core.info(
`Attestation created for ${subject.name}@${formatSubjectDigest(subject)}`
)
}
}

// Attach summary information to the GitHub Actions run
const logSummary = async (attestation: AttestResult): Promise<void> => {
const { attestationID } = attestation

if (attestationID) {
const url = attestationURL(attestationID)
core.summary.addHeading('Attestation Created', 3)
core.summary.addList([`<a href="${url}">${url}</a>`])
await core.summary.write()
const logSummary = async (attestations: AttestResult[]): Promise<void> => {
if (attestations.length <= 0) return

core.summary.addHeading(
/* istanbul ignore next */
attestations.length !== 1 ? 'Attestations Created' : 'Attestation Created',
3
)
const listItems: string[] = []
for (const { attestationID } of attestations) {
if (attestationID) {
const url = attestationURL(attestationID)
listItems.push(`<a href="${url}">${url}</a>`)
}
}
core.summary.addList(listItems)
await core.summary.write()
}

const tempDir = (): string => {
Expand Down
1 change: 1 addition & 0 deletions src/subject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type SubjectInputs = {
subjectName: string
subjectDigest: string
subjectChecksums: string
singleSubjectAttestations: boolean
downcaseName?: boolean
}
// Returns the subject specified by the action's inputs. The subject may be
Expand Down
Loading
0