mirror of
https://github.com/actions/attest-build-provenance.git
synced 2025-12-18 05:18:53 +00:00
Merge branch 'main' into codespace-reimagined-acorn-4jjp6r459rvrhq5v4
This commit is contained in:
commit
459f1be08d
2
.github/workflows/linter.yml
vendored
2
.github/workflows/linter.yml
vendored
@ -46,5 +46,5 @@ jobs:
|
|||||||
TYPESCRIPT_DEFAULT_STYLE: prettier
|
TYPESCRIPT_DEFAULT_STYLE: prettier
|
||||||
VALIDATE_ALL_CODEBASE: true
|
VALIDATE_ALL_CODEBASE: true
|
||||||
VALIDATE_JAVASCRIPT_STANDARD: false
|
VALIDATE_JAVASCRIPT_STANDARD: false
|
||||||
|
VALIDATE_TYPESCRIPT_STANDARD: false
|
||||||
VALIDATE_JSCPD: false
|
VALIDATE_JSCPD: false
|
||||||
VALIDATE_GITHUB_ACTIONS: false
|
|
||||||
|
|||||||
37
README.md
37
README.md
@ -36,7 +36,7 @@ attest:
|
|||||||
```
|
```
|
||||||
|
|
||||||
The `id-token` permission gives the action the ability to mint the OIDC token
|
The `id-token` permission gives the action the ability to mint the OIDC token
|
||||||
permission is necessary to persist the attestation. The `attestations`
|
necessary to request a Sigstore signing certificate. The `attestations`
|
||||||
permission is necessary to persist the attestation.
|
permission is necessary to persist the attestation.
|
||||||
|
|
||||||
1. Add the following to your workflow after your artifact has been built:
|
1. Add the following to your workflow after your artifact has been built:
|
||||||
@ -58,7 +58,8 @@ See [action.yml](action.yml)
|
|||||||
- uses: actions/attest-build-provenance@v1
|
- uses: actions/attest-build-provenance@v1
|
||||||
with:
|
with:
|
||||||
# Path to the artifact serving as the subject of the attestation. Must
|
# Path to the artifact serving as the subject of the attestation. Must
|
||||||
# specify exactly one of "subject-path" or "subject-digest".
|
# specify exactly one of "subject-path" or "subject-digest". May contain a
|
||||||
|
# glob pattern or list of paths (total subject count cannot exceed 2500).
|
||||||
subject-path:
|
subject-path:
|
||||||
|
|
||||||
# SHA256 digest of the subject for the attestation. Must be in the form
|
# SHA256 digest of the subject for the attestation. Must be in the form
|
||||||
@ -76,6 +77,10 @@ See [action.yml](action.yml)
|
|||||||
# the "subject-digest" parameter be specified. Defaults to false.
|
# the "subject-digest" parameter be specified. Defaults to false.
|
||||||
push-to-registry:
|
push-to-registry:
|
||||||
|
|
||||||
|
# Whether to attach a list of generated attestations to the workflow run
|
||||||
|
# summary page. Defaults to true.
|
||||||
|
show-summary:
|
||||||
|
|
||||||
# The GitHub token used to make authenticated API requests. Default is
|
# The GitHub token used to make authenticated API requests. Default is
|
||||||
# ${{ github.token }}
|
# ${{ github.token }}
|
||||||
github-token:
|
github-token:
|
||||||
@ -97,6 +102,15 @@ If multiple subjects are being attested at the same time, each attestation will
|
|||||||
be written to the output file on a separate line (using the [JSON Lines][7]
|
be written to the output file on a separate line (using the [JSON Lines][7]
|
||||||
format).
|
format).
|
||||||
|
|
||||||
|
## Attestation Limits
|
||||||
|
|
||||||
|
### Subject Limits
|
||||||
|
|
||||||
|
No more than 2500 subjects can be attested at the same time. Subjects will be
|
||||||
|
processed in batches 50. After the initial group of 50, each subsequent batch
|
||||||
|
will incur an exponentially increasing amount of delay (capped at 1 minute of
|
||||||
|
delay per batch) to avoid overwhelming the attestation API.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Identify Subject by Path
|
### Identify Subject by Path
|
||||||
@ -129,7 +143,7 @@ jobs:
|
|||||||
subject-path: '${{ github.workspace }}/my-app'
|
subject-path: '${{ github.workspace }}/my-app'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Identify Subjects by Wildcard
|
### Identify Multiple Subjects
|
||||||
|
|
||||||
If you are generating multiple artifacts, you can generate a provenance
|
If you are generating multiple artifacts, you can generate a provenance
|
||||||
attestation for each by using a wildcard in the `subject-path` input.
|
attestation for each by using a wildcard in the `subject-path` input.
|
||||||
@ -143,6 +157,23 @@ attestation for each by using a wildcard in the `subject-path` input.
|
|||||||
For supported wildcards along with behavior and documentation, see
|
For supported wildcards along with behavior and documentation, see
|
||||||
[@actions/glob][8] which is used internally to search for files.
|
[@actions/glob][8] which is used internally to search for files.
|
||||||
|
|
||||||
|
Alternatively, you can explicitly list multiple subjects with either a comma or
|
||||||
|
newline delimited list:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/attest-build-provenance@v1
|
||||||
|
with:
|
||||||
|
subject-path: 'dist/foo, dist/bar'
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/attest-build-provenance@v1
|
||||||
|
with:
|
||||||
|
subject-path: |
|
||||||
|
dist/foo
|
||||||
|
dist/bar
|
||||||
|
```
|
||||||
|
|
||||||
### Container Image
|
### Container Image
|
||||||
|
|
||||||
When working with container images you can invoke the action with the
|
When working with container images you can invoke the action with the
|
||||||
|
|||||||
@ -1,9 +1,48 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`main successfully run main 1`] = `
|
exports[`main when a non-default OIDC issuer is used successfully run main 1`] = `
|
||||||
{
|
{
|
||||||
"buildDefinition": {
|
"buildDefinition": {
|
||||||
"buildType": "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
|
"buildType": "https://actions.github.io/buildtypes/workflow/v1",
|
||||||
|
"externalParameters": {
|
||||||
|
"workflow": {
|
||||||
|
"path": ".github/workflows/main.yml",
|
||||||
|
"ref": "main",
|
||||||
|
"repository": "https://example-01.ghe.com/owner/repo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"internalParameters": {
|
||||||
|
"github": {
|
||||||
|
"event_name": "push",
|
||||||
|
"repository_id": "repo-id",
|
||||||
|
"repository_owner_id": "owner-id",
|
||||||
|
"runner_environment": "github-hosted",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"resolvedDependencies": [
|
||||||
|
{
|
||||||
|
"digest": {
|
||||||
|
"gitCommit": "babca52ab0c93ae16539e5923cb0d7403b9a093b",
|
||||||
|
},
|
||||||
|
"uri": "git+https://example-01.ghe.com/owner/repo@refs/heads/main",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"runDetails": {
|
||||||
|
"builder": {
|
||||||
|
"id": "https://example-01.ghe.com/owner/shared/.github/workflows/build.yml@main",
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"invocationId": "https://example-01.ghe.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`main when the default OIDC issuer is used successfully run main 1`] = `
|
||||||
|
{
|
||||||
|
"buildDefinition": {
|
||||||
|
"buildType": "https://actions.github.io/buildtypes/workflow/v1",
|
||||||
"externalParameters": {
|
"externalParameters": {
|
||||||
"workflow": {
|
"workflow": {
|
||||||
"path": ".github/workflows/main.yml",
|
"path": ".github/workflows/main.yml",
|
||||||
@ -16,6 +55,7 @@ exports[`main successfully run main 1`] = `
|
|||||||
"event_name": "push",
|
"event_name": "push",
|
||||||
"repository_id": "repo-id",
|
"repository_id": "repo-id",
|
||||||
"repository_owner_id": "owner-id",
|
"repository_owner_id": "owner-id",
|
||||||
|
"runner_environment": "github-hosted",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"resolvedDependencies": [
|
"resolvedDependencies": [
|
||||||
@ -29,7 +69,7 @@ exports[`main successfully run main 1`] = `
|
|||||||
},
|
},
|
||||||
"runDetails": {
|
"runDetails": {
|
||||||
"builder": {
|
"builder": {
|
||||||
"id": "https://github.com/actions/runner/github-hosted",
|
"id": "https://github.com/owner/shared/.github/workflows/build.yml@main",
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"invocationId": "https://github.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
|
"invocationId": "https://github.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
|
||||||
|
|||||||
@ -13,60 +13,13 @@ setFailedMock.mockImplementation(() => {})
|
|||||||
describe('main', () => {
|
describe('main', () => {
|
||||||
let outputs = {} as Record<string, string>
|
let outputs = {} as Record<string, string>
|
||||||
const originalEnv = process.env
|
const originalEnv = process.env
|
||||||
const issuer = 'https://token.actions.githubusercontent.com'
|
|
||||||
const audience = 'nobody'
|
|
||||||
const jwksPath = '/.well-known/jwks.json'
|
|
||||||
const tokenPath = '/token'
|
|
||||||
|
|
||||||
const claims = {
|
beforeEach(() => {
|
||||||
iss: issuer,
|
|
||||||
aud: 'nobody',
|
|
||||||
repository: 'owner/repo',
|
|
||||||
ref: 'refs/heads/main',
|
|
||||||
sha: 'babca52ab0c93ae16539e5923cb0d7403b9a093b',
|
|
||||||
workflow_ref: 'owner/repo/.github/workflows/main.yml@main',
|
|
||||||
event_name: 'push',
|
|
||||||
repository_id: 'repo-id',
|
|
||||||
repository_owner_id: 'owner-id',
|
|
||||||
run_id: 'run-id',
|
|
||||||
run_attempt: 'run-attempt',
|
|
||||||
runner_environment: 'github-hosted'
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.resetAllMocks()
|
jest.resetAllMocks()
|
||||||
|
|
||||||
setOutputMock.mockImplementation((key, value) => {
|
setOutputMock.mockImplementation((key, value) => {
|
||||||
outputs[key] = value
|
outputs[key] = value
|
||||||
})
|
})
|
||||||
|
|
||||||
process.env = {
|
|
||||||
...originalEnv,
|
|
||||||
ACTIONS_ID_TOKEN_REQUEST_URL: `${issuer}${tokenPath}?`,
|
|
||||||
ACTIONS_ID_TOKEN_REQUEST_TOKEN: 'token',
|
|
||||||
GITHUB_SERVER_URL: 'https://github.com',
|
|
||||||
GITHUB_REPOSITORY: claims.repository
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate JWT signing key
|
|
||||||
const key = await jose.generateKeyPair('PS256')
|
|
||||||
|
|
||||||
// Create JWK, JWKS, and JWT
|
|
||||||
const kid = '12345'
|
|
||||||
const jwk = await jose.exportJWK(key.publicKey)
|
|
||||||
const jwks = { keys: [{ ...jwk, kid }] }
|
|
||||||
const jwt = await new jose.SignJWT(claims)
|
|
||||||
.setProtectedHeader({ alg: 'PS256', kid })
|
|
||||||
.sign(key.privateKey)
|
|
||||||
|
|
||||||
// Mock OpenID configuration and JWKS endpoints
|
|
||||||
nock(issuer)
|
|
||||||
.get('/.well-known/openid-configuration')
|
|
||||||
.reply(200, { jwks_uri: `${issuer}${jwksPath}` })
|
|
||||||
nock(issuer).get(jwksPath).reply(200, jwks)
|
|
||||||
|
|
||||||
// Mock OIDC token endpoint for populating the provenance
|
|
||||||
nock(issuer).get(tokenPath).query({ audience }).reply(200, { value: jwt })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -74,14 +27,131 @@ describe('main', () => {
|
|||||||
process.env = originalEnv
|
process.env = originalEnv
|
||||||
})
|
})
|
||||||
|
|
||||||
it('successfully run main', async () => {
|
describe('when the default OIDC issuer is used', () => {
|
||||||
// Run the main function
|
const issuer = 'https://token.actions.githubusercontent.com'
|
||||||
await main.run()
|
const audience = 'nobody'
|
||||||
|
const jwksPath = '/.well-known/jwks.json'
|
||||||
|
const tokenPath = '/token'
|
||||||
|
|
||||||
// Verify that outputs were set correctly
|
const claims = {
|
||||||
expect(setOutputMock).toHaveBeenCalledTimes(2)
|
iss: issuer,
|
||||||
|
aud: 'nobody',
|
||||||
|
repository: 'owner/repo',
|
||||||
|
ref: 'refs/heads/main',
|
||||||
|
sha: 'babca52ab0c93ae16539e5923cb0d7403b9a093b',
|
||||||
|
workflow_ref: 'owner/repo/.github/workflows/main.yml@main',
|
||||||
|
job_workflow_ref: 'owner/shared/.github/workflows/build.yml@main',
|
||||||
|
event_name: 'push',
|
||||||
|
repository_id: 'repo-id',
|
||||||
|
repository_owner_id: 'owner-id',
|
||||||
|
run_id: 'run-id',
|
||||||
|
run_attempt: 'run-attempt',
|
||||||
|
runner_environment: 'github-hosted'
|
||||||
|
}
|
||||||
|
|
||||||
expect(outputs['predicate']).toMatchSnapshot()
|
beforeEach(async () => {
|
||||||
expect(outputs['predicate-type']).toBe('https://slsa.dev/provenance/v1')
|
process.env = {
|
||||||
|
...originalEnv,
|
||||||
|
ACTIONS_ID_TOKEN_REQUEST_URL: `${issuer}${tokenPath}?`,
|
||||||
|
ACTIONS_ID_TOKEN_REQUEST_TOKEN: 'token',
|
||||||
|
GITHUB_SERVER_URL: 'https://github.com',
|
||||||
|
GITHUB_REPOSITORY: claims.repository
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate JWT signing key
|
||||||
|
const key = await jose.generateKeyPair('PS256')
|
||||||
|
|
||||||
|
// Create JWK, JWKS, and JWT
|
||||||
|
const kid = '12345'
|
||||||
|
const jwk = await jose.exportJWK(key.publicKey)
|
||||||
|
const jwks = { keys: [{ ...jwk, kid }] }
|
||||||
|
const jwt = await new jose.SignJWT(claims)
|
||||||
|
.setProtectedHeader({ alg: 'PS256', kid })
|
||||||
|
.sign(key.privateKey)
|
||||||
|
|
||||||
|
// Mock OpenID configuration and JWKS endpoints
|
||||||
|
nock(issuer)
|
||||||
|
.get('/.well-known/openid-configuration')
|
||||||
|
.reply(200, { jwks_uri: `${issuer}${jwksPath}` })
|
||||||
|
nock(issuer).get(jwksPath).reply(200, jwks)
|
||||||
|
|
||||||
|
// Mock OIDC token endpoint for populating the provenance
|
||||||
|
nock(issuer).get(tokenPath).query({ audience }).reply(200, { value: jwt })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('successfully run main', async () => {
|
||||||
|
// Run the main function
|
||||||
|
await main.run()
|
||||||
|
|
||||||
|
// Verify that outputs were set correctly
|
||||||
|
expect(setOutputMock).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
expect(outputs['predicate']).toMatchSnapshot()
|
||||||
|
expect(outputs['predicate-type']).toBe('https://slsa.dev/provenance/v1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when a non-default OIDC issuer is used', () => {
|
||||||
|
const issuer = 'https://token.actions.example-01.ghe.com'
|
||||||
|
const audience = 'nobody'
|
||||||
|
const jwksPath = '/.well-known/jwks.json'
|
||||||
|
const tokenPath = '/token'
|
||||||
|
|
||||||
|
const claims = {
|
||||||
|
iss: issuer,
|
||||||
|
aud: 'nobody',
|
||||||
|
repository: 'owner/repo',
|
||||||
|
ref: 'refs/heads/main',
|
||||||
|
sha: 'babca52ab0c93ae16539e5923cb0d7403b9a093b',
|
||||||
|
workflow_ref: 'owner/repo/.github/workflows/main.yml@main',
|
||||||
|
job_workflow_ref: 'owner/shared/.github/workflows/build.yml@main',
|
||||||
|
event_name: 'push',
|
||||||
|
repository_id: 'repo-id',
|
||||||
|
repository_owner_id: 'owner-id',
|
||||||
|
run_id: 'run-id',
|
||||||
|
run_attempt: 'run-attempt',
|
||||||
|
runner_environment: 'github-hosted'
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
process.env = {
|
||||||
|
...originalEnv,
|
||||||
|
ACTIONS_ID_TOKEN_REQUEST_URL: `${issuer}${tokenPath}?`,
|
||||||
|
ACTIONS_ID_TOKEN_REQUEST_TOKEN: 'token',
|
||||||
|
GITHUB_SERVER_URL: 'https://example-01.ghe.com',
|
||||||
|
GITHUB_REPOSITORY: claims.repository
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate JWT signing key
|
||||||
|
const key = await jose.generateKeyPair('PS256')
|
||||||
|
|
||||||
|
// Create JWK, JWKS, and JWT
|
||||||
|
const kid = '12345'
|
||||||
|
const jwk = await jose.exportJWK(key.publicKey)
|
||||||
|
const jwks = { keys: [{ ...jwk, kid }] }
|
||||||
|
const jwt = await new jose.SignJWT(claims)
|
||||||
|
.setProtectedHeader({ alg: 'PS256', kid })
|
||||||
|
.sign(key.privateKey)
|
||||||
|
|
||||||
|
// Mock OpenID configuration and JWKS endpoints
|
||||||
|
nock(issuer)
|
||||||
|
.get('/.well-known/openid-configuration')
|
||||||
|
.reply(200, { jwks_uri: `${issuer}${jwksPath}` })
|
||||||
|
nock(issuer).get(jwksPath).reply(200, jwks)
|
||||||
|
|
||||||
|
// Mock OIDC token endpoint for populating the provenance
|
||||||
|
nock(issuer).get(tokenPath).query({ audience }).reply(200, { value: jwt })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('successfully run main', async () => {
|
||||||
|
// Run the main function
|
||||||
|
await main.run()
|
||||||
|
|
||||||
|
// Verify that outputs were set correctly
|
||||||
|
expect(setOutputMock).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
expect(outputs['predicate']).toMatchSnapshot()
|
||||||
|
expect(outputs['predicate-type']).toBe('https://slsa.dev/provenance/v1')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
16
action.yml
16
action.yml
@ -8,8 +8,9 @@ branding:
|
|||||||
inputs:
|
inputs:
|
||||||
subject-path:
|
subject-path:
|
||||||
description: >
|
description: >
|
||||||
Path to the artifact for which provenance will be generated. Must specify
|
Path to the artifact serving as the subject of the attestation. Must
|
||||||
exactly one of "subject-path" or "subject-digest".
|
specify exactly one of "subject-path" or "subject-digest". May contain a
|
||||||
|
glob pattern or list of paths (total subject count cannot exceed 2500).
|
||||||
required: false
|
required: false
|
||||||
subject-digest:
|
subject-digest:
|
||||||
description: >
|
description: >
|
||||||
@ -29,6 +30,12 @@ inputs:
|
|||||||
and that the "subject-digest" parameter be specified. Defaults to false.
|
and that the "subject-digest" parameter be specified. Defaults to false.
|
||||||
default: false
|
default: false
|
||||||
required: false
|
required: false
|
||||||
|
show-summary:
|
||||||
|
description: >
|
||||||
|
Whether to attach a list of generated attestations to the workflow run
|
||||||
|
summary page. Defaults to true.
|
||||||
|
default: true
|
||||||
|
required: false
|
||||||
github-token:
|
github-token:
|
||||||
description: >
|
description: >
|
||||||
The GitHub token used to make authenticated API requests.
|
The GitHub token used to make authenticated API requests.
|
||||||
@ -43,9 +50,9 @@ outputs:
|
|||||||
runs:
|
runs:
|
||||||
using: 'composite'
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/attest-build-provenance/predicate@db1dde0f270afe12073070ac7aa802958ae3ec04 # predicate@1.0.0
|
- uses: actions/attest-build-provenance/predicate@d58ddf9f241cd8163408934540d01c3335864d64 # predicate@1.1.2
|
||||||
id: generate-build-provenance-predicate
|
id: generate-build-provenance-predicate
|
||||||
- uses: actions/attest@12c083815ed46d5d78222e3824f4a26c42c234d3 # v1.1.2
|
- uses: actions/attest@2da0b136720d14f01f4dbeeafd1d5a4d76cbe21d # v1.4.0
|
||||||
id: attest
|
id: attest
|
||||||
with:
|
with:
|
||||||
subject-path: ${{ inputs.subject-path }}
|
subject-path: ${{ inputs.subject-path }}
|
||||||
@ -54,4 +61,5 @@ runs:
|
|||||||
predicate-type: ${{ steps.generate-build-provenance-predicate.outputs.predicate-type }}
|
predicate-type: ${{ steps.generate-build-provenance-predicate.outputs.predicate-type }}
|
||||||
predicate: ${{ steps.generate-build-provenance-predicate.outputs.predicate }}
|
predicate: ${{ steps.generate-build-provenance-predicate.outputs.predicate }}
|
||||||
push-to-registry: ${{ inputs.push-to-registry }}
|
push-to-registry: ${{ inputs.push-to-registry }}
|
||||||
|
show-summary: ${{ inputs.show-summary }}
|
||||||
github-token: ${{ inputs.github-token }}
|
github-token: ${{ inputs.github-token }}
|
||||||
|
|||||||
BIN
dist/index.js
generated
vendored
BIN
dist/index.js
generated
vendored
Binary file not shown.
BIN
dist/licenses.txt
generated
vendored
BIN
dist/licenses.txt
generated
vendored
Binary file not shown.
819
package-lock.json
generated
819
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "actions/attest-build-provenance",
|
"name": "actions/attest-build-provenance",
|
||||||
"description": "Generate signed build provenance attestations",
|
"description": "Generate signed build provenance attestations",
|
||||||
"version": "1.0.0",
|
"version": "1.1.2",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/actions/attest-build-provenance",
|
"homepage": "https://github.com/actions/attest-build-provenance",
|
||||||
@ -70,27 +70,27 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/attest": "^1.2.1",
|
"@actions/attest": "^1.3.1",
|
||||||
"@actions/core": "^1.10.1"
|
"@actions/core": "^1.10.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20.12.12",
|
"@types/node": "^22.2.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
"@typescript-eslint/eslint-plugin": "^7.17.0",
|
||||||
"@typescript-eslint/parser": "^7.9.0",
|
"@typescript-eslint/parser": "^7.18.0",
|
||||||
"@vercel/ncc": "^0.38.1",
|
"@vercel/ncc": "^0.38.1",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-github": "^4.10.2",
|
"eslint-plugin-github": "^5.0.1",
|
||||||
"eslint-plugin-jest": "^28.5.0",
|
"eslint-plugin-jest": "^28.8.0",
|
||||||
"eslint-plugin-jsonc": "^2.15.1",
|
"eslint-plugin-jsonc": "^2.16.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jose": "^5.3.0",
|
"jose": "^5.6.3",
|
||||||
"markdownlint-cli": "^0.40.0",
|
"markdownlint-cli": "^0.41.0",
|
||||||
"nock": "^13.5.4",
|
"nock": "^13.5.4",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.3.3",
|
||||||
"prettier-eslint": "^16.3.0",
|
"prettier-eslint": "^16.3.0",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.2.4",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/main.ts
27
src/main.ts
@ -1,14 +1,21 @@
|
|||||||
import { buildSLSAProvenancePredicate } from '@actions/attest'
|
import { buildSLSAProvenancePredicate } from '@actions/attest'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
const VALID_SERVER_URLS = [
|
||||||
|
'https://github.com',
|
||||||
|
new RegExp('^https://[a-z0-9-]+\\.ghe\\.com$')
|
||||||
|
] as const
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main function for the action.
|
* The main function for the action.
|
||||||
* @returns {Promise<void>} Resolves when the action is complete.
|
* @returns {Promise<void>} Resolves when the action is complete.
|
||||||
*/
|
*/
|
||||||
export async function run(): Promise<void> {
|
export async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
const issuer = getIssuer()
|
||||||
|
|
||||||
// Calculate subject from inputs and generate provenance
|
// Calculate subject from inputs and generate provenance
|
||||||
const predicate = await buildSLSAProvenancePredicate()
|
const predicate = await buildSLSAProvenancePredicate(issuer)
|
||||||
|
|
||||||
core.setOutput('predicate', predicate.params)
|
core.setOutput('predicate', predicate.params)
|
||||||
core.setOutput('predicate-type', predicate.type)
|
core.setOutput('predicate-type', predicate.type)
|
||||||
@ -18,3 +25,21 @@ export async function run(): Promise<void> {
|
|||||||
core.setFailed(error.message)
|
core.setFailed(error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Derive the current OIDC issuer based on the server URL
|
||||||
|
function getIssuer(): string {
|
||||||
|
const serverURL = process.env.GITHUB_SERVER_URL || 'https://github.com'
|
||||||
|
|
||||||
|
// Ensure the server URL is a valid GitHub server URL
|
||||||
|
if (!VALID_SERVER_URLS.some(valid_url => serverURL.match(valid_url))) {
|
||||||
|
throw new Error(`Invalid server URL: ${serverURL}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let host = new URL(serverURL).hostname
|
||||||
|
|
||||||
|
if (host === 'github.com') {
|
||||||
|
host = 'githubusercontent.com'
|
||||||
|
}
|
||||||
|
|
||||||
|
return `https://token.actions.${host}`
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user