Compare commits

...

17 Commits

Author SHA1 Message Date
Brian DeHamer 65e3b8bbb5 bump @sigstore/oci to 0.3.6 (#88)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-06-12 11:27:41 -07:00
dependabot[bot] 0164ca8f6f Bump braces from 3.0.2 to 3.0.3 (#87)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 11:02:35 -07:00
dependabot[bot] b7c5f92e1b Bump the npm-development group with 6 updates (#86)
Bumps the npm-development group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.14.0` | `20.14.2` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `7.12.0` | `7.13.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `7.12.0` | `7.13.0` |
| [eslint-plugin-github](https://github.com/github/eslint-plugin-github) | `5.0.0` | `5.0.1` |
| [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) | `28.5.0` | `28.6.0` |
| [prettier](https://github.com/prettier/prettier) | `3.3.0` | `3.3.1` |


Updates `@types/node` from 20.14.0 to 20.14.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@typescript-eslint/eslint-plugin` from 7.12.0 to 7.13.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.13.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 7.12.0 to 7.13.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.13.0/packages/parser)

Updates `eslint-plugin-github` from 5.0.0 to 5.0.1
- [Release notes](https://github.com/github/eslint-plugin-github/releases)
- [Commits](https://github.com/github/eslint-plugin-github/compare/v5.0.0...v5.0.1)

Updates `eslint-plugin-jest` from 28.5.0 to 28.6.0
- [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases)
- [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v28.5.0...v28.6.0)

Updates `prettier` from 3.3.0 to 3.3.1
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.0...3.3.1)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: eslint-plugin-github
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: eslint-plugin-jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 10:38:20 -07:00
dependabot[bot] f80a8431fd Bump eslint-plugin-github from 4.10.2 to 5.0.0 (#84)
Bumps [eslint-plugin-github](https://github.com/github/eslint-plugin-github) from 4.10.2 to 5.0.0.
- [Release notes](https://github.com/github/eslint-plugin-github/releases)
- [Commits](https://github.com/github/eslint-plugin-github/compare/v4.10.2...v5.0.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-github
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brian DeHamer <bdehamer@github.com>
2024-06-04 11:58:40 -07:00
Brian DeHamer 805ae990d5 Revert "disable github action linting (#49)" (#85)
This reverts commit 74e71f701d.
2024-06-04 08:37:19 -07:00
dependabot[bot] 4b199e0571 Bump the npm-development group with 5 updates (#82)
Bumps the npm-development group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.12.12` | `20.14.0` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `7.11.0` | `7.12.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `7.11.0` | `7.12.0` |
| [prettier](https://github.com/prettier/prettier) | `3.2.5` | `3.3.0` |
| [ts-jest](https://github.com/kulshekhar/ts-jest) | `29.1.3` | `29.1.4` |


Updates `@types/node` from 20.12.12 to 20.14.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@typescript-eslint/eslint-plugin` from 7.11.0 to 7.12.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.12.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 7.11.0 to 7.12.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.12.0/packages/parser)

Updates `prettier` from 3.2.5 to 3.3.0
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.0)

Updates `ts-jest` from 29.1.3 to 29.1.4
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.1.3...v29.1.4)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: ts-jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-04 07:57:52 -07:00
Brian DeHamer 32795ed917 bump package version to 1.2.0 (#81)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-06-03 10:10:05 -07:00
Brian DeHamer 4fa34e85c5 enforce 16MB limit on predicate size (#80)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-06-03 09:41:25 -07:00
Brian DeHamer 9e752e3d76 batch processing w/ exponential backoff (#79)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-06-03 07:56:25 -07:00
dependabot[bot] a0652efe33 Bump the npm-development group with 5 updates (#75)
* Bump the npm-development group with 5 updates

Bumps the npm-development group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `7.10.0` | `7.11.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `7.10.0` | `7.11.0` |
| [eslint-plugin-jsonc](https://github.com/ota-meshi/eslint-plugin-jsonc) | `2.15.1` | `2.16.0` |
| [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli) | `0.40.0` | `0.41.0` |
| [ts-jest](https://github.com/kulshekhar/ts-jest) | `29.1.2` | `29.1.3` |


Updates `@typescript-eslint/eslint-plugin` from 7.10.0 to 7.11.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.11.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 7.10.0 to 7.11.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.11.0/packages/parser)

Updates `eslint-plugin-jsonc` from 2.15.1 to 2.16.0
- [Release notes](https://github.com/ota-meshi/eslint-plugin-jsonc/releases)
- [Changelog](https://github.com/ota-meshi/eslint-plugin-jsonc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ota-meshi/eslint-plugin-jsonc/compare/v2.15.1...v2.16.0)

Updates `markdownlint-cli` from 0.40.0 to 0.41.0
- [Release notes](https://github.com/igorshubovych/markdownlint-cli/releases)
- [Commits](https://github.com/igorshubovych/markdownlint-cli/compare/v0.40.0...v0.41.0)

Updates `ts-jest` from 29.1.2 to 29.1.3
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.1.2...v29.1.3)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: eslint-plugin-jsonc
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: markdownlint-cli
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: ts-jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
...

Signed-off-by: dependabot[bot] <support@github.com>

* rebuild dist

Signed-off-by: Brian DeHamer <bdehamer@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Brian DeHamer <bdehamer@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brian DeHamer <bdehamer@github.com>
2024-05-28 13:27:14 -07:00
Brian DeHamer 5b17eb7cb0 fix bug w/ private-signing input (#77)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-28 13:26:14 -07:00
Brian DeHamer faa6467995 refactor core attestation logic (#73)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-28 11:00:03 -07:00
Brian DeHamer 3ff4eb4c69 centralize collection of action inputs (#72)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-24 11:01:44 -07:00
Brian DeHamer 074a7714de bump @sigstore/oci from 0.3.3 to 0.3.4 (#71)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-23 08:32:51 -07:00
dependabot[bot] 72776582f8 Bump csv-parse from 5.5.5 to 5.5.6 in the npm-production group (#69)
* ---
updated-dependencies:
- dependency-name: csv-parse
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-production
...

Signed-off-by: dependabot[bot] <support@github.com>

* regenerate dist

Signed-off-by: Brian DeHamer <bdehamer@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Brian DeHamer <bdehamer@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brian DeHamer <bdehamer@github.com>
2024-05-23 08:18:25 -07:00
dependabot[bot] e4e9a599b8 Bump the npm-development group with 4 updates (#68)
* ---
updated-dependencies:
- dependency-name: "@sigstore/mock"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
...

Signed-off-by: dependabot[bot] <support@github.com>

* rebuild dist

Signed-off-by: Brian DeHamer <bdehamer@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Brian DeHamer <bdehamer@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brian DeHamer <bdehamer@github.com>
2024-05-23 08:13:06 -07:00
Brian DeHamer 80d9f23382 process subjects in batches (#67)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-22 07:55:00 -07:00
17 changed files with 1320 additions and 820 deletions
-1
View File
@@ -47,4 +47,3 @@ jobs:
VALIDATE_ALL_CODEBASE: true
VALIDATE_JAVASCRIPT_STANDARD: false
VALIDATE_JSCPD: false
VALIDATE_GITHUB_ACTIONS: false
+23 -7
View File
@@ -65,7 +65,7 @@ See [action.yml](action.yml)
with:
# Path to the artifact serving as the subject of the attestation. Must
# specify exactly one of "subject-path" or "subject-digest". May contain
# a glob pattern or list of paths (total subject count cannot exceed 64).
# a glob pattern or list of paths (total subject count cannot exceed 2500).
subject-path:
# SHA256 digest of the subject for the attestation. Must be in the form
@@ -81,12 +81,14 @@ See [action.yml](action.yml)
# URI identifying the type of the predicate.
predicate-type:
# JSON string containing the value for the attestation predicate. Must
# supply exactly one of "predicate-path" or "predicate".
# String containing the value for the attestation predicate. String length
# cannot exceed 16MB. Must supply exactly one of "predicate-path" or
# "predicate".
predicate:
# Path to the file which contains the JSON content for the attestation
# predicate. Must supply exactly one of "predicate-path" or "predicate".
# Path to the file which contains the content for the attestation predicate.
# File size cannot exceed 16MB. Must supply exactly one of "predicate-path"
# or "predicate".
predicate-path:
# Whether to push the attestation to the image registry. Requires that the
@@ -115,6 +117,20 @@ 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]
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.
### Predicate Limits
Whether supplied via the `predicate` or `predicatePath` input, the predicate
string cannot exceed 16MB.
## Examples
### Identify Subject by Path
@@ -175,8 +191,8 @@ fully-qualified image name (e.g. "ghcr.io/user/app" or
"acme.azurecr.io/user/app"). Do NOT include a tag as part of the image name --
the specific image being attested is identified by the supplied digest.
> **NOTE**: When pushing to Docker Hub, please use "docker.io" as the
> registry portion of the image name.
> **NOTE**: When pushing to Docker Hub, please use "docker.io" as the registry
> portion of the image name.
```yaml
name: build-attested-image
+5
View File
@@ -2,12 +2,17 @@
* Unit tests for the action's entrypoint, src/index.ts
*/
import * as core from '@actions/core'
import * as main from '../src/main'
// Mock the action's entrypoint
const runMock = jest.spyOn(main, 'run').mockImplementation()
const getBooleanInputMock = jest.spyOn(core, 'getBooleanInput')
describe('index', () => {
beforeEach(() => {
getBooleanInputMock.mockImplementation(() => false)
})
it('calls run when imported', async () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('../src/index')
+139 -61
View File
@@ -20,8 +20,6 @@ import * as main from '../src/main'
// Mock the GitHub Actions core library
const infoMock = jest.spyOn(core, 'info')
const startGroupMock = jest.spyOn(core, 'startGroup')
const getInputMock = jest.spyOn(core, 'getInput')
const getBooleanInputMock = jest.spyOn(core, 'getBooleanInput')
const setOutputMock = jest.spyOn(core, 'setOutput')
const setFailedMock = jest.spyOn(core, 'setFailed')
@@ -38,12 +36,28 @@ const runMock = jest.spyOn(main, 'run')
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const defaultInputs: main.RunInputs = {
predicate: '',
predicateType: '',
predicatePath: '',
subjectName: '',
subjectDigest: '',
subjectPath: '',
pushToRegistry: false,
githubToken: '',
privateSigning: false,
batchSize: 50
}
describe('action', () => {
// Capture original environment variables and GitHub context so we can restore
// them after each test
const originalEnv = process.env
const originalContext = { ...github.context }
// Mock OIDC token endpoint
const tokenURL = 'https://token.url'
// Fake an OIDC token
const oidcSubject = 'foo@bar.com'
const oidcPayload = { sub: oidcSubject, iss: '' }
@@ -62,9 +76,6 @@ describe('action', () => {
beforeEach(() => {
jest.clearAllMocks()
// Mock OIDC token endpoint
const tokenURL = 'https://token.url'
nock(tokenURL)
.get('/')
.query({ audience: 'sigstore' })
@@ -95,24 +106,22 @@ describe('action', () => {
})
describe('when ACTIONS_ID_TOKEN_REQUEST_URL is not set', () => {
const inputs = {
'subject-digest': subjectDigest,
'subject-name': subjectName,
'predicate-type': predicateType,
const inputs: main.RunInputs = {
...defaultInputs,
subjectDigest,
subjectName,
predicateType,
predicate,
'github-token': 'gh-token'
githubToken: 'gh-token'
}
beforeEach(() => {
// Nullify the OIDC token URL
process.env.ACTIONS_ID_TOKEN_REQUEST_URL = ''
getInputMock.mockImplementation(mockInput(inputs))
getBooleanInputMock.mockImplementation(() => false)
})
it('sets a failed status', async () => {
await main.run()
await main.run(inputs)
expect(runMock).toHaveReturned()
expect(setFailedMock).toHaveBeenCalledWith(
@@ -124,12 +133,8 @@ describe('action', () => {
})
describe('when no inputs are provided', () => {
beforeEach(() => {
getInputMock.mockImplementation(() => '')
})
it('sets a failed status', async () => {
await main.run()
await main.run(defaultInputs)
expect(runMock).toHaveReturned()
expect(setFailedMock).toHaveBeenCalledWith(
@@ -139,12 +144,13 @@ describe('action', () => {
})
describe('when the repository is private', () => {
const inputs = {
'subject-digest': subjectDigest,
'subject-name': subjectName,
'predicate-type': predicateType,
const inputs: main.RunInputs = {
...defaultInputs,
subjectDigest,
subjectName,
predicateType,
predicate,
'github-token': 'gh-token'
githubToken: 'gh-token'
}
beforeEach(async () => {
@@ -154,9 +160,6 @@ describe('action', () => {
repo: { owner: 'foo', repo: 'bar' }
})
getInputMock.mockImplementation(mockInput(inputs))
getBooleanInputMock.mockImplementation(() => false)
await mockFulcio({
baseURL: 'https://fulcio.githubapp.com',
strict: false
@@ -165,7 +168,7 @@ describe('action', () => {
})
it('invokes the action w/o error', async () => {
await main.run()
await main.run(inputs)
expect(runMock).toHaveReturned()
expect(setFailedMock).not.toHaveBeenCalledWith()
@@ -204,12 +207,14 @@ describe('action', () => {
const getRegCredsSpy = jest.spyOn(oci, 'getRegistryCredentials')
const attachArtifactSpy = jest.spyOn(oci, 'attachArtifactToImage')
const inputs = {
'subject-digest': subjectDigest,
'subject-name': subjectName,
'predicate-type': predicateType,
const inputs: main.RunInputs = {
...defaultInputs,
subjectDigest,
subjectName,
predicateType,
predicate,
'github-token': 'gh-token'
githubToken: 'gh-token',
pushToRegistry: true
}
beforeEach(async () => {
@@ -219,11 +224,6 @@ describe('action', () => {
repo: { owner: 'foo', repo: 'bar' }
})
// Mock the action's inputs
getInputMock.mockImplementation(mockInput(inputs))
// This is where we mock the push-to-registry input
getBooleanInputMock.mockImplementation(() => true)
await mockFulcio({
baseURL: 'https://fulcio.sigstore.dev',
strict: false
@@ -244,7 +244,7 @@ describe('action', () => {
})
it('invokes the action w/o error', async () => {
await main.run()
await main.run(inputs)
expect(runMock).toHaveReturned()
expect(setFailedMock).not.toHaveBeenCalled()
@@ -289,11 +289,16 @@ describe('action', () => {
})
})
describe('when too many subjects are specified', () => {
describe('when the subject count exceeds the batch size', () => {
let dir = ''
const filename = 'subject'
let scope: nock.Scope
beforeEach(async () => {
const filename = 'subject'
// Start from scratch
nock.cleanAll()
const subjectCount = 5
const content = 'file content'
// Set-up temp directory
@@ -301,7 +306,90 @@ describe('action', () => {
dir = await fs.mkdtemp(tmpDir + path.sep)
// Add files for glob testing
for (let i = 0; i < 65; i++) {
for (let i = 0; i < subjectCount; i++) {
await fs.writeFile(path.join(dir, `${filename}-${i}`), content)
// Set-up a Fulcio mock for each subject
await mockFulcio({
baseURL: 'https://fulcio.githubapp.com',
strict: false
})
// Set-up a TSA mock for each subject
await mockTSA({ baseURL: 'https://timestamp.githubapp.com' })
// Set-up a GH API mock for each subject
mockAgent
.get('https://api.github.com')
.intercept({
path: /^\/repos\/.*\/.*\/attestations$/,
method: 'post'
})
.reply(201, { id: attestationID })
}
// Set-up a OIDC token mock for each subject
scope = nock(tokenURL)
.get('/')
.query({ audience: 'sigstore' })
.times(subjectCount)
.reply(200, { value: oidcToken })
// Set the GH context with private repository visibility and a repo owner.
setGHContext({
payload: { repository: { visibility: 'private' } },
repo: { owner: 'foo', repo: 'bar' }
})
})
afterEach(async () => {
// Clean-up temp directory
await fs.rm(dir, { recursive: true })
})
it('invokes the action w/o error', async () => {
const inputs: main.RunInputs = {
...defaultInputs,
subjectPath: path.join(dir, `${filename}-*`),
predicateType,
predicate,
githubToken: 'gh-token',
batchSize: 2
}
await main.run(inputs)
expect(runMock).toHaveReturned()
expect(setFailedMock).not.toHaveBeenCalled()
expect(infoMock).toHaveBeenNthCalledWith(
1,
expect.stringMatching('Processing subject batch 1/3')
)
expect(infoMock).toHaveBeenNthCalledWith(
10,
expect.stringMatching('Processing subject batch 2/3')
)
expect(infoMock).toHaveBeenNthCalledWith(
19,
expect.stringMatching('Processing subject batch 3/3')
)
expect(scope.isDone()).toBe(true)
})
})
describe('when the subject count exceeds the max', () => {
let dir = ''
const filename = 'subject'
beforeEach(async () => {
const subjectCount = 2501
const content = 'file content'
// Set-up temp directory
const tmpDir = await fs.realpath(os.tmpdir())
dir = await fs.mkdtemp(tmpDir + path.sep)
// Add files for glob testing
for (let i = 0; i < subjectCount; i++) {
await fs.writeFile(path.join(dir, `${filename}-${i}`), content)
}
@@ -310,14 +398,6 @@ describe('action', () => {
payload: { repository: { visibility: 'private' } },
repo: { owner: 'foo', repo: 'bar' }
})
// Mock the action's inputs
getInputMock.mockImplementation(
mockInput({
predicate: '{}',
'subject-path': path.join(dir, `${filename}-*`)
})
)
})
afterEach(async () => {
@@ -326,27 +406,25 @@ describe('action', () => {
})
it('sets a failed status', async () => {
await main.run()
const inputs: main.RunInputs = {
...defaultInputs,
subjectPath: path.join(dir, `${filename}-*`),
predicateType,
predicate,
githubToken: 'gh-token'
}
await main.run(inputs)
expect(runMock).toHaveReturned()
expect(setFailedMock).toHaveBeenCalledWith(
new Error(
'Too many subjects specified. The maximum number of subjects is 64.'
'Too many subjects specified. The maximum number of subjects is 2500.'
)
)
})
})
})
function mockInput(inputs: Record<string, string>): typeof core.getInput {
return (name: string): string => {
if (name in inputs) {
return inputs[name]
}
return ''
}
}
// Stubbing the GitHub context is a bit tricky. We need to use
// `Object.defineProperty` because `github.context` is read-only.
function setGHContext(context: object): void {
+77 -40
View File
@@ -1,92 +1,129 @@
import fs from 'fs/promises'
import os from 'os'
import path from 'path'
import { predicateFromInputs } from '../src/predicate'
import { predicateFromInputs, PredicateInputs } from '../src/predicate'
describe('subjectFromInputs', () => {
afterEach(() => {
process.env['INPUT_PREDICATE'] = ''
process.env['INPUT_PREDICATE-PATH'] = ''
process.env['INPUT_PREDICATE-TYPE'] = ''
})
const blankInputs: PredicateInputs = {
predicateType: '',
predicate: '',
predicatePath: ''
}
describe('when no inputs are provided', () => {
it('throws an error', () => {
expect(() => predicateFromInputs()).toThrow(/predicate-type/i)
expect(() => predicateFromInputs(blankInputs)).toThrow(/predicate-type/i)
})
})
describe('when neither predicate path nor predicate are provided', () => {
beforeEach(() => {
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
})
it('throws an error', () => {
expect(() => predicateFromInputs()).toThrow(
const inputs: PredicateInputs = {
...blankInputs,
predicateType: 'https://example.com/predicate'
}
expect(() => predicateFromInputs(inputs)).toThrow(
/one of predicate-path or predicate must be provided/i
)
})
})
describe('when both predicate path and predicate are provided', () => {
beforeEach(() => {
process.env['INPUT_PREDICATE-PATH'] = 'path/to/predicate'
process.env['INPUT_PREDICATE'] = '{}'
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
})
it('throws an error', () => {
expect(() => predicateFromInputs()).toThrow(
const inputs: PredicateInputs = {
predicateType: 'https://example.com/predicate',
predicate: '{}',
predicatePath: 'path/to/predicate'
}
expect(() => predicateFromInputs(inputs)).toThrow(
/only one of predicate-path or predicate may be provided/i
)
})
})
describe('when specifying a predicate path', () => {
let dir = ''
const filename = 'subject'
const predicateType = 'https://example.com/predicate'
const content = '{}'
let predicatePath = ''
beforeEach(async () => {
// Set-up temp directory
const tmpDir = await fs.realpath(os.tmpdir())
dir = await fs.mkdtemp(tmpDir + path.sep)
const dir = await fs.mkdtemp(tmpDir + path.sep)
const filename = 'subject'
predicatePath = path.join(dir, filename)
// Write file to temp directory
await fs.writeFile(path.join(dir, filename), content)
await fs.writeFile(predicatePath, content)
})
afterEach(async () => {
// Clean-up temp directory
await fs.rm(dir, { recursive: true })
})
beforeEach(() => {
process.env['INPUT_PREDICATE-PATH'] = path.join(dir, filename)
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
await fs.rm(path.parse(predicatePath).dir, { recursive: true })
})
it('returns the predicate', () => {
expect(predicateFromInputs()).toEqual({
type: 'https://example.com/predicate',
params: {}
const inputs: PredicateInputs = {
...blankInputs,
predicateType,
predicatePath
}
expect(predicateFromInputs(inputs)).toEqual({
type: predicateType,
params: JSON.parse(content)
})
})
})
describe('when specifying a predicate path that does not exist', () => {
const predicateType = 'https://example.com/predicate'
const predicatePath = 'foo'
it('returns the predicate', () => {
const inputs: PredicateInputs = {
...blankInputs,
predicateType,
predicatePath
}
expect(() => predicateFromInputs(inputs)).toThrow(/file not found/)
})
})
describe('when specifying a predicate value', () => {
const predicateType = 'https://example.com/predicate'
const content = '{}'
beforeEach(() => {
process.env['INPUT_PREDICATE'] = content
process.env['INPUT_PREDICATE-TYPE'] = 'https://example.com/predicate'
})
it('returns the predicate', () => {
expect(predicateFromInputs()).toEqual({
type: 'https://example.com/predicate',
params: {}
const inputs: PredicateInputs = {
...blankInputs,
predicateType,
predicate: content
}
expect(predicateFromInputs(inputs)).toEqual({
type: predicateType,
params: JSON.parse(content)
})
})
})
describe('when specifying a predicate value exceeding the max size', () => {
const predicateType = 'https://example.com/predicate'
const content = JSON.stringify({ a: 'a'.repeat(16 * 1024 * 1024) })
it('throws an error', () => {
const inputs: PredicateInputs = {
...blankInputs,
predicateType,
predicate: content
}
expect(() => predicateFromInputs(inputs)).toThrow(
/predicate string exceeds maximum/
)
})
})
})
+15
View File
@@ -0,0 +1,15 @@
import { highlight, mute } from '../src/style'
describe('style', () => {
describe('highlight', () => {
it('adds cyan color to the string', () => {
expect(highlight('foo')).toBe('\x1B[36mfoo\x1B[39m')
})
})
describe('mute', () => {
it('adds gray color to the string', () => {
expect(mute('foo')).toBe('\x1B[38;5;244mfoo\x1B[39m')
})
})
})
+107 -99
View File
@@ -2,47 +2,45 @@ import crypto from 'crypto'
import fs from 'fs/promises'
import os from 'os'
import path from 'path'
import { subjectFromInputs } from '../src/subject'
import { subjectFromInputs, SubjectInputs } from '../src/subject'
describe('subjectFromInputs', () => {
beforeEach(() => {
process.env['INPUT_PUSH-TO-REGISTRY'] = 'false'
})
afterEach(() => {
process.env['INPUT_SUBJECT-PATH'] = ''
process.env['INPUT_SUBJECT-DIGEST'] = ''
process.env['INPUT_SUBJECT-NAME'] = ''
})
const blankInputs: SubjectInputs = {
subjectPath: '',
subjectName: '',
subjectDigest: ''
}
describe('when no inputs are provided', () => {
it('throws an error', async () => {
await expect(subjectFromInputs()).rejects.toThrow(
await expect(subjectFromInputs(blankInputs)).rejects.toThrow(
/one of subject-path or subject-digest must be provided/i
)
})
})
describe('when both subject path and subject digest are provided', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-PATH'] = 'path/to/subject'
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
})
it('throws an error', async () => {
await expect(subjectFromInputs()).rejects.toThrow(
const inputs: SubjectInputs = {
subjectName: 'foo',
subjectPath: 'path/to/subject',
subjectDigest: 'digest'
}
await expect(subjectFromInputs(inputs)).rejects.toThrow(
/only one of subject-path or subject-digest may be provided/i
)
})
})
describe('when subject digest is provided but not the name', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
})
it('throws an error', async () => {
await expect(subjectFromInputs()).rejects.toThrow(
const inputs: SubjectInputs = {
...blankInputs,
subjectDigest: 'digest'
}
await expect(subjectFromInputs(inputs)).rejects.toThrow(
/subject-name must be provided when using subject-digest/i
)
})
@@ -52,39 +50,42 @@ describe('subjectFromInputs', () => {
const name = 'Subject'
describe('when the digest is malformed', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
process.env['INPUT_SUBJECT-NAME'] = name
})
it('throws an error', async () => {
await expect(subjectFromInputs()).rejects.toThrow(
const inputs: SubjectInputs = {
...blankInputs,
subjectDigest: 'digest',
subjectName: name
}
await expect(subjectFromInputs(inputs)).rejects.toThrow(
/subject-digest must be in the format "sha256:<hex-digest>"/i
)
})
})
describe('when the alogrithm is not supported', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = 'md5:deadbeef'
process.env['INPUT_SUBJECT-NAME'] = name
})
it('throws an error', async () => {
await expect(subjectFromInputs()).rejects.toThrow(
const inputs: SubjectInputs = {
...blankInputs,
subjectDigest: 'md5:deadbeef',
subjectName: name
}
await expect(subjectFromInputs(inputs)).rejects.toThrow(
/subject-digest must be in the format "sha256:<hex-digest>"/i
)
})
})
describe('when the sha256 digest is malformed', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = 'sha256:deadbeef'
process.env['INPUT_SUBJECT-NAME'] = name
})
it('throws an error', async () => {
await expect(subjectFromInputs()).rejects.toThrow(
const inputs: SubjectInputs = {
...blankInputs,
subjectDigest: 'sha256:deadbeef',
subjectName: name
}
await expect(subjectFromInputs(inputs)).rejects.toThrow(
/subject-digest must be in the format "sha256:<hex-digest>"/i
)
})
@@ -95,13 +96,14 @@ describe('subjectFromInputs', () => {
const digest =
'7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = `${alg}:${digest}`
process.env['INPUT_SUBJECT-NAME'] = name
})
it('returns the subject', async () => {
const subject = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectDigest: `${alg}:${digest}`,
subjectName: name
}
const subject = await subjectFromInputs(inputs)
expect(subject).toBeDefined()
expect(subject).toHaveLength(1)
@@ -110,20 +112,21 @@ describe('subjectFromInputs', () => {
})
})
describe('when the push-to-registry is true', () => {
describe('when the downcaseName is true', () => {
const imageName = 'ghcr.io/FOO/bar'
const alg = 'sha256'
const digest =
'7d070f6b64d9bcc530fe99cc21eaaa4b3c364e0b2d367d7735671fa202a03b32'
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = `${alg}:${digest}`
process.env['INPUT_SUBJECT-NAME'] = imageName
process.env['INPUT_PUSH-TO-REGISTRY'] = 'true'
})
it('returns the subject (with name downcased)', async () => {
const subject = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectDigest: `${alg}:${digest}`,
subjectName: imageName,
downcaseName: true
}
const subject = await subjectFromInputs(inputs)
expect(subject).toBeDefined()
expect(subject).toHaveLength(1)
@@ -135,19 +138,20 @@ describe('subjectFromInputs', () => {
describe('when specifying a subject path', () => {
describe('when the file does NOT exist', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-PATH'] = '/f/a/k/e'
})
it('throws an error', async () => {
await expect(subjectFromInputs()).rejects.toThrow(
const inputs: SubjectInputs = {
...blankInputs,
subjectPath: '/f/a/k/e'
}
await expect(subjectFromInputs(inputs)).rejects.toThrow(
/could not find subject at path/i
)
})
})
})
describe('when the file eixts', () => {
describe('when the file exists', () => {
let dir = ''
const filename = 'subject'
const content = 'file content'
@@ -178,12 +182,13 @@ describe('subjectFromInputs', () => {
})
describe('when no name is provided', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, filename)
})
it('returns the subject', async () => {
const subject = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectPath: path.join(dir, filename)
}
const subject = await subjectFromInputs(inputs)
expect(subject).toBeDefined()
expect(subject).toHaveLength(1)
@@ -195,13 +200,14 @@ describe('subjectFromInputs', () => {
describe('when a name is provided', () => {
const name = 'mysubject'
beforeEach(() => {
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, filename)
process.env['INPUT_SUBJECT-NAME'] = name
})
it('returns the subject', async () => {
const subject = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectPath: path.join(dir, filename),
subjectName: name
}
const subject = await subjectFromInputs(inputs)
expect(subject).toBeDefined()
expect(subject).toHaveLength(1)
@@ -211,12 +217,13 @@ describe('subjectFromInputs', () => {
})
describe('when a file glob is supplied', () => {
beforeEach(async () => {
process.env['INPUT_SUBJECT-PATH'] = path.join(dir, 'subject-*')
})
it('returns the multiple subjects', async () => {
const subjects = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectPath: path.join(dir, 'subject-*')
}
const subjects = await subjectFromInputs(inputs)
expect(subjects).toBeDefined()
expect(subjects).toHaveLength(3)
@@ -230,12 +237,13 @@ describe('subjectFromInputs', () => {
})
describe('when a file glob is supplied which also matches non-files', () => {
beforeEach(async () => {
process.env['INPUT_SUBJECT-PATH'] = `${dir}*`
})
it('returns the subjects (excluding non-files)', async () => {
const subjects = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectPath: `${dir}*`
}
const subjects = await subjectFromInputs(inputs)
expect(subjects).toBeDefined()
expect(subjects).toHaveLength(7)
@@ -243,13 +251,13 @@ describe('subjectFromInputs', () => {
})
describe('when a comma-separated list is supplied', () => {
beforeEach(async () => {
process.env['INPUT_SUBJECT-PATH'] =
`${path.join(dir, 'subject-1')},${path.join(dir, 'subject-2')}`
})
it('returns the multiple subjects', async () => {
const subjects = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectPath: `${path.join(dir, 'subject-1')},${path.join(dir, 'subject-2')}`
}
const subjects = await subjectFromInputs(inputs)
expect(subjects).toBeDefined()
expect(subjects).toHaveLength(2)
@@ -266,13 +274,13 @@ describe('subjectFromInputs', () => {
})
describe('when a multi-line list is supplied', () => {
beforeEach(async () => {
process.env['INPUT_SUBJECT-PATH'] =
`${path.join(dir, 'subject-0')}\n${path.join(dir, 'subject-2')}`
})
it('returns the multiple subjects', async () => {
const subjects = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectPath: `${path.join(dir, 'subject-0')}\n${path.join(dir, 'subject-2')}`
}
const subjects = await subjectFromInputs(inputs)
expect(subjects).toBeDefined()
expect(subjects).toHaveLength(2)
@@ -289,13 +297,13 @@ describe('subjectFromInputs', () => {
})
describe('when a multi-line glob list is supplied', () => {
beforeEach(async () => {
process.env['INPUT_SUBJECT-PATH'] =
`${path.join(dir, 'subject-*')}\n ${path.join(dir, 'other-*')} `
})
it('returns the multiple subjects', async () => {
const subjects = await subjectFromInputs()
const inputs: SubjectInputs = {
...blankInputs,
subjectPath: `${path.join(dir, 'subject-*')}\n ${path.join(dir, 'other-*')} `
}
const subjects = await subjectFromInputs(inputs)
expect(subjects).toBeDefined()
expect(subjects).toHaveLength(6)
+6 -4
View File
@@ -10,7 +10,7 @@ inputs:
description: >
Path to the artifact serving as the subject of the attestation. Must
specify exactly one of "subject-path" or "subject-digest". May contain a
glob pattern or list of paths (total subject count cannot exceed 64).
glob pattern or list of paths (total subject count cannot exceed 2500).
required: false
subject-digest:
description: >
@@ -30,13 +30,15 @@ inputs:
required: true
predicate:
description: >
String containing the value for the attestation predicate. Must supply
exactly one of "predicate-path" or "predicate".
String containing the value for the attestation predicate. String length
cannot exceed 16MB. Must supply exactly one of "predicate-path" or
"predicate".
required: false
predicate-path:
description: >
Path to the file which contains the content for the attestation predicate.
Must supply exactly one of "predicate-path" or "predicate".
File size cannot exceed 16MB. Must supply exactly one of "predicate-path"
or "predicate".
required: false
push-to-registry:
description: >
Generated Vendored
+377 -233
View File
@@ -11744,13 +11744,20 @@ class OCIImage {
});
// Upload artifact manifest
artifactDescriptor = await __classPrivateFieldGet(this, _OCIImage_client, "f").uploadManifest(JSON.stringify(manifest));
// Check to see if registry supports the referrers API. For most
// registries the presence of a subjectDigest response header when
// uploading the artifact manifest indicates that the referrers API IS
// supported -- however, this is not a guarantee (AWS ECR does NOT support
// the referrers API but still reports a subjectDigest).
const referrersSupported = await __classPrivateFieldGet(this, _OCIImage_client, "f").pingReferrers();
// Manually update the referrers list if the referrers API is not supported.
// The lack of a subjectDigest indicates that the referrers API is not
// supported.
if (artifactDescriptor.subjectDigest === undefined) {
if (!referrersSupported) {
// Strip subjectDigest from the artifact descriptor (in case it was returned)
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
const { subjectDigest, ...descriptor } = artifactDescriptor;
await __classPrivateFieldGet(this, _OCIImage_instances, "m", _OCIImage_createReferrersIndexByTag).call(this, {
artifact: {
...artifactDescriptor,
...descriptor,
artifactType: opts.mediaType,
annotations,
},
@@ -11814,7 +11821,7 @@ async function _OCIImage_createReferrersIndexByTag(opts) {
if (!referrerManifest.manifests.some((manifest) => manifest.digest === opts.artifact.digest)) {
// Add the artifact to the index
referrerManifest.manifests.push(opts.artifact);
__classPrivateFieldGet(this, _OCIImage_client, "f").uploadManifest(JSON.stringify(referrerManifest), {
await __classPrivateFieldGet(this, _OCIImage_client, "f").uploadManifest(JSON.stringify(referrerManifest), {
mediaType: constants_1.CONTENT_TYPE_OCI_INDEX,
reference: referrerTag,
etag,
@@ -11953,7 +11960,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
var _RegistryClient_instances, _RegistryClient_baseURL, _RegistryClient_repository, _RegistryClient_fetch, _RegistryClient_fetchDistributionToken, _RegistryClient_fetchOAuth2Token;
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.RegistryClient = void 0;
exports.RegistryClient = exports.ZERO_DIGEST = void 0;
/*
Copyright 2023 The Sigstore Authors.
@@ -11980,6 +11987,7 @@ const ALL_MANIFEST_MEDIA_TYPES = [
constants_1.CONTENT_TYPE_DOCKER_MANIFEST,
constants_1.CONTENT_TYPE_DOCKER_MANIFEST_LIST,
].join(',');
exports.ZERO_DIGEST = 'sha256:0000000000000000000000000000000000000000000000000000000000000000';
class RegistryClient {
constructor(registry, repository, opts) {
_RegistryClient_instances.add(this);
@@ -12115,6 +12123,11 @@ class RegistryClient {
subjectDigest,
};
}
// Returns true if the registry supports the referrers API
async pingReferrers() {
const response = await __classPrivateFieldGet(this, _RegistryClient_fetch, "f").call(this, `${__classPrivateFieldGet(this, _RegistryClient_baseURL, "f")}/v2/${__classPrivateFieldGet(this, _RegistryClient_repository, "f")}/referrers/${exports.ZERO_DIGEST}`);
return response.status === 200;
}
static digest(blob) {
const hash = node_crypto_1.default.createHash('sha256');
hash.update(blob);
@@ -13199,7 +13212,7 @@ exports.ClientTrustConfig = exports.SigningConfig = exports.TrustedRoot = export
/* eslint-disable */
const sigstore_common_1 = __nccwpck_require__(82193);
function createBaseTransparencyLogInstance() {
return { baseUrl: "", hashAlgorithm: 0, publicKey: undefined, logId: undefined };
return { baseUrl: "", hashAlgorithm: 0, publicKey: undefined, logId: undefined, checkpointKeyId: undefined };
}
exports.TransparencyLogInstance = {
fromJSON(object) {
@@ -13208,6 +13221,7 @@ exports.TransparencyLogInstance = {
hashAlgorithm: isSet(object.hashAlgorithm) ? (0, sigstore_common_1.hashAlgorithmFromJSON)(object.hashAlgorithm) : 0,
publicKey: isSet(object.publicKey) ? sigstore_common_1.PublicKey.fromJSON(object.publicKey) : undefined,
logId: isSet(object.logId) ? sigstore_common_1.LogId.fromJSON(object.logId) : undefined,
checkpointKeyId: isSet(object.checkpointKeyId) ? sigstore_common_1.LogId.fromJSON(object.checkpointKeyId) : undefined,
};
},
toJSON(message) {
@@ -13217,6 +13231,8 @@ exports.TransparencyLogInstance = {
message.publicKey !== undefined &&
(obj.publicKey = message.publicKey ? sigstore_common_1.PublicKey.toJSON(message.publicKey) : undefined);
message.logId !== undefined && (obj.logId = message.logId ? sigstore_common_1.LogId.toJSON(message.logId) : undefined);
message.checkpointKeyId !== undefined &&
(obj.checkpointKeyId = message.checkpointKeyId ? sigstore_common_1.LogId.toJSON(message.checkpointKeyId) : undefined);
return obj;
},
};
@@ -79893,6 +79909,63 @@ function wrappy (fn, cb) {
}
/***/ }),
/***/ 26056:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.createAttestation = void 0;
const attest_1 = __nccwpck_require__(74113);
const oci_1 = __nccwpck_require__(47353);
const OCI_TIMEOUT = 2000;
const OCI_RETRY = 3;
const createAttestation = async (subject, predicate, opts) => {
// Sign provenance w/ Sigstore
const attestation = await (0, attest_1.attest)({
subjectName: subject.name,
subjectDigest: subject.digest,
predicateType: predicate.type,
predicate: predicate.params,
sigstore: opts.sigstoreInstance,
token: opts.githubToken
});
const subDigest = subjectDigest(subject);
const result = {
...attestation,
subjectName: subject.name,
subjectDigest: subDigest
};
if (opts.pushToRegistry) {
const credentials = (0, oci_1.getRegistryCredentials)(subject.name);
const artifact = await (0, oci_1.attachArtifactToImage)({
credentials,
imageName: subject.name,
imageDigest: subDigest,
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
mediaType: attestation.bundle.mediaType,
annotations: {
'dev.sigstore.bundle.content': 'dsse-envelope',
'dev.sigstore.bundle.predicateType': predicate.type
},
fetchOpts: { timeout: OCI_TIMEOUT, retry: OCI_RETRY }
});
// Add the attestation's digest to the result
result.attestationDigest = artifact.digest;
}
return result;
};
exports.createAttestation = createAttestation;
// Returns the subject's digest as a formatted string of the form
// "<algorithm>:<digest>".
const subjectDigest = (subject) => {
const alg = Object.keys(subject.digest).sort()[0];
return `${alg}:${subject.digest[alg]}`;
};
/***/ }),
/***/ 69112:
@@ -79905,6 +79978,61 @@ exports.SEARCH_PUBLIC_GOOD_URL = void 0;
exports.SEARCH_PUBLIC_GOOD_URL = 'https://search.sigstore.dev';
/***/ }),
/***/ 6144:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"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;
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
/**
* The entrypoint for the action.
*/
const core = __importStar(__nccwpck_require__(42186));
const main_1 = __nccwpck_require__(70399);
const DEFAULT_BATCH_SIZE = 50;
const inputs = {
subjectPath: core.getInput('subject-path'),
subjectName: core.getInput('subject-name'),
subjectDigest: core.getInput('subject-digest'),
predicateType: core.getInput('predicate-type'),
predicate: core.getInput('predicate'),
predicatePath: core.getInput('predicate-path'),
pushToRegistry: core.getBooleanInput('push-to-registry'),
githubToken: core.getInput('github-token'),
// undocumented -- not part of public interface
privateSigning: ['true', 'True', 'TRUE', '1'].includes(core.getInput('private-signing')),
// internal only
batchSize: DEFAULT_BATCH_SIZE
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(0, main_1.run)(inputs);
/***/ }),
/***/ 70399:
@@ -79940,23 +80068,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.run = void 0;
const attest_1 = __nccwpck_require__(74113);
const core = __importStar(__nccwpck_require__(42186));
const github = __importStar(__nccwpck_require__(95438));
const oci_1 = __nccwpck_require__(47353);
const fs_1 = __importDefault(__nccwpck_require__(57147));
const os_1 = __importDefault(__nccwpck_require__(22037));
const path_1 = __importDefault(__nccwpck_require__(71017));
const attest_1 = __nccwpck_require__(26056);
const endpoints_1 = __nccwpck_require__(69112);
const predicate_1 = __nccwpck_require__(72103);
const style = __importStar(__nccwpck_require__(41583));
const subject_1 = __nccwpck_require__(95206);
const COLOR_CYAN = '\x1B[36m';
const COLOR_GRAY = '\x1B[38;5;244m';
const COLOR_DEFAULT = '\x1B[39m';
const ATTESTATION_FILE_NAME = 'attestation.jsonl';
const MAX_SUBJECT_COUNT = 64;
const OCI_TIMEOUT = 2000;
const OCI_RETRY = 3;
const DELAY_INTERVAL_MS = 75;
const DELAY_MAX_MS = 1200;
/* istanbul ignore next */
const logHandler = (level, ...args) => {
// Send any HTTP-related log events to the GitHub Actions debug log
@@ -79968,13 +80092,13 @@ const logHandler = (level, ...args) => {
* The main function for the action.
* @returns {Promise<void>} Resolves when the action is complete.
*/
async function run() {
async function run(inputs) {
process.on('log', logHandler);
// Provenance visibility will be public ONLY if we can confirm that the
// repository is public AND the undocumented "private-signing" arg is NOT set.
// Otherwise, it will be private.
const sigstoreInstance = github.context.payload.repository?.visibility === 'public' &&
core.getInput('private-signing') !== 'true'
!inputs.privateSigning
? 'public-good'
: 'github';
try {
@@ -79982,35 +80106,41 @@ async function run() {
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
throw new Error('missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.');
}
// Gather list of subjets
const subjects = await (0, subject_1.subjectFromInputs)();
if (subjects.length > MAX_SUBJECT_COUNT) {
throw new Error(`Too many subjects specified. The maximum number of subjects is ${MAX_SUBJECT_COUNT}.`);
}
const predicate = (0, predicate_1.predicateFromInputs)();
const subjects = await (0, subject_1.subjectFromInputs)({
...inputs,
downcaseName: inputs.pushToRegistry
});
const predicate = (0, predicate_1.predicateFromInputs)(inputs);
const outputPath = path_1.default.join(tempDir(), ATTESTATION_FILE_NAME);
// Generate attestations for each subject serially
for (const subject of subjects) {
const att = await createAttestation(subject, predicate, sigstoreInstance);
// Write attestation bundle to output file
fs_1.default.writeFileSync(outputPath, JSON.stringify(att.bundle) + os_1.default.EOL, {
encoding: 'utf-8',
flag: 'a'
});
if (att.attestationID) {
atts.push({ subject, attestationID: att.attestationID });
}
}
if (atts.length > 0) {
core.summary.addHeading(
/* istanbul ignore next */
atts.length > 1 ? 'Attestations Created' : 'Attestation Created', 3);
for (const { subject, attestationID } of atts) {
core.summary.addLink(`${subject.name}@${subjectDigest(subject)}`, attestationURL(attestationID));
}
core.summary.write();
}
core.setOutput('bundle-path', outputPath);
const subjectChunks = chunkArray(subjects, inputs.batchSize);
// Generate attestations for each subject serially, working in batches
for (let i = 0; i < subjectChunks.length; i++) {
if (subjectChunks.length > 1) {
core.info(`Processing subject batch ${i + 1}/${subjectChunks.length}`);
}
// Calculate the delay time for this batch
const delayTime = delay(i);
for (const subject of subjectChunks[i]) {
// Delay between attestations (only when chunk size > 1)
if (i > 0) {
await new Promise(resolve => setTimeout(resolve, delayTime));
}
const att = await (0, attest_1.createAttestation)(subject, predicate, {
sigstoreInstance,
pushToRegistry: inputs.pushToRegistry,
githubToken: inputs.githubToken
});
atts.push(att);
logAttestation(att, sigstoreInstance);
// Write attestation bundle to output file
fs_1.default.writeFileSync(outputPath, JSON.stringify(att.bundle) + os_1.default.EOL, {
encoding: 'utf-8',
flag: 'a'
});
}
}
logSummary(atts);
}
catch (err) {
// Fail the workflow run if an error occurs
@@ -80019,7 +80149,7 @@ async function run() {
/* istanbul ignore if */
if (err instanceof Error && 'cause' in err) {
const innerErr = err.cause;
core.info(mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`));
core.info(style.mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`));
}
}
finally {
@@ -80027,53 +80157,40 @@ async function run() {
}
}
exports.run = run;
const createAttestation = async (subject, predicate, sigstoreInstance) => {
// Sign provenance w/ Sigstore
const attestation = await (0, attest_1.attest)({
subjectName: subject.name,
subjectDigest: subject.digest,
predicateType: predicate.type,
predicate: predicate.params,
sigstore: sigstoreInstance,
token: core.getInput('github-token')
});
core.info(`Attestation created for ${subject.name}@${subjectDigest(subject)}`);
// Log details about the attestation to the GitHub Actions run
const logAttestation = (attestation, sigstoreInstance) => {
core.info(`Attestation created for ${attestation.subjectName}@${attestation.subjectDigest}`);
const instanceName = sigstoreInstance === 'public-good' ? 'Public Good' : 'GitHub';
core.startGroup(highlight(`Attestation signed using certificate from ${instanceName} Sigstore instance`));
core.startGroup(style.highlight(`Attestation signed using certificate from ${instanceName} Sigstore instance`));
core.info(attestation.certificate);
core.endGroup();
if (attestation.tlogID) {
core.info(highlight('Attestation signature uploaded to Rekor transparency log'));
core.info(style.highlight('Attestation signature uploaded to Rekor transparency log'));
core.info(`${endpoints_1.SEARCH_PUBLIC_GOOD_URL}?logIndex=${attestation.tlogID}`);
}
if (attestation.attestationID) {
core.info(highlight('Attestation uploaded to repository'));
core.info(style.highlight('Attestation uploaded to repository'));
core.info(attestationURL(attestation.attestationID));
}
if (core.getBooleanInput('push-to-registry', { required: false })) {
const credentials = (0, oci_1.getRegistryCredentials)(subject.name);
const artifact = await (0, oci_1.attachArtifactToImage)({
credentials,
imageName: subject.name,
imageDigest: subjectDigest(subject),
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
mediaType: attestation.bundle.mediaType,
annotations: {
'dev.sigstore.bundle.content': 'dsse-envelope',
'dev.sigstore.bundle.predicateType': core.getInput('predicate-type')
},
fetchOpts: { timeout: OCI_TIMEOUT, retry: OCI_RETRY }
});
core.info(highlight('Attestation uploaded to registry'));
core.info(`${subject.name}@${artifact.digest}`);
if (attestation.attestationDigest) {
core.info(style.highlight('Attestation uploaded to registry'));
core.info(`${attestation.subjectName}@${attestation.attestationDigest}`);
}
};
// Attach summary information to the GitHub Actions run
const logSummary = (attestations) => {
if (attestations.length > 0) {
core.summary.addHeading(
/* istanbul ignore next */
attestations.length > 1 ? 'Attestations Created' : 'Attestation Created', 3);
for (const { subjectName, subjectDigest, attestationID } of attestations) {
if (attestationID) {
core.summary.addLink(`${subjectName}@${subjectDigest}`, attestationURL(attestationID));
}
}
core.summary.write();
}
return attestation;
};
// Emphasis string using ANSI color codes
const highlight = (str) => `${COLOR_CYAN}${str}${COLOR_DEFAULT}`;
// De-emphasize string using ANSI color codes
/* istanbul ignore next */
const mute = (str) => `${COLOR_GRAY}${str}${COLOR_DEFAULT}`;
const tempDir = () => {
const basePath = process.env['RUNNER_TEMP'];
/* istanbul ignore if */
@@ -80082,12 +80199,13 @@ const tempDir = () => {
}
return fs_1.default.mkdtempSync(path_1.default.join(basePath, path_1.default.sep));
};
// Returns the subject's digest as a formatted string of the form
// "<algorithm>:<digest>".
const subjectDigest = (subject) => {
const alg = Object.keys(subject.digest).sort()[0];
return `${alg}:${subject.digest[alg]}`;
// Transforms an array into an array of arrays, each containing at most
// `chunkSize` elements.
const chunkArray = (array, chunkSize) => {
return Array.from({ length: Math.ceil(array.length / chunkSize) }, (_, index) => array.slice(index * chunkSize, (index + 1) * chunkSize));
};
// Calculate the delay time for a given iteration
const delay = (iteration) => Math.min(DELAY_INTERVAL_MS * 2 ** iteration, DELAY_MAX_MS);
const attestationURL = (id) => `${github.context.serverUrl}/${github.context.repo.owner}/${github.context.repo.repo}/attestations/${id}`;
@@ -80098,56 +80216,68 @@ const attestationURL = (id) => `${github.context.serverUrl}/${github.context.rep
"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.predicateFromInputs = void 0;
const core = __importStar(__nccwpck_require__(42186));
const fs_1 = __importDefault(__nccwpck_require__(57147));
const MAX_PREDICATE_SIZE_BYTES = 16 * 1024 * 1024;
// Returns the predicate specified by the action's inputs. The predicate value
// may be specified as a path to a file or as a string.
const predicateFromInputs = () => {
const predicateType = core.getInput('predicate-type', { required: true });
const predicateStr = core.getInput('predicate', { required: false });
const predicatePath = core.getInput('predicate-path', { required: false });
if (!predicatePath && !predicateStr) {
const predicateFromInputs = (inputs) => {
const { predicateType, predicate, predicatePath } = inputs;
if (!predicateType) {
throw new Error('predicate-type must be provided');
}
if (!predicatePath && !predicate) {
throw new Error('One of predicate-path or predicate must be provided');
}
if (predicatePath && predicateStr) {
if (predicatePath && predicate) {
throw new Error('Only one of predicate-path or predicate may be provided');
}
const params = predicatePath
? fs_1.default.readFileSync(predicatePath, 'utf-8')
: predicateStr;
let params = predicate;
if (predicatePath) {
if (!fs_1.default.existsSync(predicatePath)) {
throw new Error(`predicate file not found: ${predicatePath}`);
}
/* istanbul ignore next */
if (fs_1.default.statSync(predicatePath).size > MAX_PREDICATE_SIZE_BYTES) {
throw new Error(`predicate file exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`);
}
params = fs_1.default.readFileSync(predicatePath, 'utf-8');
}
else {
if (predicate.length > MAX_PREDICATE_SIZE_BYTES) {
throw new Error(`predicate string exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`);
}
params = predicate;
}
return { type: predicateType, params: JSON.parse(params) };
};
exports.predicateFromInputs = predicateFromInputs;
/***/ }),
/***/ 41583:
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.mute = exports.highlight = void 0;
const COLOR_CYAN = '\x1B[36m';
const COLOR_GRAY = '\x1B[38;5;244m';
const COLOR_DEFAULT = '\x1B[39m';
// Emphasis string using ANSI color codes
const highlight = (str) => `${COLOR_CYAN}${str}${COLOR_DEFAULT}`;
exports.highlight = highlight;
// De-emphasize string using ANSI color codes
const mute = (str) => `${COLOR_GRAY}${str}${COLOR_DEFAULT}`;
exports.mute = mute;
/***/ }),
/***/ 95206:
@@ -80183,24 +80313,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.subjectFromInputs = void 0;
const core = __importStar(__nccwpck_require__(42186));
const glob = __importStar(__nccwpck_require__(28090));
const crypto_1 = __importDefault(__nccwpck_require__(6113));
const sync_1 = __nccwpck_require__(74393);
const fs_1 = __importDefault(__nccwpck_require__(57147));
const path_1 = __importDefault(__nccwpck_require__(71017));
const MAX_SUBJECT_COUNT = 2500;
const DIGEST_ALGORITHM = 'sha256';
// Returns the subject specified by the action's inputs. The subject may be
// specified as a path to a file or as a digest. If a path is provided, the
// file's digest is calculated and returned along with the subject's name. If a
// digest is provided, the name must also be provided.
const subjectFromInputs = async () => {
const subjectPath = core.getInput('subject-path', { required: false });
const subjectDigest = core.getInput('subject-digest', { required: false });
const subjectName = core.getInput('subject-name', { required: false });
const pushToRegistry = core.getBooleanInput('push-to-registry', {
required: false
});
const subjectFromInputs = async (inputs) => {
const { subjectPath, subjectDigest, subjectName, downcaseName } = inputs;
if (!subjectPath && !subjectDigest) {
throw new Error('One of subject-path or subject-digest must be provided');
}
@@ -80212,7 +80337,7 @@ const subjectFromInputs = async () => {
}
// If push-to-registry is enabled, ensure the subject name is lowercase
// to conform to OCI image naming conventions
const name = pushToRegistry ? subjectName.toLowerCase() : subjectName;
const name = downcaseName ? subjectName.toLowerCase() : subjectName;
if (subjectPath) {
return await getSubjectFromPath(subjectPath, name);
}
@@ -80224,27 +80349,31 @@ exports.subjectFromInputs = subjectFromInputs;
// Returns the subject specified by the path to a file. The file's digest is
// calculated and returned along with the subject's name.
const getSubjectFromPath = async (subjectPath, subjectName) => {
const subjects = [];
const digestedSubjects = [];
const files = [];
// Parse the list of subject paths
const subjectPaths = parseList(subjectPath);
// Expand the globbed paths to a list of files
for (const subPath of subjectPaths) {
// Expand the globbed path to a list of files
/* eslint-disable-next-line github/no-then */
const files = await glob.create(subPath).then(async (g) => g.glob());
for (const file of files) {
// Skip anything that is NOT a file
if (!fs_1.default.statSync(file).isFile()) {
continue;
}
const name = subjectName || path_1.default.parse(file).base;
const digest = await digestFile(DIGEST_ALGORITHM, file);
subjects.push({ name, digest: { [DIGEST_ALGORITHM]: digest } });
}
files.push(...(await glob.create(subPath).then(async (g) => g.glob())));
}
if (subjects.length === 0) {
if (files.length > MAX_SUBJECT_COUNT) {
throw new Error(`Too many subjects specified. The maximum number of subjects is ${MAX_SUBJECT_COUNT}.`);
}
for (const file of files) {
// Skip anything that is NOT a file
if (!fs_1.default.statSync(file).isFile()) {
continue;
}
const name = subjectName || path_1.default.parse(file).base;
const digest = await digestFile(DIGEST_ALGORITHM, file);
digestedSubjects.push({ name, digest: { [DIGEST_ALGORITHM]: digest } });
}
if (digestedSubjects.length === 0) {
throw new Error(`Could not find subject at path ${subjectPath}`);
}
return Promise.all(subjects);
return digestedSubjects;
};
// Returns the subject specified by the digest of a file. The digest is returned
// along with the subject's name.
@@ -80423,6 +80552,14 @@ module.exports = require("node:fs");
/***/ }),
/***/ 93977:
/***/ ((module) => {
"use strict";
module.exports = require("node:fs/promises");
/***/ }),
/***/ 70612:
/***/ ((module) => {
@@ -80447,6 +80584,22 @@ module.exports = require("node:stream");
/***/ }),
/***/ 76915:
/***/ ((module) => {
"use strict";
module.exports = require("node:string_decoder");
/***/ }),
/***/ 41041:
/***/ ((module) => {
"use strict";
module.exports = require("node:url");
/***/ }),
/***/ 47261:
/***/ ((module) => {
@@ -88350,9 +88503,9 @@ const proc = typeof process === 'object' && process
stdout: null,
stderr: null,
};
const events_1 = __nccwpck_require__(82361);
const stream_1 = __importDefault(__nccwpck_require__(12781));
const string_decoder_1 = __nccwpck_require__(71576);
const node_events_1 = __nccwpck_require__(15673);
const node_stream_1 = __importDefault(__nccwpck_require__(84492));
const node_string_decoder_1 = __nccwpck_require__(76915);
/**
* Return true if the argument is a Minipass stream, Node stream, or something
* else that Minipass can interact with.
@@ -88360,7 +88513,7 @@ const string_decoder_1 = __nccwpck_require__(71576);
const isStream = (s) => !!s &&
typeof s === 'object' &&
(s instanceof Minipass ||
s instanceof stream_1.default ||
s instanceof node_stream_1.default ||
(0, exports.isReadable)(s) ||
(0, exports.isWritable)(s));
exports.isStream = isStream;
@@ -88369,17 +88522,17 @@ exports.isStream = isStream;
*/
const isReadable = (s) => !!s &&
typeof s === 'object' &&
s instanceof events_1.EventEmitter &&
s instanceof node_events_1.EventEmitter &&
typeof s.pipe === 'function' &&
// node core Writable streams have a pipe() method, but it throws
s.pipe !== stream_1.default.Writable.prototype.pipe;
s.pipe !== node_stream_1.default.Writable.prototype.pipe;
exports.isReadable = isReadable;
/**
* Return true if the argument is a valid {@link Minipass.Writable}
*/
const isWritable = (s) => !!s &&
typeof s === 'object' &&
s instanceof events_1.EventEmitter &&
s instanceof node_events_1.EventEmitter &&
typeof s.write === 'function' &&
typeof s.end === 'function';
exports.isWritable = isWritable;
@@ -88486,7 +88639,7 @@ const isEncodingOptions = (o) => !o.objectMode && !!o.encoding && o.encoding !==
* `Events` is the set of event handler signatures that this object
* will emit, see {@link Minipass.Events}
*/
class Minipass extends events_1.EventEmitter {
class Minipass extends node_events_1.EventEmitter {
[FLOWING] = false;
[PAUSED] = false;
[PIPES] = [];
@@ -88541,7 +88694,7 @@ class Minipass extends events_1.EventEmitter {
}
this[ASYNC] = !!options.async;
this[DECODER] = this[ENCODING]
? new string_decoder_1.StringDecoder(this[ENCODING])
? new node_string_decoder_1.StringDecoder(this[ENCODING])
: null;
//@ts-ignore - private option for debugging and testing
if (options && options.debugExposeBuffer === true) {
@@ -89400,14 +89553,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.PathScurry = exports.Path = exports.PathScurryDarwin = exports.PathScurryPosix = exports.PathScurryWin32 = exports.PathScurryBase = exports.PathPosix = exports.PathWin32 = exports.PathBase = exports.ChildrenCache = exports.ResolveCache = void 0;
const lru_cache_1 = __nccwpck_require__(66091);
const path_1 = __nccwpck_require__(71017);
const url_1 = __nccwpck_require__(57310);
const actualFS = __importStar(__nccwpck_require__(57147));
const node_path_1 = __nccwpck_require__(49411);
const node_url_1 = __nccwpck_require__(41041);
const fs_1 = __nccwpck_require__(57147);
const actualFS = __importStar(__nccwpck_require__(87561));
const realpathSync = fs_1.realpathSync.native;
// TODO: test perf of fs/promises realpath vs realpathCB,
// since the promises one uses realpath.native
const promises_1 = __nccwpck_require__(73292);
const promises_1 = __nccwpck_require__(93977);
const minipass_1 = __nccwpck_require__(14968);
const defaultFS = {
lstatSync: fs_1.lstatSync,
@@ -89423,8 +89576,8 @@ const defaultFS = {
},
};
// if they just gave us require('fs') then use our default
const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS
? defaultFS
const fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS ?
defaultFS
: {
...defaultFS,
...fsOption,
@@ -89465,20 +89618,13 @@ const ENOREADLINK = 0b0001_0000_0000;
const ENOREALPATH = 0b0010_0000_0000;
const ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
const TYPEMASK = 0b0011_1111_1111;
const entToType = (s) => s.isFile()
? IFREG
: s.isDirectory()
? IFDIR
: s.isSymbolicLink()
? IFLNK
: s.isCharacterDevice()
? IFCHR
: s.isBlockDevice()
? IFBLK
: s.isSocket()
? IFSOCK
: s.isFIFO()
? IFIFO
const entToType = (s) => s.isFile() ? IFREG
: s.isDirectory() ? IFDIR
: s.isSymbolicLink() ? IFLNK
: s.isCharacterDevice() ? IFCHR
: s.isBlockDevice() ? IFBLK
: s.isSocket() ? IFSOCK
: s.isFIFO() ? IFIFO
: UNKNOWN;
// normalize unicode path names
const normalizeCache = new Map();
@@ -89582,6 +89728,11 @@ class PathBase {
* @internal
*/
nocase;
/**
* boolean indicating that this path is the current working directory
* of the PathScurry collection that contains it.
*/
isCWD = false;
// potential default fs override
#fs;
// Stats fields
@@ -89669,13 +89820,19 @@ class PathBase {
#realpath;
/**
* This property is for compatibility with the Dirent class as of
* Node v20, where Dirent['path'] refers to the path of the directory
* that was passed to readdir. So, somewhat counterintuitively, this
* property refers to the *parent* path, not the path object itself.
* For root entries, it's the path to the entry itself.
* Node v20, where Dirent['parentPath'] refers to the path of the
* directory that was passed to readdir. For root entries, it's the path
* to the entry itself.
*/
get parentPath() {
return (this.parent || this).fullpath();
}
/**
* Deprecated alias for Dirent['parentPath'] Somewhat counterintuitively,
* this property refers to the *parent* path, not the path object itself.
*/
get path() {
return (this.parent || this).fullpath();
return this.parentPath;
}
/**
* Do not create new Path objects directly. They should always be accessed
@@ -89730,8 +89887,8 @@ class PathBase {
const rootPath = this.getRootString(path);
const dir = path.substring(rootPath.length);
const dirParts = dir.split(this.splitSep);
const result = rootPath
? this.getRoot(rootPath).#resolveParts(dirParts)
const result = rootPath ?
this.getRoot(rootPath).#resolveParts(dirParts)
: this.#resolveParts(dirParts);
return result;
}
@@ -89782,9 +89939,7 @@ class PathBase {
}
// find the child
const children = this.children();
const name = this.nocase
? normalizeNocase(pathPart)
: normalize(pathPart);
const name = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
for (const p of children) {
if (p.#matchName === name) {
return p;
@@ -89794,9 +89949,7 @@ class PathBase {
// actually exist. If we know the parent isn't a dir, then
// in fact it CAN'T exist.
const s = this.parent ? this.sep : '';
const fullpath = this.#fullpath
? this.#fullpath + s + pathPart
: undefined;
const fullpath = this.#fullpath ? this.#fullpath + s + pathPart : undefined;
const pchild = this.newChild(pathPart, UNKNOWN, {
...opts,
parent: this,
@@ -89815,6 +89968,8 @@ class PathBase {
* the cwd, then this ends up being equivalent to the fullpath()
*/
relative() {
if (this.isCWD)
return '';
if (this.#relative !== undefined) {
return this.#relative;
}
@@ -89835,6 +89990,8 @@ class PathBase {
relativePosix() {
if (this.sep === '/')
return this.relative();
if (this.isCWD)
return '';
if (this.#relativePosix !== undefined)
return this.#relativePosix;
const name = this.name;
@@ -89900,23 +90057,15 @@ class PathBase {
return this[`is${type}`]();
}
getType() {
return this.isUnknown()
? 'Unknown'
: this.isDirectory()
? 'Directory'
: this.isFile()
? 'File'
: this.isSymbolicLink()
? 'SymbolicLink'
: this.isFIFO()
? 'FIFO'
: this.isCharacterDevice()
? 'CharacterDevice'
: this.isBlockDevice()
? 'BlockDevice'
: /* c8 ignore start */ this.isSocket()
? 'Socket'
: 'Unknown';
return (this.isUnknown() ? 'Unknown'
: this.isDirectory() ? 'Directory'
: this.isFile() ? 'File'
: this.isSymbolicLink() ? 'SymbolicLink'
: this.isFIFO() ? 'FIFO'
: this.isCharacterDevice() ? 'CharacterDevice'
: this.isBlockDevice() ? 'BlockDevice'
: /* c8 ignore start */ this.isSocket() ? 'Socket'
: 'Unknown');
/* c8 ignore stop */
}
/**
@@ -90050,8 +90199,8 @@ class PathBase {
* directly.
*/
isNamed(n) {
return !this.nocase
? this.#matchName === normalize(n)
return !this.nocase ?
this.#matchName === normalize(n)
: this.#matchName === normalizeNocase(n);
}
/**
@@ -90107,7 +90256,7 @@ class PathBase {
/* c8 ignore stop */
try {
const read = this.#fs.readlinkSync(this.fullpath());
const linkTarget = (this.parent.realpathSync())?.resolve(read);
const linkTarget = this.parent.realpathSync()?.resolve(read);
if (linkTarget) {
return (this.#linkTarget = linkTarget);
}
@@ -90228,9 +90377,7 @@ class PathBase {
#readdirMaybePromoteChild(e, c) {
for (let p = c.provisional; p < c.length; p++) {
const pchild = c[p];
const name = this.nocase
? normalizeNocase(e.name)
: normalize(e.name);
const name = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
if (name !== pchild.#matchName) {
continue;
}
@@ -90529,6 +90676,8 @@ class PathBase {
[setAsCwd](oldCwd) {
if (oldCwd === this)
return;
oldCwd.isCWD = false;
this.isCWD = true;
const changed = new Set([]);
let rp = [];
let p = this;
@@ -90583,7 +90732,7 @@ class PathWin32 extends PathBase {
* @internal
*/
getRootString(path) {
return path_1.win32.parse(path).root;
return node_path_1.win32.parse(path).root;
}
/**
* @internal
@@ -90705,7 +90854,7 @@ class PathScurryBase {
constructor(cwd = process.cwd(), pathImpl, sep, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS, } = {}) {
this.#fs = fsFromOption(fs);
if (cwd instanceof URL || cwd.startsWith('file://')) {
cwd = (0, url_1.fileURLToPath)(cwd);
cwd = (0, node_url_1.fileURLToPath)(cwd);
}
// resolve and split root, and then add to the store.
// this is the only time we call path.resolve()
@@ -91294,7 +91443,7 @@ class PathScurryWin32 extends PathScurryBase {
sep = '\\';
constructor(cwd = process.cwd(), opts = {}) {
const { nocase = true } = opts;
super(cwd, path_1.win32, '\\', { ...opts, nocase });
super(cwd, node_path_1.win32, '\\', { ...opts, nocase });
this.nocase = nocase;
for (let p = this.cwd; p; p = p.parent) {
p.nocase = this.nocase;
@@ -91307,7 +91456,7 @@ class PathScurryWin32 extends PathScurryBase {
// if the path starts with a single separator, it's not a UNC, and we'll
// just get separator as the root, and driveFromUNC will return \
// In that case, mount \ on the root from the cwd.
return path_1.win32.parse(dir).root.toUpperCase();
return node_path_1.win32.parse(dir).root.toUpperCase();
}
/**
* @internal
@@ -91337,7 +91486,7 @@ class PathScurryPosix extends PathScurryBase {
sep = '/';
constructor(cwd = process.cwd(), opts = {}) {
const { nocase = false } = opts;
super(cwd, path_1.posix, '/', { ...opts, nocase });
super(cwd, node_path_1.posix, '/', { ...opts, nocase });
this.nocase = nocase;
}
/**
@@ -91387,10 +91536,8 @@ exports.Path = process.platform === 'win32' ? PathWin32 : PathPosix;
* {@link PathScurryWin32} on Windows systems, {@link PathScurryDarwin} on
* Darwin (macOS) systems, {@link PathScurryPosix} on all others.
*/
exports.PathScurry = process.platform === 'win32'
? PathScurryWin32
: process.platform === 'darwin'
? PathScurryDarwin
exports.PathScurry = process.platform === 'win32' ? PathScurryWin32
: process.platform === 'darwin' ? PathScurryDarwin
: PathScurryPosix;
//# sourceMappingURL=index.js.map
@@ -93284,6 +93431,13 @@ const normalize_options = function(opts){
`got ${JSON.stringify(options.on_record)}`
], options);
}
// Normalize option `on_skip`
// options.on_skip ??= (err, chunk) => {
// this.emit('skip', err, chunk);
// };
if(options.on_skip !== undefined && options.on_skip !== null && typeof options.on_skip !== 'function'){
throw new Error(`Invalid Option: on_skip must be a function, got ${JSON.stringify(options.on_skip)}`);
}
// Normalize option `quote`
if(options.quote === null || options.quote === false || options.quote === ''){
options.quote = null;
@@ -94339,22 +94493,12 @@ module.exports = {"i8":"3.0.4"};
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
(() => {
"use strict";
var exports = __webpack_exports__;
Object.defineProperty(exports, "__esModule", ({ value: true }));
/**
* The entrypoint for the action.
*/
const main_1 = __nccwpck_require__(70399);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(0, main_1.run)();
})();
module.exports = __webpack_exports__;
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __nccwpck_require__(6144);
/******/ module.exports = __webpack_exports__;
/******/
/******/ })()
;
+277 -220
View File
@@ -1,40 +1,40 @@
{
"name": "actions/attest",
"version": "1.1.2",
"version": "1.2.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "actions/attest",
"version": "1.1.2",
"version": "1.2.1",
"license": "MIT",
"dependencies": {
"@actions/attest": "^1.2.1",
"@actions/core": "^1.10.1",
"@actions/glob": "^0.4.0",
"@sigstore/oci": "^0.3.3",
"csv-parse": "^5.5.5"
"@sigstore/oci": "^0.3.6",
"csv-parse": "^5.5.6"
},
"devDependencies": {
"@sigstore/mock": "^0.7.3",
"@sigstore/mock": "^0.7.4",
"@types/jest": "^29.5.12",
"@types/make-fetch-happen": "^10.0.4",
"@types/node": "^20.12.11",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@types/node": "^20.14.2",
"@typescript-eslint/eslint-plugin": "^7.13.0",
"@typescript-eslint/parser": "^7.13.0",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"eslint-plugin-github": "^4.10.2",
"eslint-plugin-jest": "^28.5.0",
"eslint-plugin-jsonc": "^2.15.1",
"eslint-plugin-github": "^5.0.1",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-jsonc": "^2.16.0",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"js-yaml": "^4.1.0",
"markdownlint-cli": "^0.40.0",
"markdownlint-cli": "^0.41.0",
"nock": "^13.5.4",
"prettier": "^3.2.5",
"prettier": "^3.3.1",
"prettier-eslint": "^16.3.0",
"ts-jest": "^29.1.2",
"ts-jest": "^29.1.4",
"typescript": "^5.4.5",
"undici": "^5.28.4"
},
@@ -1708,14 +1708,14 @@
}
},
"node_modules/@sigstore/mock": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@sigstore/mock/-/mock-0.7.3.tgz",
"integrity": "sha512-Nztnzos5YubhLv5A+2TJxI7k/75P30hKrb+PwyeUwDMEzuztWpbgAinkICytQnrNHkqkoLiE3rDiX/cxsfTkzA==",
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@sigstore/mock/-/mock-0.7.4.tgz",
"integrity": "sha512-ij9X2Fij9fcH7upxf3KuAZ38ecGSMm+Asvbik5xiHTBUcwe1+bZ5eG6k5p1eHaNY+XJ581bC6O33871Bm5m5mQ==",
"dev": true,
"dependencies": {
"@peculiar/webcrypto": "^1.4.6",
"@peculiar/x509": "^1.9.7",
"@sigstore/protobuf-specs": "^0.3.0",
"@sigstore/protobuf-specs": "^0.3.2",
"asn1js": "^3.0.5",
"bytestreamjs": "^2.0.1",
"canonicalize": "^2.0.0",
@@ -1729,9 +1729,9 @@
}
},
"node_modules/@sigstore/oci": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@sigstore/oci/-/oci-0.3.3.tgz",
"integrity": "sha512-GFNS7BVC0YvZnajj/ZtboH98A8T0rApkkI3988BzkuIJ5f3Z+mTXr/b5K7OekfHv7LvLzSziXXRRnsb6Cx8zXg==",
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/@sigstore/oci/-/oci-0.3.6.tgz",
"integrity": "sha512-nv/uHEHj6AbzGcBg1Cs7EsetB0M+N8GW1wYA26KQT6ymirv5UWUtqx9L1hbJjClpQ6/8R0vYXCpunvic2O1jfg==",
"dependencies": {
"make-fetch-happen": "^13.0.1",
"proc-log": "^4.2.0"
@@ -1741,9 +1741,9 @@
}
},
"node_modules/@sigstore/protobuf-specs": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.1.tgz",
"integrity": "sha512-aIL8Z9NsMr3C64jyQzE0XlkEyBLpgEJJFDHLVVStkFV5Q3Il/r/YtY6NJWKQ4cy4AE7spP1IX5Jq7VCAxHHMfQ==",
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz",
"integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==",
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
@@ -1960,9 +1960,9 @@
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
},
"node_modules/@types/node": {
"version": "20.12.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
"integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
"version": "20.14.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz",
"integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==",
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -2037,16 +2037,16 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz",
"integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.0.tgz",
"integrity": "sha512-FX1X6AF0w8MdVFLSdqwqN/me2hyhuQg4ykN6ZpVhh1ij/80pTvDKclX1sZB9iqex8SjQfVhwMKs3JtnnMLzG9w==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.9.0",
"@typescript-eslint/type-utils": "7.9.0",
"@typescript-eslint/utils": "7.9.0",
"@typescript-eslint/visitor-keys": "7.9.0",
"@typescript-eslint/scope-manager": "7.13.0",
"@typescript-eslint/type-utils": "7.13.0",
"@typescript-eslint/utils": "7.13.0",
"@typescript-eslint/visitor-keys": "7.13.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -2070,15 +2070,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz",
"integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.0.tgz",
"integrity": "sha512-EjMfl69KOS9awXXe83iRN7oIEXy9yYdqWfqdrFAYAAr6syP8eLEFI7ZE4939antx2mNgPRW/o1ybm2SFYkbTVA==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "7.9.0",
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/typescript-estree": "7.9.0",
"@typescript-eslint/visitor-keys": "7.9.0",
"@typescript-eslint/scope-manager": "7.13.0",
"@typescript-eslint/types": "7.13.0",
"@typescript-eslint/typescript-estree": "7.13.0",
"@typescript-eslint/visitor-keys": "7.13.0",
"debug": "^4.3.4"
},
"engines": {
@@ -2098,13 +2098,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz",
"integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz",
"integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/visitor-keys": "7.9.0"
"@typescript-eslint/types": "7.13.0",
"@typescript-eslint/visitor-keys": "7.13.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -2115,13 +2115,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz",
"integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.0.tgz",
"integrity": "sha512-xMEtMzxq9eRkZy48XuxlBFzpVMDurUAfDu5Rz16GouAtXm0TaAoTFzqWUFPPuQYXI/CDaH/Bgx/fk/84t/Bc9A==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "7.9.0",
"@typescript-eslint/utils": "7.9.0",
"@typescript-eslint/typescript-estree": "7.13.0",
"@typescript-eslint/utils": "7.13.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
@@ -2142,9 +2142,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz",
"integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz",
"integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==",
"dev": true,
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -2155,13 +2155,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz",
"integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz",
"integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/visitor-keys": "7.9.0",
"@typescript-eslint/types": "7.13.0",
"@typescript-eslint/visitor-keys": "7.13.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -2207,15 +2207,15 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz",
"integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz",
"integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "7.9.0",
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/typescript-estree": "7.9.0"
"@typescript-eslint/scope-manager": "7.13.0",
"@typescript-eslint/types": "7.13.0",
"@typescript-eslint/typescript-estree": "7.13.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
@@ -2229,12 +2229,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz",
"integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz",
"integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/types": "7.13.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
@@ -2721,11 +2721,12 @@
}
},
"node_modules/braces": {
"version": "3.0.2",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -3037,9 +3038,9 @@
}
},
"node_modules/commander": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
"integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"dev": true,
"engines": {
"node": ">=18"
@@ -3095,9 +3096,9 @@
}
},
"node_modules/csv-parse": {
"version": "5.5.5",
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.5.tgz",
"integrity": "sha512-erCk7tyU3yLWAhk6wvKxnyPtftuy/6Ak622gOO7BCJ05+TYffnPCJF905wmOQm+BpkX54OdAl8pveJwUdpnCXQ=="
"version": "5.5.6",
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.6.tgz",
"integrity": "sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A=="
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@@ -3667,9 +3668,9 @@
}
},
"node_modules/eslint-plugin-github": {
"version": "4.10.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.2.tgz",
"integrity": "sha512-F1F5aAFgi1Y5hYoTFzGQACBkw5W1hu2Fu5FSTrMlXqrojJnKl1S2pWO/rprlowRQpt+hzHhqSpsfnodJEVd5QA==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-5.0.1.tgz",
"integrity": "sha512-qbXG3wL5Uh2JB92EKeX2hPtO9c/t75qVxQjVLYuTFfhHifLZzv9CBvLCvoaBhLrAC/xTMVht7DK/NofYK8X4Dg==",
"dev": true,
"dependencies": {
"@github/browserslist-config": "^1.0.0",
@@ -3768,9 +3769,9 @@
}
},
"node_modules/eslint-plugin-jest": {
"version": "28.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.5.0.tgz",
"integrity": "sha512-6np6DGdmNq/eBbA7HOUNV8fkfL86PYwBfwyb8n23FXgJNTR8+ot3smRHjza9LGsBBZRypK3qyF79vMjohIL8eQ==",
"version": "28.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.6.0.tgz",
"integrity": "sha512-YG28E1/MIKwnz+e2H7VwYPzHUYU4aMa19w0yGcwXnnmJH6EfgHahTJ2un3IyraUxNfnz/KUhJAFXNNwWPo12tg==",
"dev": true,
"dependencies": {
"@typescript-eslint/utils": "^6.0.0 || ^7.0.0"
@@ -3793,9 +3794,9 @@
}
},
"node_modules/eslint-plugin-jsonc": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.15.1.tgz",
"integrity": "sha512-PVFrqIJa8BbM/e828RSn0SwB/Z5ye+2LDuy2XqG6AymNgPsfApRRcznsbxP7VrjdLEU4Nb+g9n/d6opyp0jp9A==",
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.16.0.tgz",
"integrity": "sha512-Af/ZL5mgfb8FFNleH6KlO4/VdmDuTqmM+SPnWcdoWywTetv7kq+vQe99UyQb9XO3b0OWLVuTH7H0d/PXYCMdSg==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
@@ -4114,9 +4115,10 @@
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -4909,8 +4911,9 @@
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
@@ -6251,14 +6254,14 @@
}
},
"node_modules/markdownlint-cli": {
"version": "0.40.0",
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz",
"integrity": "sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA==",
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz",
"integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==",
"dev": true,
"dependencies": {
"commander": "~12.0.0",
"commander": "~12.1.0",
"get-stdin": "~9.0.0",
"glob": "~10.3.12",
"glob": "~10.4.1",
"ignore": "~5.3.1",
"js-yaml": "^4.1.0",
"jsonc-parser": "~3.2.1",
@@ -6266,7 +6269,7 @@
"markdownlint": "~0.34.0",
"minimatch": "~9.0.4",
"run-con": "~1.3.2",
"toml": "~3.0.0"
"smol-toml": "~1.2.0"
},
"bin": {
"markdownlint": "markdownlint.js"
@@ -6285,27 +6288,45 @@
}
},
"node_modules/markdownlint-cli/node_modules/glob": {
"version": "10.3.12",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
"integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
"integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
"dev": true,
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^2.3.6",
"minimatch": "^9.0.1",
"minipass": "^7.0.4",
"path-scurry": "^1.10.2"
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"engines": {
"node": ">=16 || 14 >=14.17"
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/markdownlint-cli/node_modules/jackspeak": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
"integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
"dev": true,
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/markdownlint-cli/node_modules/minimatch": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
@@ -6410,8 +6431,9 @@
}
},
"node_modules/minipass": {
"version": "7.0.4",
"license": "ISC",
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"engines": {
"node": ">=16 || 14 >=14.17"
}
@@ -6847,15 +6869,15 @@
"license": "MIT"
},
"node_modules/path-scurry": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
"integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.17"
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -6993,9 +7015,10 @@
}
},
"node_modules/prettier": {
"version": "3.2.5",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.1.tgz",
"integrity": "sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -7683,6 +7706,16 @@
"npm": ">= 3.0.0"
}
},
"node_modules/smol-toml": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.0.tgz",
"integrity": "sha512-KObxdQANC/xje3OoatMbSwQf2XAvJ0RbK+4nmQRszFNZptbNRnMWqbLF/zb4sMi9xJ6HNyhWXeuZ9zC/I/XY7w==",
"dev": true,
"engines": {
"node": ">= 18",
"pnpm": ">= 9"
}
},
"node_modules/socks": {
"version": "2.7.3",
"license": "MIT",
@@ -8023,8 +8056,9 @@
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
@@ -8032,12 +8066,6 @@
"node": ">=8.0"
}
},
"node_modules/toml": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
"dev": true
},
"node_modules/ts-api-utils": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
@@ -8051,9 +8079,10 @@
}
},
"node_modules/ts-jest": {
"version": "29.1.2",
"version": "29.1.4",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.4.tgz",
"integrity": "sha512-YiHwDhSvCiItoAgsKtoLFCuakDzDsJ1DLDnSouTaTmdOcOwIkSzbLXduaQ6M5DRVhuZC/NYaaZ/mtHbWMv/S6Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"bs-logger": "0.x",
"fast-json-stable-stringify": "2.x",
@@ -8068,10 +8097,11 @@
"ts-jest": "cli.js"
},
"engines": {
"node": "^16.10.0 || ^18.0.0 || >=20.0.0"
"node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
},
"peerDependencies": {
"@babel/core": ">=7.0.0-beta.0 <8",
"@jest/transform": "^29.0.0",
"@jest/types": "^29.0.0",
"babel-jest": "^29.0.0",
"jest": "^29.0.0",
@@ -8081,6 +8111,9 @@
"@babel/core": {
"optional": true
},
"@jest/transform": {
"optional": true
},
"@jest/types": {
"optional": true
},
@@ -9787,14 +9820,14 @@
"integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg=="
},
"@sigstore/mock": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@sigstore/mock/-/mock-0.7.3.tgz",
"integrity": "sha512-Nztnzos5YubhLv5A+2TJxI7k/75P30hKrb+PwyeUwDMEzuztWpbgAinkICytQnrNHkqkoLiE3rDiX/cxsfTkzA==",
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@sigstore/mock/-/mock-0.7.4.tgz",
"integrity": "sha512-ij9X2Fij9fcH7upxf3KuAZ38ecGSMm+Asvbik5xiHTBUcwe1+bZ5eG6k5p1eHaNY+XJ581bC6O33871Bm5m5mQ==",
"dev": true,
"requires": {
"@peculiar/webcrypto": "^1.4.6",
"@peculiar/x509": "^1.9.7",
"@sigstore/protobuf-specs": "^0.3.0",
"@sigstore/protobuf-specs": "^0.3.2",
"asn1js": "^3.0.5",
"bytestreamjs": "^2.0.1",
"canonicalize": "^2.0.0",
@@ -9805,18 +9838,18 @@
}
},
"@sigstore/oci": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@sigstore/oci/-/oci-0.3.3.tgz",
"integrity": "sha512-GFNS7BVC0YvZnajj/ZtboH98A8T0rApkkI3988BzkuIJ5f3Z+mTXr/b5K7OekfHv7LvLzSziXXRRnsb6Cx8zXg==",
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/@sigstore/oci/-/oci-0.3.6.tgz",
"integrity": "sha512-nv/uHEHj6AbzGcBg1Cs7EsetB0M+N8GW1wYA26KQT6ymirv5UWUtqx9L1hbJjClpQ6/8R0vYXCpunvic2O1jfg==",
"requires": {
"make-fetch-happen": "^13.0.1",
"proc-log": "^4.2.0"
}
},
"@sigstore/protobuf-specs": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.1.tgz",
"integrity": "sha512-aIL8Z9NsMr3C64jyQzE0XlkEyBLpgEJJFDHLVVStkFV5Q3Il/r/YtY6NJWKQ4cy4AE7spP1IX5Jq7VCAxHHMfQ=="
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz",
"integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw=="
},
"@sigstore/sign": {
"version": "2.3.1",
@@ -10012,9 +10045,9 @@
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
},
"@types/node": {
"version": "20.12.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
"integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
"version": "20.14.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz",
"integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==",
"requires": {
"undici-types": "~5.26.4"
}
@@ -10083,16 +10116,16 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz",
"integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.0.tgz",
"integrity": "sha512-FX1X6AF0w8MdVFLSdqwqN/me2hyhuQg4ykN6ZpVhh1ij/80pTvDKclX1sZB9iqex8SjQfVhwMKs3JtnnMLzG9w==",
"dev": true,
"requires": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.9.0",
"@typescript-eslint/type-utils": "7.9.0",
"@typescript-eslint/utils": "7.9.0",
"@typescript-eslint/visitor-keys": "7.9.0",
"@typescript-eslint/scope-manager": "7.13.0",
"@typescript-eslint/type-utils": "7.13.0",
"@typescript-eslint/utils": "7.13.0",
"@typescript-eslint/visitor-keys": "7.13.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -10100,54 +10133,54 @@
}
},
"@typescript-eslint/parser": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz",
"integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.0.tgz",
"integrity": "sha512-EjMfl69KOS9awXXe83iRN7oIEXy9yYdqWfqdrFAYAAr6syP8eLEFI7ZE4939antx2mNgPRW/o1ybm2SFYkbTVA==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "7.9.0",
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/typescript-estree": "7.9.0",
"@typescript-eslint/visitor-keys": "7.9.0",
"@typescript-eslint/scope-manager": "7.13.0",
"@typescript-eslint/types": "7.13.0",
"@typescript-eslint/typescript-estree": "7.13.0",
"@typescript-eslint/visitor-keys": "7.13.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz",
"integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz",
"integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/visitor-keys": "7.9.0"
"@typescript-eslint/types": "7.13.0",
"@typescript-eslint/visitor-keys": "7.13.0"
}
},
"@typescript-eslint/type-utils": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz",
"integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.0.tgz",
"integrity": "sha512-xMEtMzxq9eRkZy48XuxlBFzpVMDurUAfDu5Rz16GouAtXm0TaAoTFzqWUFPPuQYXI/CDaH/Bgx/fk/84t/Bc9A==",
"dev": true,
"requires": {
"@typescript-eslint/typescript-estree": "7.9.0",
"@typescript-eslint/utils": "7.9.0",
"@typescript-eslint/typescript-estree": "7.13.0",
"@typescript-eslint/utils": "7.13.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
}
},
"@typescript-eslint/types": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz",
"integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz",
"integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz",
"integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz",
"integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/visitor-keys": "7.9.0",
"@typescript-eslint/types": "7.13.0",
"@typescript-eslint/visitor-keys": "7.13.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -10177,24 +10210,24 @@
}
},
"@typescript-eslint/utils": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz",
"integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz",
"integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "7.9.0",
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/typescript-estree": "7.9.0"
"@typescript-eslint/scope-manager": "7.13.0",
"@typescript-eslint/types": "7.13.0",
"@typescript-eslint/typescript-estree": "7.13.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz",
"integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz",
"integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "7.9.0",
"@typescript-eslint/types": "7.13.0",
"eslint-visitor-keys": "^3.4.3"
}
},
@@ -10524,10 +10557,12 @@
}
},
"braces": {
"version": "3.0.2",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
}
},
"browserslist": {
@@ -10704,9 +10739,9 @@
}
},
"commander": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
"integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"dev": true
},
"common-tags": {
@@ -10742,9 +10777,9 @@
}
},
"csv-parse": {
"version": "5.5.5",
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.5.tgz",
"integrity": "sha512-erCk7tyU3yLWAhk6wvKxnyPtftuy/6Ak622gOO7BCJ05+TYffnPCJF905wmOQm+BpkX54OdAl8pveJwUdpnCXQ=="
"version": "5.5.6",
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.6.tgz",
"integrity": "sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A=="
},
"damerau-levenshtein": {
"version": "1.0.8",
@@ -11157,9 +11192,9 @@
}
},
"eslint-plugin-github": {
"version": "4.10.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.2.tgz",
"integrity": "sha512-F1F5aAFgi1Y5hYoTFzGQACBkw5W1hu2Fu5FSTrMlXqrojJnKl1S2pWO/rprlowRQpt+hzHhqSpsfnodJEVd5QA==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-5.0.1.tgz",
"integrity": "sha512-qbXG3wL5Uh2JB92EKeX2hPtO9c/t75qVxQjVLYuTFfhHifLZzv9CBvLCvoaBhLrAC/xTMVht7DK/NofYK8X4Dg==",
"dev": true,
"requires": {
"@github/browserslist-config": "^1.0.0",
@@ -11240,18 +11275,18 @@
}
},
"eslint-plugin-jest": {
"version": "28.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.5.0.tgz",
"integrity": "sha512-6np6DGdmNq/eBbA7HOUNV8fkfL86PYwBfwyb8n23FXgJNTR8+ot3smRHjza9LGsBBZRypK3qyF79vMjohIL8eQ==",
"version": "28.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.6.0.tgz",
"integrity": "sha512-YG28E1/MIKwnz+e2H7VwYPzHUYU4aMa19w0yGcwXnnmJH6EfgHahTJ2un3IyraUxNfnz/KUhJAFXNNwWPo12tg==",
"dev": true,
"requires": {
"@typescript-eslint/utils": "^6.0.0 || ^7.0.0"
}
},
"eslint-plugin-jsonc": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.15.1.tgz",
"integrity": "sha512-PVFrqIJa8BbM/e828RSn0SwB/Z5ye+2LDuy2XqG6AymNgPsfApRRcznsbxP7VrjdLEU4Nb+g9n/d6opyp0jp9A==",
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.16.0.tgz",
"integrity": "sha512-Af/ZL5mgfb8FFNleH6KlO4/VdmDuTqmM+SPnWcdoWywTetv7kq+vQe99UyQb9XO3b0OWLVuTH7H0d/PXYCMdSg==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
@@ -11451,7 +11486,9 @@
}
},
"fill-range": {
"version": "7.0.1",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
@@ -11945,6 +11982,8 @@
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"is-number-object": {
@@ -12902,14 +12941,14 @@
}
},
"markdownlint-cli": {
"version": "0.40.0",
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz",
"integrity": "sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA==",
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz",
"integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==",
"dev": true,
"requires": {
"commander": "~12.0.0",
"commander": "~12.1.0",
"get-stdin": "~9.0.0",
"glob": "~10.3.12",
"glob": "~10.4.1",
"ignore": "~5.3.1",
"js-yaml": "^4.1.0",
"jsonc-parser": "~3.2.1",
@@ -12917,7 +12956,7 @@
"markdownlint": "~0.34.0",
"minimatch": "~9.0.4",
"run-con": "~1.3.2",
"toml": "~3.0.0"
"smol-toml": "~1.2.0"
},
"dependencies": {
"brace-expansion": {
@@ -12930,16 +12969,26 @@
}
},
"glob": {
"version": "10.3.12",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
"integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
"integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
"dev": true,
"requires": {
"foreground-child": "^3.1.0",
"jackspeak": "^2.3.6",
"minimatch": "^9.0.1",
"minipass": "^7.0.4",
"path-scurry": "^1.10.2"
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"path-scurry": "^1.11.1"
}
},
"jackspeak": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
"integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
"dev": true,
"requires": {
"@isaacs/cliui": "^8.0.2",
"@pkgjs/parseargs": "^0.11.0"
}
},
"minimatch": {
@@ -13007,7 +13056,9 @@
"dev": true
},
"minipass": {
"version": "7.0.4"
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
},
"minipass-collect": {
"version": "2.0.1",
@@ -13288,9 +13339,9 @@
"dev": true
},
"path-scurry": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
"integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"requires": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
@@ -13379,7 +13430,9 @@
"dev": true
},
"prettier": {
"version": "3.2.5",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.1.tgz",
"integrity": "sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==",
"dev": true
},
"prettier-eslint": {
@@ -13788,6 +13841,12 @@
"smart-buffer": {
"version": "4.2.0"
},
"smol-toml": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.0.tgz",
"integrity": "sha512-KObxdQANC/xje3OoatMbSwQf2XAvJ0RbK+4nmQRszFNZptbNRnMWqbLF/zb4sMi9xJ6HNyhWXeuZ9zC/I/XY7w==",
"dev": true
},
"socks": {
"version": "2.7.3",
"requires": {
@@ -14012,17 +14071,13 @@
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
},
"toml": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
"dev": true
},
"ts-api-utils": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
@@ -14031,7 +14086,9 @@
"requires": {}
},
"ts-jest": {
"version": "29.1.2",
"version": "29.1.4",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.4.tgz",
"integrity": "sha512-YiHwDhSvCiItoAgsKtoLFCuakDzDsJ1DLDnSouTaTmdOcOwIkSzbLXduaQ6M5DRVhuZC/NYaaZ/mtHbWMv/S6Q==",
"dev": true,
"requires": {
"bs-logger": "0.x",
+13 -13
View File
@@ -1,7 +1,7 @@
{
"name": "actions/attest",
"description": "Generate signed attestations for workflow artifacts",
"version": "1.1.2",
"version": "1.2.1",
"author": "",
"private": true,
"homepage": "https://github.com/actions/attest",
@@ -72,29 +72,29 @@
"@actions/attest": "^1.2.1",
"@actions/core": "^1.10.1",
"@actions/glob": "^0.4.0",
"@sigstore/oci": "^0.3.3",
"csv-parse": "^5.5.5"
"@sigstore/oci": "^0.3.6",
"csv-parse": "^5.5.6"
},
"devDependencies": {
"@sigstore/mock": "^0.7.3",
"@sigstore/mock": "^0.7.4",
"@types/jest": "^29.5.12",
"@types/make-fetch-happen": "^10.0.4",
"@types/node": "^20.12.11",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@types/node": "^20.14.2",
"@typescript-eslint/eslint-plugin": "^7.13.0",
"@typescript-eslint/parser": "^7.13.0",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"eslint-plugin-github": "^4.10.2",
"eslint-plugin-jest": "^28.5.0",
"eslint-plugin-jsonc": "^2.15.1",
"eslint-plugin-github": "^5.0.1",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-jsonc": "^2.16.0",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"js-yaml": "^4.1.0",
"markdownlint-cli": "^0.40.0",
"markdownlint-cli": "^0.41.0",
"nock": "^13.5.4",
"prettier": "^3.2.5",
"prettier": "^3.3.1",
"prettier-eslint": "^16.3.0",
"ts-jest": "^29.1.2",
"ts-jest": "^29.1.4",
"typescript": "^5.4.5",
"undici": "^5.28.4"
}
+67
View File
@@ -0,0 +1,67 @@
import { Attestation, Predicate, Subject, attest } from '@actions/attest'
import { attachArtifactToImage, getRegistryCredentials } from '@sigstore/oci'
const OCI_TIMEOUT = 2000
const OCI_RETRY = 3
export type SigstoreInstance = 'public-good' | 'github'
export type AttestResult = Attestation & {
subjectName: string
subjectDigest: string
attestationDigest?: string
}
export const createAttestation = async (
subject: Subject,
predicate: Predicate,
opts: {
sigstoreInstance: SigstoreInstance
pushToRegistry: boolean
githubToken: string
}
): Promise<AttestResult> => {
// Sign provenance w/ Sigstore
const attestation = await attest({
subjectName: subject.name,
subjectDigest: subject.digest,
predicateType: predicate.type,
predicate: predicate.params,
sigstore: opts.sigstoreInstance,
token: opts.githubToken
})
const subDigest = subjectDigest(subject)
const result: AttestResult = {
...attestation,
subjectName: subject.name,
subjectDigest: subDigest
}
if (opts.pushToRegistry) {
const credentials = getRegistryCredentials(subject.name)
const artifact = await attachArtifactToImage({
credentials,
imageName: subject.name,
imageDigest: subDigest,
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
mediaType: attestation.bundle.mediaType,
annotations: {
'dev.sigstore.bundle.content': 'dsse-envelope',
'dev.sigstore.bundle.predicateType': predicate.type
},
fetchOpts: { timeout: OCI_TIMEOUT, retry: OCI_RETRY }
})
// Add the attestation's digest to the result
result.attestationDigest = artifact.digest
}
return result
}
// Returns the subject's digest as a formatted string of the form
// "<algorithm>:<digest>".
const subjectDigest = (subject: Subject): string => {
const alg = Object.keys(subject.digest).sort()[0]
return `${alg}:${subject.digest[alg]}`
}
+22 -2
View File
@@ -1,7 +1,27 @@
/**
* The entrypoint for the action.
*/
import { run } from './main'
import * as core from '@actions/core'
import { run, RunInputs } from './main'
const DEFAULT_BATCH_SIZE = 50
const inputs: RunInputs = {
subjectPath: core.getInput('subject-path'),
subjectName: core.getInput('subject-name'),
subjectDigest: core.getInput('subject-digest'),
predicateType: core.getInput('predicate-type'),
predicate: core.getInput('predicate'),
predicatePath: core.getInput('predicate-path'),
pushToRegistry: core.getBooleanInput('push-to-registry'),
githubToken: core.getInput('github-token'),
// undocumented -- not part of public interface
privateSigning: ['true', 'True', 'TRUE', '1'].includes(
core.getInput('private-signing')
),
// internal only
batchSize: DEFAULT_BATCH_SIZE
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
run()
run(inputs)
+105 -104
View File
@@ -1,26 +1,25 @@
import { Attestation, Predicate, Subject, attest } from '@actions/attest'
import * as core from '@actions/core'
import * as github from '@actions/github'
import { attachArtifactToImage, getRegistryCredentials } from '@sigstore/oci'
import fs from 'fs'
import os from 'os'
import path from 'path'
import { AttestResult, SigstoreInstance, createAttestation } from './attest'
import { SEARCH_PUBLIC_GOOD_URL } from './endpoints'
import { predicateFromInputs } from './predicate'
import { subjectFromInputs } from './subject'
import { PredicateInputs, predicateFromInputs } from './predicate'
import * as style from './style'
import { SubjectInputs, subjectFromInputs } from './subject'
type SigstoreInstance = 'public-good' | 'github'
type AttestedSubject = { subject: Subject; attestationID: string }
const COLOR_CYAN = '\x1B[36m'
const COLOR_GRAY = '\x1B[38;5;244m'
const COLOR_DEFAULT = '\x1B[39m'
const ATTESTATION_FILE_NAME = 'attestation.jsonl'
const DELAY_INTERVAL_MS = 75
const DELAY_MAX_MS = 1200
const MAX_SUBJECT_COUNT = 64
const OCI_TIMEOUT = 2000
const OCI_RETRY = 3
export type RunInputs = SubjectInputs &
PredicateInputs & {
pushToRegistry: boolean
githubToken: string
privateSigning: boolean
batchSize: number
}
/* istanbul ignore next */
const logHandler = (level: string, ...args: unknown[]): void => {
@@ -34,7 +33,7 @@ const logHandler = (level: string, ...args: unknown[]): void => {
* The main function for the action.
* @returns {Promise<void>} Resolves when the action is complete.
*/
export async function run(): Promise<void> {
export async function run(inputs: RunInputs): Promise<void> {
process.on('log', logHandler)
// Provenance visibility will be public ONLY if we can confirm that the
@@ -42,61 +41,62 @@ export async function run(): Promise<void> {
// Otherwise, it will be private.
const sigstoreInstance: SigstoreInstance =
github.context.payload.repository?.visibility === 'public' &&
core.getInput('private-signing') !== 'true'
!inputs.privateSigning
? 'public-good'
: 'github'
try {
const atts: AttestedSubject[] = []
const atts: AttestResult[] = []
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
throw new Error(
'missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.'
)
}
// Gather list of subjets
const subjects = await subjectFromInputs()
if (subjects.length > MAX_SUBJECT_COUNT) {
throw new Error(
`Too many subjects specified. The maximum number of subjects is ${MAX_SUBJECT_COUNT}.`
)
}
const subjects = await subjectFromInputs({
...inputs,
downcaseName: inputs.pushToRegistry
})
const predicate = predicateFromInputs(inputs)
const predicate = predicateFromInputs()
const outputPath = path.join(tempDir(), ATTESTATION_FILE_NAME)
// Generate attestations for each subject serially
for (const subject of subjects) {
const att = await createAttestation(subject, predicate, sigstoreInstance)
// Write attestation bundle to output file
fs.writeFileSync(outputPath, JSON.stringify(att.bundle) + os.EOL, {
encoding: 'utf-8',
flag: 'a'
})
if (att.attestationID) {
atts.push({ subject, attestationID: att.attestationID })
}
}
if (atts.length > 0) {
core.summary.addHeading(
/* istanbul ignore next */
atts.length > 1 ? 'Attestations Created' : 'Attestation Created',
3
)
for (const { subject, attestationID } of atts) {
core.summary.addLink(
`${subject.name}@${subjectDigest(subject)}`,
attestationURL(attestationID)
)
}
core.summary.write()
}
core.setOutput('bundle-path', outputPath)
const subjectChunks = chunkArray(subjects, inputs.batchSize)
// Generate attestations for each subject serially, working in batches
for (let i = 0; i < subjectChunks.length; i++) {
if (subjectChunks.length > 1) {
core.info(`Processing subject batch ${i + 1}/${subjectChunks.length}`)
}
// Calculate the delay time for this batch
const delayTime = delay(i)
for (const subject of subjectChunks[i]) {
// Delay between attestations (only when chunk size > 1)
if (i > 0) {
await new Promise(resolve => setTimeout(resolve, delayTime))
}
const att = await createAttestation(subject, predicate, {
sigstoreInstance,
pushToRegistry: inputs.pushToRegistry,
githubToken: inputs.githubToken
})
atts.push(att)
logAttestation(att, sigstoreInstance)
// Write attestation bundle to output file
fs.writeFileSync(outputPath, JSON.stringify(att.bundle) + os.EOL, {
encoding: 'utf-8',
flag: 'a'
})
}
}
logSummary(atts)
} catch (err) {
// Fail the workflow run if an error occurs
core.setFailed(
@@ -108,7 +108,9 @@ export async function run(): Promise<void> {
if (err instanceof Error && 'cause' in err) {
const innerErr = err.cause
core.info(
mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`)
style.mute(
innerErr instanceof Error ? innerErr.toString() : `${innerErr}`
)
)
}
} finally {
@@ -116,27 +118,19 @@ export async function run(): Promise<void> {
}
}
const createAttestation = async (
subject: Subject,
predicate: Predicate,
// Log details about the attestation to the GitHub Actions run
const logAttestation = (
attestation: AttestResult,
sigstoreInstance: SigstoreInstance
): Promise<Attestation> => {
// Sign provenance w/ Sigstore
const attestation = await attest({
subjectName: subject.name,
subjectDigest: subject.digest,
predicateType: predicate.type,
predicate: predicate.params,
sigstore: sigstoreInstance,
token: core.getInput('github-token')
})
core.info(`Attestation created for ${subject.name}@${subjectDigest(subject)}`)
): void => {
core.info(
`Attestation created for ${attestation.subjectName}@${attestation.subjectDigest}`
)
const instanceName =
sigstoreInstance === 'public-good' ? 'Public Good' : 'GitHub'
core.startGroup(
highlight(
style.highlight(
`Attestation signed using certificate from ${instanceName} Sigstore instance`
)
)
@@ -145,43 +139,44 @@ const createAttestation = async (
if (attestation.tlogID) {
core.info(
highlight('Attestation signature uploaded to Rekor transparency log')
style.highlight(
'Attestation signature uploaded to Rekor transparency log'
)
)
core.info(`${SEARCH_PUBLIC_GOOD_URL}?logIndex=${attestation.tlogID}`)
}
if (attestation.attestationID) {
core.info(highlight('Attestation uploaded to repository'))
core.info(style.highlight('Attestation uploaded to repository'))
core.info(attestationURL(attestation.attestationID))
}
if (core.getBooleanInput('push-to-registry', { required: false })) {
const credentials = getRegistryCredentials(subject.name)
const artifact = await attachArtifactToImage({
credentials,
imageName: subject.name,
imageDigest: subjectDigest(subject),
artifact: Buffer.from(JSON.stringify(attestation.bundle)),
mediaType: attestation.bundle.mediaType,
annotations: {
'dev.sigstore.bundle.content': 'dsse-envelope',
'dev.sigstore.bundle.predicateType': core.getInput('predicate-type')
},
fetchOpts: { timeout: OCI_TIMEOUT, retry: OCI_RETRY }
})
core.info(highlight('Attestation uploaded to registry'))
core.info(`${subject.name}@${artifact.digest}`)
if (attestation.attestationDigest) {
core.info(style.highlight('Attestation uploaded to registry'))
core.info(`${attestation.subjectName}@${attestation.attestationDigest}`)
}
return attestation
}
// Emphasis string using ANSI color codes
const highlight = (str: string): string => `${COLOR_CYAN}${str}${COLOR_DEFAULT}`
// Attach summary information to the GitHub Actions run
const logSummary = (attestations: AttestResult[]): void => {
if (attestations.length > 0) {
core.summary.addHeading(
/* istanbul ignore next */
attestations.length > 1 ? 'Attestations Created' : 'Attestation Created',
3
)
// De-emphasize string using ANSI color codes
/* istanbul ignore next */
const mute = (str: string): string => `${COLOR_GRAY}${str}${COLOR_DEFAULT}`
for (const { subjectName, subjectDigest, attestationID } of attestations) {
if (attestationID) {
core.summary.addLink(
`${subjectName}@${subjectDigest}`,
attestationURL(attestationID)
)
}
}
core.summary.write()
}
}
const tempDir = (): string => {
const basePath = process.env['RUNNER_TEMP']
@@ -194,12 +189,18 @@ const tempDir = (): string => {
return fs.mkdtempSync(path.join(basePath, path.sep))
}
// Returns the subject's digest as a formatted string of the form
// "<algorithm>:<digest>".
const subjectDigest = (subject: Subject): string => {
const alg = Object.keys(subject.digest).sort()[0]
return `${alg}:${subject.digest[alg]}`
// Transforms an array into an array of arrays, each containing at most
// `chunkSize` elements.
const chunkArray = <T>(array: T[], chunkSize: number): T[][] => {
return Array.from(
{ length: Math.ceil(array.length / chunkSize) },
(_, index) => array.slice(index * chunkSize, (index + 1) * chunkSize)
)
}
// Calculate the delay time for a given iteration
const delay = (iteration: number): number =>
Math.min(DELAY_INTERVAL_MS * 2 ** iteration, DELAY_MAX_MS)
const attestationURL = (id: string): string =>
`${github.context.serverUrl}/${github.context.repo.owner}/${github.context.repo.repo}/attestations/${id}`
+40 -10
View File
@@ -1,26 +1,56 @@
import * as core from '@actions/core'
import fs from 'fs'
import type { Predicate } from '@actions/attest'
export type PredicateInputs = {
predicateType: string
predicate: string
predicatePath: string
}
const MAX_PREDICATE_SIZE_BYTES = 16 * 1024 * 1024
// Returns the predicate specified by the action's inputs. The predicate value
// may be specified as a path to a file or as a string.
export const predicateFromInputs = (): Predicate => {
const predicateType = core.getInput('predicate-type', { required: true })
const predicateStr = core.getInput('predicate', { required: false })
const predicatePath = core.getInput('predicate-path', { required: false })
export const predicateFromInputs = (inputs: PredicateInputs): Predicate => {
const { predicateType, predicate, predicatePath } = inputs
if (!predicatePath && !predicateStr) {
if (!predicateType) {
throw new Error('predicate-type must be provided')
}
if (!predicatePath && !predicate) {
throw new Error('One of predicate-path or predicate must be provided')
}
if (predicatePath && predicateStr) {
if (predicatePath && predicate) {
throw new Error('Only one of predicate-path or predicate may be provided')
}
const params = predicatePath
? fs.readFileSync(predicatePath, 'utf-8')
: predicateStr
let params: string = predicate
if (predicatePath) {
if (!fs.existsSync(predicatePath)) {
throw new Error(`predicate file not found: ${predicatePath}`)
}
/* istanbul ignore next */
if (fs.statSync(predicatePath).size > MAX_PREDICATE_SIZE_BYTES) {
throw new Error(
`predicate file exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`
)
}
params = fs.readFileSync(predicatePath, 'utf-8')
} else {
if (predicate.length > MAX_PREDICATE_SIZE_BYTES) {
throw new Error(
`predicate string exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`
)
}
params = predicate
}
return { type: predicateType, params: JSON.parse(params) }
}
+11
View File
@@ -0,0 +1,11 @@
const COLOR_CYAN = '\x1B[36m'
const COLOR_GRAY = '\x1B[38;5;244m'
const COLOR_DEFAULT = '\x1B[39m'
// Emphasis string using ANSI color codes
export const highlight = (str: string): string =>
`${COLOR_CYAN}${str}${COLOR_DEFAULT}`
// De-emphasize string using ANSI color codes
export const mute = (str: string): string =>
`${COLOR_GRAY}${str}${COLOR_DEFAULT}`
+36 -26
View File
@@ -1,4 +1,3 @@
import * as core from '@actions/core'
import * as glob from '@actions/glob'
import crypto from 'crypto'
import { parse } from 'csv-parse/sync'
@@ -7,19 +6,23 @@ import path from 'path'
import type { Subject } from '@actions/attest'
const MAX_SUBJECT_COUNT = 2500
const DIGEST_ALGORITHM = 'sha256'
export type SubjectInputs = {
subjectPath: string
subjectName: string
subjectDigest: string
downcaseName?: boolean
}
// Returns the subject specified by the action's inputs. The subject may be
// specified as a path to a file or as a digest. If a path is provided, the
// file's digest is calculated and returned along with the subject's name. If a
// digest is provided, the name must also be provided.
export const subjectFromInputs = async (): Promise<Subject[]> => {
const subjectPath = core.getInput('subject-path', { required: false })
const subjectDigest = core.getInput('subject-digest', { required: false })
const subjectName = core.getInput('subject-name', { required: false })
const pushToRegistry = core.getBooleanInput('push-to-registry', {
required: false
})
export const subjectFromInputs = async (
inputs: SubjectInputs
): Promise<Subject[]> => {
const { subjectPath, subjectDigest, subjectName, downcaseName } = inputs
if (!subjectPath && !subjectDigest) {
throw new Error('One of subject-path or subject-digest must be provided')
@@ -37,7 +40,7 @@ export const subjectFromInputs = async (): Promise<Subject[]> => {
// If push-to-registry is enabled, ensure the subject name is lowercase
// to conform to OCI image naming conventions
const name = pushToRegistry ? subjectName.toLowerCase() : subjectName
const name = downcaseName ? subjectName.toLowerCase() : subjectName
if (subjectPath) {
return await getSubjectFromPath(subjectPath, name)
@@ -52,34 +55,41 @@ const getSubjectFromPath = async (
subjectPath: string,
subjectName?: string
): Promise<Subject[]> => {
const subjects: Subject[] = []
const digestedSubjects: Subject[] = []
const files: string[] = []
// Parse the list of subject paths
const subjectPaths = parseList(subjectPath)
// Expand the globbed paths to a list of files
for (const subPath of subjectPaths) {
// Expand the globbed path to a list of files
/* eslint-disable-next-line github/no-then */
const files = await glob.create(subPath).then(async g => g.glob())
for (const file of files) {
// Skip anything that is NOT a file
if (!fs.statSync(file).isFile()) {
continue
}
const name = subjectName || path.parse(file).base
const digest = await digestFile(DIGEST_ALGORITHM, file)
subjects.push({ name, digest: { [DIGEST_ALGORITHM]: digest } })
}
files.push(...(await glob.create(subPath).then(async g => g.glob())))
}
if (subjects.length === 0) {
if (files.length > MAX_SUBJECT_COUNT) {
throw new Error(
`Too many subjects specified. The maximum number of subjects is ${MAX_SUBJECT_COUNT}.`
)
}
for (const file of files) {
// Skip anything that is NOT a file
if (!fs.statSync(file).isFile()) {
continue
}
const name = subjectName || path.parse(file).base
const digest = await digestFile(DIGEST_ALGORITHM, file)
digestedSubjects.push({ name, digest: { [DIGEST_ALGORITHM]: digest } })
}
if (digestedSubjects.length === 0) {
throw new Error(`Could not find subject at path ${subjectPath}`)
}
return Promise.all(subjects)
return digestedSubjects
}
// Returns the subject specified by the digest of a file. The digest is returned