forked from actions/toolkit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Brian DeHamer <[email protected]>
- Loading branch information
Showing
24 changed files
with
4,224 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright 2024 GitHub | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
# `@actions/attest` | ||
|
||
Functions for generating signed attestations for workflow artifacts. | ||
|
||
Attestations bind some subject (a named artifact along with its digest) to a | ||
predicate (some assertion about that subject) using the [in-toto | ||
statement](https://github.com/in-toto/attestation/tree/main/spec/v1) format. A | ||
signature is generated for the attestation using a | ||
[Sigstore](https://www.sigstore.dev/)-issued signing certificate. | ||
|
||
Once the attestation has been created and signed, it will be uploaded to the GH | ||
attestations API and associated with the repository from which the workflow was | ||
initiated. | ||
|
||
## Usage | ||
|
||
### `attest` | ||
|
||
The `attest` function takes the supplied subject/predicate pair and generates a | ||
signed attestation. | ||
|
||
```js | ||
const { attest } = require('@actions/attest'); | ||
const core = require('@actions/core'); | ||
|
||
async function run() { | ||
// In order to persist attestations to the repo, this should be a token with | ||
// repository write permissions. | ||
const ghToken = core.getInput('gh-token'); | ||
|
||
const attestation = await attest({ | ||
subjectName: 'my-artifact-name', | ||
subjectDigest: { 'sha256': '36ab4667...'}, | ||
predicateType: 'https://in-toto.io/attestation/release', | ||
predicate: { . . . }, | ||
token: ghToken | ||
}); | ||
|
||
console.log(attestation); | ||
} | ||
|
||
run(); | ||
``` | ||
|
||
The `attest` function supports the following options: | ||
|
||
```typescript | ||
export type AttestOptions = { | ||
// The name of the subject to be attested. | ||
subjectName: string | ||
// The digest of the subject to be attested. Should be a map of digest | ||
// algorithms to their hex-encoded values. | ||
subjectDigest: Record<string, string> | ||
// URI identifying the content type of the predicate being attested. | ||
predicateType: string | ||
// Predicate to be attested. | ||
predicate: object | ||
// GitHub token for writing attestations. | ||
token: string | ||
// Sigstore instance to use for signing. Must be one of "public-good" or | ||
// "github". | ||
sigstore?: 'public-good' | 'github' | ||
// Whether to skip writing the attestation to the GH attestations API. | ||
skipWrite?: boolean | ||
} | ||
``` | ||
### `attestProvenance` | ||
The `attestProvenance` function accepts the name and digest of some artifact and | ||
generates a build provenance attestation over those values. | ||
The attestation is formed by first generating a [SLSA provenance | ||
predicate](https://slsa.dev/spec/v1.0/provenance) populated with | ||
[metadata](https://github.com/slsa-framework/github-actions-buildtypes/tree/main/workflow/v1) | ||
pulled from the GitHub Actions run. | ||
```js | ||
const { attestProvenance } = require('@actions/attest'); | ||
const core = require('@actions/core'); | ||
|
||
async function run() { | ||
// In order to persist attestations to the repo, this should be a token with | ||
// repository write permissions. | ||
const ghToken = core.getInput('gh-token'); | ||
|
||
const attestation = await attestProvenance({ | ||
subjectName: 'my-artifact-name', | ||
subjectDigest: { 'sha256': '36ab4667...'}, | ||
token: ghToken | ||
}); | ||
|
||
console.log(attestation); | ||
} | ||
|
||
run(); | ||
``` | ||
|
||
The `attestProvenance` function supports the following options: | ||
|
||
```typescript | ||
export type AttestProvenanceOptions = { | ||
// The name of the subject to be attested. | ||
subjectName: string | ||
// The digest of the subject to be attested. Should be a map of digest | ||
// algorithms to their hex-encoded values. | ||
subjectDigest: Record<string, string> | ||
// GitHub token for writing attestations. | ||
token: string | ||
// Sigstore instance to use for signing. Must be one of "public-good" or | ||
// "github". | ||
sigstore?: 'public-good' | 'github' | ||
// Whether to skip writing the attestation to the GH attestations API. | ||
skipWrite?: boolean | ||
} | ||
``` | ||
### `Attestation` | ||
The `Attestation` returned by `attest`/`attestProvenance` has the following | ||
fields: | ||
```typescript | ||
export type Attestation = { | ||
/* | ||
* JSON-serialized Sigstore bundle containing the provenance attestation, | ||
* signature, signing certificate and witnessed timestamp. | ||
*/ | ||
bundle: SerializedBundle | ||
/* | ||
* PEM-encoded signing certificate used to sign the attestation. | ||
*/ | ||
certificate: string | ||
/* | ||
* ID of Rekor transparency log entry created for the attestation (if | ||
* applicable). | ||
*/ | ||
tlogID?: string | ||
/* | ||
* ID of the persisted attestation (accessible via the GH API). | ||
*/ | ||
attestationID?: string | ||
} | ||
``` | ||
For details about the Sigstore bundle format, see the [Bundle protobuf | ||
specification](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto). | ||
## Sigstore Instance | ||
When generating the signed attestation there are two different Sigstore | ||
instances which can be used to issue the signing certificate. By default, | ||
workflows initiated from public repositories will use the Sigstore public-good | ||
instance and persist the attestation signature to the public [Rekor transparency | ||
log](https://docs.sigstore.dev/logging/overview/). Workflows initiated from | ||
private/internal repositories will use the GitHub-internal Sigstore instance | ||
which uses a signed timestamp issued by GitHub's timestamp authority in place of | ||
the public transparency log. | ||
The default Sigstore instance selection can be overridden by passing an explicit | ||
value of either "public-good" or "github" for the `sigstore` option when calling | ||
either `attest` or `attestProvenance`. | ||
## Storage | ||
Attestations created by `attest`/`attestProvenance` will be uploaded to the GH | ||
attestations API and associated with the appropriate repository. Attestation | ||
storage is only supported for public repositories or repositories which belong | ||
to a GitHub Enterprise Cloud account. | ||
In order to generate attestations for private, non-Enterprise repositories, the | ||
`skipWrite` option should be set to `true`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# @actions/attest Releases | ||
|
||
### 1.0.0 | ||
|
||
- Initial release |
19 changes: 19 additions & 0 deletions
19
packages/attest/__tests__/__snapshots__/intoto.test.ts.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`buildIntotoStatement returns a provenance hydrated from env vars 1`] = ` | ||
{ | ||
"_type": "https://in-toto.io/Statement/v1", | ||
"predicate": { | ||
"key": "value", | ||
}, | ||
"predicateType": "predicatey", | ||
"subject": [ | ||
{ | ||
"digest": { | ||
"sha256": "7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32", | ||
}, | ||
"name": "subjecty", | ||
}, | ||
], | ||
} | ||
`; |
42 changes: 42 additions & 0 deletions
42
packages/attest/__tests__/__snapshots__/provenance.test.ts.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`buildSLSAProvenancePredicate returns a provenance hydrated from env vars 1`] = ` | ||
{ | ||
"params": { | ||
"buildDefinition": { | ||
"buildType": "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1", | ||
"externalParameters": { | ||
"workflow": { | ||
"path": ".github/workflows/main.yml", | ||
"ref": "main", | ||
"repository": "https://github.com/owner/repo", | ||
}, | ||
}, | ||
"internalParameters": { | ||
"github": { | ||
"event_name": "push", | ||
"repository_id": "repo-id", | ||
"repository_owner_id": "owner-id", | ||
}, | ||
}, | ||
"resolvedDependencies": [ | ||
{ | ||
"digest": { | ||
"gitCommit": "babca52ab0c93ae16539e5923cb0d7403b9a093b", | ||
}, | ||
"uri": "git+https://github.com/owner/repo@refs/heads/main", | ||
}, | ||
], | ||
}, | ||
"runDetails": { | ||
"builder": { | ||
"id": "https://github.com/actions/runner/github-hosted", | ||
}, | ||
"metadata": { | ||
"invocationId": "https://github.com/owner/repo/actions/runs/run-id/attempts/run-attempt", | ||
}, | ||
}, | ||
}, | ||
"type": "https://slsa.dev/provenance/v1", | ||
} | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import {attest, attestProvenance} from '../src' | ||
|
||
it('exports functions', () => { | ||
expect(attestProvenance).toBeInstanceOf(Function) | ||
expect(attest).toBeInstanceOf(Function) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import {buildIntotoStatement} from '../src/intoto' | ||
import type {Predicate, Subject} from '../src/shared.types' | ||
|
||
describe('buildIntotoStatement', () => { | ||
const subject: Subject = { | ||
name: 'subjecty', | ||
digest: { | ||
sha256: '7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32' | ||
} | ||
} | ||
|
||
const predicate: Predicate = { | ||
type: 'predicatey', | ||
params: { | ||
key: 'value' | ||
} | ||
} | ||
|
||
it('returns a provenance hydrated from env vars', () => { | ||
const statement = buildIntotoStatement(subject, predicate) | ||
expect(statement).toMatchSnapshot() | ||
}) | ||
}) |
Oops, something went wrong.