10 Commits

Author SHA1 Message Date
Brian DeHamer
12c083815e Bump @sigstore/oci from 0.3.2 to 0.3.3 (#66)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-16 11:25:01 -07:00
Brian DeHamer
38ff958ab6 downcase subject name for OCI images (#63)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-14 08:11:10 -07:00
dependabot[bot]
60d0be1445 Bump the npm-development group with 4 updates (#64)
Bumps the npm-development group with 4 updates: [@sigstore/mock](https://github.com/sigstore/sigstore-js), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@sigstore/mock` from 0.7.2 to 0.7.3
- [Release notes](https://github.com/sigstore/sigstore-js/releases)
- [Commits](https://github.com/sigstore/sigstore-js/compare/@sigstore/mock@0.7.2...@sigstore/mock@0.7.3)

Updates `@types/node` from 20.12.10 to 20.12.11
- [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.8.0 to 7.9.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.9.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 7.8.0 to 7.9.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.9.0/packages/parser)

---
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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 16:48:00 -07:00
dependabot[bot]
32f49af665 Bump the npm-development group with 3 updates (#55)
* Bump the npm-development group with 3 updates

Bumps the npm-development group with 3 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) and [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli).


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

Updates `eslint-plugin-jest` from 28.3.0 to 28.5.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.3.0...v28.5.0)

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

---
updated-dependencies:
- dependency-name: "@types/node"
  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: markdownlint-cli
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
...

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

* re-generate 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-10 08:12:16 -07:00
Brian DeHamer
3f67a24e31 bump @sigstore/oci from 0.3.0 to 0.3.2 (#61)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-10 08:10:56 -07:00
Brian DeHamer
e259ee2285 prep 1.1.1 release (#60)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-09 12:34:33 -07:00
Brian DeHamer
58fa41a101 send api errors to gha debug log (#59)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-09 12:34:14 -07:00
Brian DeHamer
b0d8b47eb7 include more detail in error logging (#58)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-09 12:34:01 -07:00
Brian DeHamer
9b22bf5c9f bump @sigstore/sign from 2.3.0 to 2.3.1 (#57)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-09 12:33:44 -07:00
Brian DeHamer
9cbbc78ff9 update release documentation (#56)
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-05-09 12:33:29 -07:00
10 changed files with 883 additions and 620 deletions

View File

@@ -175,7 +175,7 @@ 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 "index.docker.io" as the
> **NOTE**: When pushing to Docker Hub, please use "docker.io" as the
> registry portion of the image name.
```yaml

View File

@@ -11,6 +11,14 @@ Follow the steps below to tag a new release for the `actions/attest` action.
gh release create vX.X.X
```
1. Move (or create) the major version tag to point to the same commit tagged
above:
```shell
git tag -fa vX -m "vX"
git push origin vX --force
```
1. As appropriate, update any actions like
[`actions/attest-build-provenance`](https://github.com/actions/attest-build-provenance)
and [`actions/attest-sbom`](https://github.com/actions/attest-sbom) which

View File

@@ -116,7 +116,9 @@ describe('action', () => {
expect(runMock).toHaveReturned()
expect(setFailedMock).toHaveBeenCalledWith(
expect.stringMatching(/missing "id-token" permission/)
new Error(
'missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.'
)
)
})
})
@@ -131,9 +133,7 @@ describe('action', () => {
expect(runMock).toHaveReturned()
expect(setFailedMock).toHaveBeenCalledWith(
expect.stringMatching(
/one of subject-path or subject-digest must be provided/i
)
new Error('One of subject-path or subject-digest must be provided')
)
})
})
@@ -330,7 +330,9 @@ describe('action', () => {
expect(runMock).toHaveReturned()
expect(setFailedMock).toHaveBeenCalledWith(
'Too many subjects specified. The maximum number of subjects is 64.'
new Error(
'Too many subjects specified. The maximum number of subjects is 64.'
)
)
})
})

View File

@@ -5,6 +5,10 @@ import path from 'path'
import { subjectFromInputs } from '../src/subject'
describe('subjectFromInputs', () => {
beforeEach(() => {
process.env['INPUT_PUSH-TO-REGISTRY'] = 'false'
})
afterEach(() => {
process.env['INPUT_SUBJECT-PATH'] = ''
process.env['INPUT_SUBJECT-DIGEST'] = ''
@@ -45,12 +49,12 @@ describe('subjectFromInputs', () => {
})
describe('when specifying a subject digest', () => {
const name = 'subject'
const name = 'Subject'
describe('when the digest is malformed', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = 'digest'
process.env['INPUT_SUBJECT-NAME'] = 'subject'
process.env['INPUT_SUBJECT-NAME'] = name
})
it('throws an error', async () => {
@@ -63,7 +67,7 @@ describe('subjectFromInputs', () => {
describe('when the alogrithm is not supported', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = 'md5:deadbeef'
process.env['INPUT_SUBJECT-NAME'] = 'subject'
process.env['INPUT_SUBJECT-NAME'] = name
})
it('throws an error', async () => {
@@ -76,7 +80,7 @@ describe('subjectFromInputs', () => {
describe('when the sha256 digest is malformed', () => {
beforeEach(() => {
process.env['INPUT_SUBJECT-DIGEST'] = 'sha256:deadbeef'
process.env['INPUT_SUBJECT-NAME'] = 'subject'
process.env['INPUT_SUBJECT-NAME'] = name
})
it('throws an error', async () => {
@@ -105,6 +109,28 @@ describe('subjectFromInputs', () => {
expect(subject[0].digest).toEqual({ [alg]: digest })
})
})
describe('when the push-to-registry 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()
expect(subject).toBeDefined()
expect(subject).toHaveLength(1)
expect(subject[0].name).toEqual(imageName.toLowerCase())
expect(subject[0].digest).toEqual({ [alg]: digest })
})
})
})
describe('when specifying a subject path', () => {

643
dist/index.js generated vendored
View File

@@ -11433,7 +11433,7 @@ exports.SignedCertificateTimestamp = SignedCertificateTimestamp;
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.HEADER_OCI_SUBJECT = exports.HEADER_LOCATION = exports.HEADER_IF_MATCH = exports.HEADER_ETAG = exports.HEADER_DIGEST = exports.HEADER_CONTENT_TYPE = exports.HEADER_CONTENT_LENGTH = exports.HEADER_AUTHORIZATION = exports.HEADER_AUTHENTICATE = exports.HEADER_API_VERSION = exports.HEADER_ACCEPT = exports.CONTENT_TYPE_EMPTY_DESCRIPTOR = exports.CONTENT_TYPE_OCTET_STREAM = exports.CONTENT_TYPE_OCI_MANIFEST = exports.CONTENT_TYPE_OCI_INDEX = void 0;
exports.HEADER_OCI_SUBJECT = exports.HEADER_LOCATION = exports.HEADER_IF_MATCH = exports.HEADER_ETAG = exports.HEADER_DIGEST = exports.HEADER_CONTENT_TYPE = exports.HEADER_CONTENT_LENGTH = exports.HEADER_AUTHORIZATION = exports.HEADER_AUTHENTICATE = exports.HEADER_API_VERSION = exports.HEADER_ACCEPT = exports.CONTENT_TYPE_EMPTY_DESCRIPTOR = exports.CONTENT_TYPE_OCTET_STREAM = exports.CONTENT_TYPE_DOCKER_MANIFEST_LIST = exports.CONTENT_TYPE_DOCKER_MANIFEST = exports.CONTENT_TYPE_OCI_MANIFEST = exports.CONTENT_TYPE_OCI_INDEX = void 0;
/*
Copyright 2023 The Sigstore Authors.
@@ -11451,6 +11451,8 @@ limitations under the License.
*/
exports.CONTENT_TYPE_OCI_INDEX = 'application/vnd.oci.image.index.v1+json';
exports.CONTENT_TYPE_OCI_MANIFEST = 'application/vnd.oci.image.manifest.v1+json';
exports.CONTENT_TYPE_DOCKER_MANIFEST = 'application/vnd.docker.distribution.manifest.v2+json';
exports.CONTENT_TYPE_DOCKER_MANIFEST_LIST = 'application/vnd.docker.distribution.manifest.list.v2+json';
exports.CONTENT_TYPE_OCTET_STREAM = 'application/octet-stream';
exports.CONTENT_TYPE_EMPTY_DESCRIPTOR = 'application/vnd.oci.empty.v1+json';
exports.HEADER_ACCEPT = 'Accept';
@@ -11575,6 +11577,96 @@ class OCIError extends Error {
exports.OCIError = OCIError;
/***/ }),
/***/ 437:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
/*
Copyright 2024 The Sigstore Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const http2_1 = __nccwpck_require__(85158);
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
const proc_log_1 = __nccwpck_require__(56528);
const promise_retry_1 = __importDefault(__nccwpck_require__(54742));
const { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_TOO_MANY_REQUESTS, HTTP_STATUS_REQUEST_TIMEOUT, } = http2_1.constants;
const fetchWithRetry = async (url, options = {}) => {
return (0, promise_retry_1.default)(async (retry, attemptNum) => {
/* eslint-disable @typescript-eslint/no-explicit-any */
const logRetry = (reason) => {
proc_log_1.log.http('fetch', `${options.method} ${url} attempt ${attemptNum} failed with ${reason}`);
};
const response = await (0, make_fetch_happen_1.default)(url, {
...options,
retry: false, // We're handling retries ourselves
}).catch((reason) => {
logRetry(reason);
return retry(reason);
});
if (retryable(response.status)) {
logRetry(response.status);
return retry(response);
}
return response;
}, retryOpts(options.retry)).catch((err) => {
// If we got an actual error, throw it
if (err instanceof Error) {
throw err;
}
// Otherwise, return the response (this is simply a retry-able response for
// which we exceeded the retry limit)
return err;
});
};
// Returns a wrapped fetch function with default options
fetchWithRetry.defaults = (defaultOptions = {}, wrappedFetch = fetchWithRetry) => {
const defaultedFetch = (url, options = {}) => {
const finalOptions = {
...defaultOptions,
...options,
headers: { ...defaultOptions.headers, ...options.headers },
};
return wrappedFetch(url, finalOptions);
};
defaultedFetch.defaults = (newDefaults = {}) => fetchWithRetry.defaults(newDefaults, defaultedFetch);
return defaultedFetch;
};
// Determine if a status code is retryable. This includes 5xx errors, 408, and
// 429.
const retryable = (status) => [HTTP_STATUS_REQUEST_TIMEOUT, HTTP_STATUS_TOO_MANY_REQUESTS].includes(status) || status >= HTTP_STATUS_INTERNAL_SERVER_ERROR;
// Normalize the retry options to the format expected by promise-retry
const retryOpts = (retry) => {
if (typeof retry === 'boolean') {
return { retries: retry ? 1 : 0 };
}
else if (typeof retry === 'number') {
return { retries: retry };
}
else {
return { retries: 0, ...retry };
}
};
exports["default"] = fetchWithRetry;
/***/ }),
/***/ 79539:
@@ -11614,13 +11706,14 @@ limitations under the License.
const constants_1 = __nccwpck_require__(61319);
const error_1 = __nccwpck_require__(60064);
const registry_1 = __nccwpck_require__(27464);
const DOCKER_DEFAULT_REGISTRY = 'registry-1.docker.io';
const EMPTY_BLOB = Buffer.from('{}');
class OCIImage {
constructor(image, creds, opts) {
_OCIImage_instances.add(this);
_OCIImage_client.set(this, void 0);
_OCIImage_credentials.set(this, void 0);
__classPrivateFieldSet(this, _OCIImage_client, new registry_1.RegistryClient(image.registry, image.path, opts), "f");
__classPrivateFieldSet(this, _OCIImage_client, new registry_1.RegistryClient(canonicalizeRegistryName(image.registry), image.path, opts), "f");
__classPrivateFieldSet(this, _OCIImage_credentials, creds, "f");
}
async addArtifact(opts) {
@@ -11750,6 +11843,13 @@ const newIndex = () => ({
const digestToTag = (digest) => {
return digest.replace(':', '-');
};
// Canonicalize the registry name to match the format used by the registry
// client. This is used primarily to handle the special case of the Docker Hub
// registry.
// https://github.com/moby/moby/blob/v24.0.2/registry/config.go#L25-L48
const canonicalizeRegistryName = (registry) => {
return registry.endsWith('docker.io') ? DOCKER_DEFAULT_REGISTRY : registry;
};
/***/ }),
@@ -11869,11 +11969,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
const node_crypto_1 = __importDefault(__nccwpck_require__(6005));
const constants_1 = __nccwpck_require__(61319);
const credentials_1 = __nccwpck_require__(95475);
const error_1 = __nccwpck_require__(60064);
const fetch_1 = __importDefault(__nccwpck_require__(437));
const ALL_MANIFEST_MEDIA_TYPES = [
constants_1.CONTENT_TYPE_OCI_INDEX,
constants_1.CONTENT_TYPE_OCI_MANIFEST,
constants_1.CONTENT_TYPE_DOCKER_MANIFEST,
constants_1.CONTENT_TYPE_DOCKER_MANIFEST_LIST,
].join(',');
class RegistryClient {
constructor(registry, repository, opts) {
_RegistryClient_instances.add(this);
@@ -11881,7 +11987,7 @@ class RegistryClient {
_RegistryClient_repository.set(this, void 0);
_RegistryClient_fetch.set(this, void 0);
__classPrivateFieldSet(this, _RegistryClient_repository, repository, "f");
__classPrivateFieldSet(this, _RegistryClient_fetch, make_fetch_happen_1.default.defaults(opts), "f");
__classPrivateFieldSet(this, _RegistryClient_fetch, fetch_1.default.defaults(opts), "f");
// Use http for localhost registries, https otherwise
const hostname = new URL(`http://${registry}`).hostname;
/* istanbul ignore next */
@@ -11968,9 +12074,7 @@ class RegistryClient {
async checkManifest(reference) {
const response = await __classPrivateFieldGet(this, _RegistryClient_fetch, "f").call(this, `${__classPrivateFieldGet(this, _RegistryClient_baseURL, "f")}/v2/${__classPrivateFieldGet(this, _RegistryClient_repository, "f")}/manifests/${reference}`, {
method: 'HEAD',
headers: {
[constants_1.HEADER_ACCEPT]: `${constants_1.CONTENT_TYPE_OCI_MANIFEST},${constants_1.CONTENT_TYPE_OCI_INDEX}`,
},
headers: { [constants_1.HEADER_ACCEPT]: ALL_MANIFEST_MEDIA_TYPES },
}).then((0, error_1.ensureStatus)(200));
const mediaType = response.headers.get(constants_1.HEADER_CONTENT_TYPE) ||
/* istanbul ignore next */ '';
@@ -11982,9 +12086,7 @@ class RegistryClient {
// Retrieves a manifest by reference
async getManifest(reference) {
const response = await __classPrivateFieldGet(this, _RegistryClient_fetch, "f").call(this, `${__classPrivateFieldGet(this, _RegistryClient_baseURL, "f")}/v2/${__classPrivateFieldGet(this, _RegistryClient_repository, "f")}/manifests/${reference}`, {
headers: {
[constants_1.HEADER_ACCEPT]: `${constants_1.CONTENT_TYPE_OCI_MANIFEST},${constants_1.CONTENT_TYPE_OCI_INDEX}`,
},
headers: { [constants_1.HEADER_ACCEPT]: ALL_MANIFEST_MEDIA_TYPES },
}).then((0, error_1.ensureStatus)(200));
const body = await response.json();
const mediaType = response.headers.get(constants_1.HEADER_CONTENT_TYPE) ||
@@ -13923,8 +14025,23 @@ exports.internalError = internalError;
"use strict";
/*
Copyright 2023 The Sigstore Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.checkStatus = exports.HTTPError = void 0;
exports.HTTPError = void 0;
class HTTPError extends Error {
constructor({ status, message, location, }) {
super(`(${status}) ${message}`);
@@ -13933,38 +14050,11 @@ class HTTPError extends Error {
}
}
exports.HTTPError = HTTPError;
const checkStatus = async (response) => {
if (response.ok) {
return response;
}
else {
let message = response.statusText;
const location = response.headers?.get('Location') || undefined;
const contentType = response.headers?.get('Content-Type');
// If response type is JSON, try to parse the body for a message
if (contentType?.includes('application/json')) {
try {
await response.json().then((body) => {
message = body.message;
});
}
catch (e) {
// ignore
}
}
throw new HTTPError({
status: response.status,
message: message,
location: location,
});
}
};
exports.checkStatus = checkStatus;
/***/ }),
/***/ 62960:
/***/ 78509:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
@@ -13972,6 +14062,110 @@ exports.checkStatus = checkStatus;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.fetchWithRetry = void 0;
/*
Copyright 2023 The Sigstore Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const http2_1 = __nccwpck_require__(85158);
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
const proc_log_1 = __nccwpck_require__(56528);
const promise_retry_1 = __importDefault(__nccwpck_require__(54742));
const util_1 = __nccwpck_require__(90724);
const error_1 = __nccwpck_require__(11294);
const { HTTP2_HEADER_LOCATION, HTTP2_HEADER_CONTENT_TYPE, HTTP2_HEADER_USER_AGENT, HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_TOO_MANY_REQUESTS, HTTP_STATUS_REQUEST_TIMEOUT, } = http2_1.constants;
async function fetchWithRetry(url, options) {
return (0, promise_retry_1.default)(async (retry, attemptNum) => {
const method = options.method || 'POST';
const headers = {
[HTTP2_HEADER_USER_AGENT]: util_1.ua.getUserAgent(),
...options.headers,
};
const response = await (0, make_fetch_happen_1.default)(url, {
method,
headers,
body: options.body,
timeout: options.timeout,
retry: false, // We're handling retries ourselves
}).catch((reason) => {
proc_log_1.log.http('fetch', `${method} ${url} attempt ${attemptNum} failed with ${reason}`);
return retry(reason);
});
if (response.ok) {
return response;
}
else {
const error = await errorFromResponse(response);
proc_log_1.log.http('fetch', `${method} ${url} attempt ${attemptNum} failed with ${response.status}`);
if (retryable(response.status)) {
return retry(error);
}
else {
throw error;
}
}
}, retryOpts(options.retry));
}
exports.fetchWithRetry = fetchWithRetry;
// Translate a Response into an HTTPError instance. This will attempt to parse
// the response body for a message, but will default to the statusText if none
// is found.
const errorFromResponse = async (response) => {
let message = response.statusText;
const location = response.headers?.get(HTTP2_HEADER_LOCATION) || undefined;
const contentType = response.headers?.get(HTTP2_HEADER_CONTENT_TYPE);
// If response type is JSON, try to parse the body for a message
if (contentType?.includes('application/json')) {
try {
const body = await response.json();
message = body.message || message;
}
catch (e) {
// ignore
}
}
return new error_1.HTTPError({
status: response.status,
message: message,
location: location,
});
};
// Determine if a status code is retryable. This includes 5xx errors, 408, and
// 429.
const retryable = (status) => [HTTP_STATUS_REQUEST_TIMEOUT, HTTP_STATUS_TOO_MANY_REQUESTS].includes(status) || status >= HTTP_STATUS_INTERNAL_SERVER_ERROR;
// Normalize the retry options to the format expected by promise-retry
const retryOpts = (retry) => {
if (typeof retry === 'boolean') {
return { retries: retry ? 1 : 0 };
}
else if (typeof retry === 'number') {
return { retries: retry };
}
else {
return { retries: 0, ...retry };
}
};
/***/ }),
/***/ 62960:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Fulcio = void 0;
/*
@@ -13989,33 +14183,26 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
const util_1 = __nccwpck_require__(90724);
const error_1 = __nccwpck_require__(11294);
const fetch_1 = __nccwpck_require__(78509);
/**
* Fulcio API client.
*/
class Fulcio {
constructor(options) {
this.fetch = make_fetch_happen_1.default.defaults({
retry: options.retry,
timeout: options.timeout,
headers: {
'Content-Type': 'application/json',
'User-Agent': util_1.ua.getUserAgent(),
},
});
this.baseUrl = options.baseURL;
this.options = options;
}
async createSigningCertificate(request) {
const url = `${this.baseUrl}/api/v2/signingCert`;
const response = await this.fetch(url, {
method: 'POST',
const { baseURL, retry, timeout } = this.options;
const url = `${baseURL}/api/v2/signingCert`;
const response = await (0, fetch_1.fetchWithRetry)(url, {
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
timeout,
retry,
});
await (0, error_1.checkStatus)(response);
const data = await response.json();
return data;
return response.json();
}
}
exports.Fulcio = Fulcio;
@@ -14024,13 +14211,10 @@ exports.Fulcio = Fulcio;
/***/ }),
/***/ 56205:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Rekor = void 0;
/*
@@ -14048,23 +14232,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
const util_1 = __nccwpck_require__(90724);
const error_1 = __nccwpck_require__(11294);
const fetch_1 = __nccwpck_require__(78509);
/**
* Rekor API client.
*/
class Rekor {
constructor(options) {
this.fetch = make_fetch_happen_1.default.defaults({
retry: options.retry,
timeout: options.timeout,
headers: {
Accept: 'application/json',
'User-Agent': util_1.ua.getUserAgent(),
},
});
this.baseUrl = options.baseURL;
this.options = options;
}
/**
* Create a new entry in the Rekor log.
@@ -14072,13 +14246,17 @@ class Rekor {
* @returns {Promise<Entry>} The created entry
*/
async createEntry(propsedEntry) {
const url = `${this.baseUrl}/api/v1/log/entries`;
const response = await this.fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
const { baseURL, timeout, retry } = this.options;
const url = `${baseURL}/api/v1/log/entries`;
const response = await (0, fetch_1.fetchWithRetry)(url, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify(propsedEntry),
timeout,
retry,
});
await (0, error_1.checkStatus)(response);
const data = await response.json();
return entryFromResponse(data);
}
@@ -14088,45 +14266,19 @@ class Rekor {
* @returns {Promise<Entry>} The retrieved entry
*/
async getEntry(uuid) {
const url = `${this.baseUrl}/api/v1/log/entries/${uuid}`;
const response = await this.fetch(url);
await (0, error_1.checkStatus)(response);
const { baseURL, timeout, retry } = this.options;
const url = `${baseURL}/api/v1/log/entries/${uuid}`;
const response = await (0, fetch_1.fetchWithRetry)(url, {
method: 'GET',
headers: {
Accept: 'application/json',
},
timeout,
retry,
});
const data = await response.json();
return entryFromResponse(data);
}
/**
* Search the Rekor log index for entries matching the given query.
* @param opts {SearchIndex} Options to search the Rekor log
* @returns {Promise<string[]>} UUIDs of matching entries
*/
async searchIndex(opts) {
const url = `${this.baseUrl}/api/v1/index/retrieve`;
const response = await this.fetch(url, {
method: 'POST',
body: JSON.stringify(opts),
headers: { 'Content-Type': 'application/json' },
});
await (0, error_1.checkStatus)(response);
const data = await response.json();
return data;
}
/**
* Search the Rekor logs for matching the given query.
* @param opts {SearchLogQuery} Query to search the Rekor log
* @returns {Promise<Entry[]>} List of matching entries
*/
async searchLog(opts) {
const url = `${this.baseUrl}/api/v1/log/entries/retrieve`;
const response = await this.fetch(url, {
method: 'POST',
body: JSON.stringify(opts),
headers: { 'Content-Type': 'application/json' },
});
await (0, error_1.checkStatus)(response);
const rawData = await response.json();
const data = rawData.map((d) => entryFromResponse(d));
return data;
}
}
exports.Rekor = Rekor;
// Unpack the response from the Rekor API into a more convenient format.
@@ -14147,13 +14299,10 @@ function entryFromResponse(data) {
/***/ }),
/***/ 82759:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.TimestampAuthority = void 0;
/*
@@ -14171,28 +14320,22 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const make_fetch_happen_1 = __importDefault(__nccwpck_require__(9525));
const util_1 = __nccwpck_require__(90724);
const error_1 = __nccwpck_require__(11294);
const fetch_1 = __nccwpck_require__(78509);
class TimestampAuthority {
constructor(options) {
this.fetch = make_fetch_happen_1.default.defaults({
retry: options.retry,
timeout: options.timeout,
headers: {
'Content-Type': 'application/json',
'User-Agent': util_1.ua.getUserAgent(),
},
});
this.baseUrl = options.baseURL;
this.options = options;
}
async createTimestamp(request) {
const url = `${this.baseUrl}/api/v1/timestamp`;
const response = await this.fetch(url, {
method: 'POST',
const { baseURL, timeout, retry } = this.options;
const url = `${baseURL}/api/v1/timestamp`;
const response = await (0, fetch_1.fetchWithRetry)(url, {
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
timeout,
retry,
});
await (0, error_1.checkStatus)(response);
return response.buffer();
}
}
@@ -40919,6 +41062,8 @@ class CacheEntry {
const cacheWritePromise = new Promise((resolve, reject) => {
cacheWriteResolve = resolve
cacheWriteReject = reject
}).catch((err) => {
body.emit('error', err)
})
body = new CachingMinipassPipeline({ events: ['integrity', 'size'] }, new MinipassFlush({
@@ -41673,6 +41818,7 @@ const { Minipass } = __nccwpck_require__(14968)
const fetch = __nccwpck_require__(68998)
const promiseRetry = __nccwpck_require__(54742)
const ssri = __nccwpck_require__(4406)
const { log } = __nccwpck_require__(56528)
const CachingMinipassPipeline = __nccwpck_require__(61064)
const { getAgent } = __nccwpck_require__(79907)
@@ -41760,6 +41906,8 @@ const remoteFetch = (request, options) => {
options.onRetry(res)
}
/* eslint-disable-next-line max-len */
log.http('fetch', `${req.method} ${req.url} attempt ${attemptNum} failed with ${res.status}`)
return retryHandler(res)
}
@@ -41783,6 +41931,7 @@ const remoteFetch = (request, options) => {
options.onRetry(err)
}
log.http('fetch', `${req.method} ${req.url} attempt ${attemptNum} failed with ${err.code}`)
return retryHandler(err)
}
}, options.retry).catch((err) => {
@@ -49022,6 +49171,166 @@ module.exports = async (
};
/***/ }),
/***/ 56528:
/***/ ((module) => {
const META = Symbol('proc-log.meta')
module.exports = {
META: META,
output: {
LEVELS: [
'standard',
'error',
'buffer',
'flush',
],
KEYS: {
standard: 'standard',
error: 'error',
buffer: 'buffer',
flush: 'flush',
},
standard: function (...args) {
return process.emit('output', 'standard', ...args)
},
error: function (...args) {
return process.emit('output', 'error', ...args)
},
buffer: function (...args) {
return process.emit('output', 'buffer', ...args)
},
flush: function (...args) {
return process.emit('output', 'flush', ...args)
},
},
log: {
LEVELS: [
'notice',
'error',
'warn',
'info',
'verbose',
'http',
'silly',
'timing',
'pause',
'resume',
],
KEYS: {
notice: 'notice',
error: 'error',
warn: 'warn',
info: 'info',
verbose: 'verbose',
http: 'http',
silly: 'silly',
timing: 'timing',
pause: 'pause',
resume: 'resume',
},
error: function (...args) {
return process.emit('log', 'error', ...args)
},
notice: function (...args) {
return process.emit('log', 'notice', ...args)
},
warn: function (...args) {
return process.emit('log', 'warn', ...args)
},
info: function (...args) {
return process.emit('log', 'info', ...args)
},
verbose: function (...args) {
return process.emit('log', 'verbose', ...args)
},
http: function (...args) {
return process.emit('log', 'http', ...args)
},
silly: function (...args) {
return process.emit('log', 'silly', ...args)
},
timing: function (...args) {
return process.emit('log', 'timing', ...args)
},
pause: function () {
return process.emit('log', 'pause')
},
resume: function () {
return process.emit('log', 'resume')
},
},
time: {
LEVELS: [
'start',
'end',
],
KEYS: {
start: 'start',
end: 'end',
},
start: function (name, fn) {
process.emit('time', 'start', name)
function end () {
return process.emit('time', 'end', name)
}
if (typeof fn === 'function') {
const res = fn()
if (res && res.finally) {
return res.finally(end)
}
end()
return res
}
return end
},
end: function (name) {
return process.emit('time', 'end', name)
},
},
input: {
LEVELS: [
'start',
'end',
'read',
],
KEYS: {
start: 'start',
end: 'end',
read: 'read',
},
start: function (fn) {
process.emit('input', 'start')
function end () {
return process.emit('input', 'end')
}
if (typeof fn === 'function') {
const res = fn()
if (res && res.finally) {
return res.finally(end)
}
end()
return res
}
return end
},
end: function () {
return process.emit('input', 'end')
},
read: function (...args) {
let resolve, reject
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve
reject = _reject
})
process.emit('input', 'read', resolve, reject, ...args)
return promise
},
},
}
/***/ }),
/***/ 54742:
@@ -79642,14 +79951,25 @@ const endpoints_1 = __nccwpck_require__(69112);
const predicate_1 = __nccwpck_require__(72103);
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;
/* istanbul ignore next */
const logHandler = (level, ...args) => {
// Send any HTTP-related log events to the GitHub Actions debug log
if (level === 'http') {
core.debug(args.join(' '));
}
};
/**
* The main function for the action.
* @returns {Promise<void>} Resolves when the action is complete.
*/
async function run() {
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.
@@ -79694,13 +80014,17 @@ async function run() {
}
catch (err) {
// Fail the workflow run if an error occurs
core.setFailed(err instanceof Error ? err.message : /* istanbul ignore next */ `${err}`);
core.setFailed(err instanceof Error ? err : /* istanbul ignore next */ `${err}`);
// Log the cause of the error if one is available
/* istanbul ignore if */
if (err instanceof Error && 'cause' in err) {
const innerErr = err.cause;
core.debug(innerErr instanceof Error ? innerErr.message : `${innerErr}}`);
core.info(mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`));
}
}
finally {
process.removeListener('log', logHandler);
}
}
exports.run = run;
const createAttestation = async (subject, predicate, sigstoreInstance) => {
@@ -79737,14 +80061,19 @@ const createAttestation = async (subject, predicate, sigstoreInstance) => {
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}`);
}
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 */
@@ -79869,6 +80198,9 @@ 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
});
if (!subjectPath && !subjectDigest) {
throw new Error('One of subject-path or subject-digest must be provided');
}
@@ -79878,11 +80210,14 @@ const subjectFromInputs = async () => {
if (subjectDigest && !subjectName) {
throw new Error('subject-name must be provided when using subject-digest');
}
// 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;
if (subjectPath) {
return await getSubjectFromPath(subjectPath, subjectName);
return await getSubjectFromPath(subjectPath, name);
}
else {
return [getSubjectFromDigest(subjectDigest, subjectName)];
return [getSubjectFromDigest(subjectDigest, name)];
}
};
exports.subjectFromInputs = subjectFromInputs;
@@ -83326,7 +83661,7 @@ exports.LRUCache = LRUCache;
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Glob = void 0;
const minimatch_1 = __nccwpck_require__(7111);
const path_scurry_1 = __nccwpck_require__(69569);
const path_scurry_1 = __nccwpck_require__(51081);
const url_1 = __nccwpck_require__(57310);
const pattern_js_1 = __nccwpck_require__(92895);
const walker_js_1 = __nccwpck_require__(45548);
@@ -89034,7 +89369,7 @@ exports.Minipass = Minipass;
/***/ }),
/***/ 69569:
/***/ 51081:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
@@ -89115,21 +89450,21 @@ const IFMT = 0b1111;
// mask to unset low 4 bits
const IFMT_UNKNOWN = ~IFMT;
// set after successfully calling readdir() and getting entries.
const READDIR_CALLED = 16;
const READDIR_CALLED = 0b0000_0001_0000;
// set after a successful lstat()
const LSTAT_CALLED = 32;
const LSTAT_CALLED = 0b0000_0010_0000;
// set if an entry (or one of its parents) is definitely not a dir
const ENOTDIR = 64;
const ENOTDIR = 0b0000_0100_0000;
// set if an entry (or one of its parents) does not exist
// (can also be set on lstat errors like EACCES or ENAMETOOLONG)
const ENOENT = 128;
const ENOENT = 0b0000_1000_0000;
// cannot have child entries -- also verify &IFMT is either IFDIR or IFLNK
// set if we fail to readlink
const ENOREADLINK = 256;
const ENOREADLINK = 0b0001_0000_0000;
// set if we know realpath() will fail
const ENOREALPATH = 512;
const ENOREALPATH = 0b0010_0000_0000;
const ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
const TYPEMASK = 1023;
const TYPEMASK = 0b0011_1111_1111;
const entToType = (s) => s.isFile()
? IFREG
: s.isDirectory()
@@ -89743,7 +90078,7 @@ class PathBase {
/* c8 ignore stop */
try {
const read = await this.#fs.promises.readlink(this.fullpath());
const linkTarget = this.parent.resolve(read);
const linkTarget = (await this.parent.realpath())?.resolve(read);
if (linkTarget) {
return (this.#linkTarget = linkTarget);
}
@@ -89772,7 +90107,7 @@ class PathBase {
/* c8 ignore stop */
try {
const read = this.#fs.readlinkSync(this.fullpath());
const linkTarget = this.parent.resolve(read);
const linkTarget = (this.parent.realpathSync())?.resolve(read);
if (linkTarget) {
return (this.#linkTarget = linkTarget);
}
@@ -89787,7 +90122,9 @@ class PathBase {
this.#type |= READDIR_CALLED;
// mark all remaining provisional children as ENOENT
for (let p = children.provisional; p < children.length; p++) {
children[p].#markENOENT();
const c = children[p];
if (c)
c.#markENOENT();
}
}
#markENOENT() {
@@ -93860,7 +94197,7 @@ exports.parse = parse;
/***/ ((module) => {
"use strict";
module.exports = {"i8":"2.3.0"};
module.exports = {"i8":"2.3.1"};
/***/ }),
@@ -93940,7 +94277,7 @@ module.exports = JSON.parse('[["0","\\u0000",128],["a1","。",62],["8140"," 
/***/ ((module) => {
"use strict";
module.exports = JSON.parse('{"name":"make-fetch-happen","version":"13.0.0","description":"Opinionated, caching, retrying fetch client","main":"lib/index.js","files":["bin/","lib/"],"scripts":{"test":"tap","posttest":"npm run lint","eslint":"eslint","lint":"eslint \\"**/*.js\\"","lintfix":"npm run lint -- --fix","postlint":"template-oss-check","snap":"tap","template-oss-apply":"template-oss-apply --force"},"repository":{"type":"git","url":"https://github.com/npm/make-fetch-happen.git"},"keywords":["http","request","fetch","mean girls","caching","cache","subresource integrity"],"author":"GitHub Inc.","license":"ISC","dependencies":{"@npmcli/agent":"^2.0.0","cacache":"^18.0.0","http-cache-semantics":"^4.1.1","is-lambda":"^1.0.1","minipass":"^7.0.2","minipass-fetch":"^3.0.0","minipass-flush":"^1.0.5","minipass-pipeline":"^1.2.4","negotiator":"^0.6.3","promise-retry":"^2.0.1","ssri":"^10.0.0"},"devDependencies":{"@npmcli/eslint-config":"^4.0.0","@npmcli/template-oss":"4.18.0","nock":"^13.2.4","safe-buffer":"^5.2.1","standard-version":"^9.3.2","tap":"^16.0.0"},"engines":{"node":"^16.14.0 || >=18.0.0"},"tap":{"color":1,"files":"test/*.js","check-coverage":true,"timeout":60,"nyc-arg":["--exclude","tap-snapshots/**"]},"templateOSS":{"//@npmcli/template-oss":"This file is partially managed by @npmcli/template-oss. Edits may be overwritten.","ciVersions":["16.14.0","16.x","18.0.0","18.x"],"version":"4.18.0","publish":"true"}}');
module.exports = JSON.parse('{"name":"make-fetch-happen","version":"13.0.1","description":"Opinionated, caching, retrying fetch client","main":"lib/index.js","files":["bin/","lib/"],"scripts":{"test":"tap","posttest":"npm run lint","eslint":"eslint","lint":"eslint \\"**/*.{js,cjs,ts,mjs,jsx,tsx}\\"","lintfix":"npm run lint -- --fix","postlint":"template-oss-check","snap":"tap","template-oss-apply":"template-oss-apply --force"},"repository":{"type":"git","url":"https://github.com/npm/make-fetch-happen.git"},"keywords":["http","request","fetch","mean girls","caching","cache","subresource integrity"],"author":"GitHub Inc.","license":"ISC","dependencies":{"@npmcli/agent":"^2.0.0","cacache":"^18.0.0","http-cache-semantics":"^4.1.1","is-lambda":"^1.0.1","minipass":"^7.0.2","minipass-fetch":"^3.0.0","minipass-flush":"^1.0.5","minipass-pipeline":"^1.2.4","negotiator":"^0.6.3","proc-log":"^4.2.0","promise-retry":"^2.0.1","ssri":"^10.0.0"},"devDependencies":{"@npmcli/eslint-config":"^4.0.0","@npmcli/template-oss":"4.21.4","nock":"^13.2.4","safe-buffer":"^5.2.1","standard-version":"^9.3.2","tap":"^16.0.0"},"engines":{"node":"^16.14.0 || >=18.0.0"},"tap":{"color":1,"files":"test/*.js","check-coverage":true,"timeout":60,"nyc-arg":["--exclude","tap-snapshots/**"]},"templateOSS":{"//@npmcli/template-oss":"This file is partially managed by @npmcli/template-oss. Edits may be overwritten.","version":"4.21.4","publish":"true"}}');
/***/ }),

19
dist/licenses.txt generated vendored
View File

@@ -2887,6 +2887,25 @@ will be liable to anyone for any damages related to this
software or this license, under any kind of legal claim.***
proc-log
ISC
The ISC License
Copyright (c) GitHub, Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
promise-retry
MIT
Copyright (c) 2014 IndigoUnited

727
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "actions/attest",
"description": "Generate signed attestations for workflow artifacts",
"version": "1.1.0",
"version": "1.1.2",
"author": "",
"private": true,
"homepage": "https://github.com/actions/attest",
@@ -72,25 +72,25 @@
"@actions/attest": "^1.2.1",
"@actions/core": "^1.10.1",
"@actions/glob": "^0.4.0",
"@sigstore/oci": "^0.3.0",
"@sigstore/oci": "^0.3.3",
"csv-parse": "^5.5.5"
},
"devDependencies": {
"@sigstore/mock": "^0.7.2",
"@sigstore/mock": "^0.7.3",
"@types/jest": "^29.5.12",
"@types/make-fetch-happen": "^10.0.4",
"@types/node": "^20.12.7",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"@types/node": "^20.12.11",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"eslint-plugin-github": "^4.10.2",
"eslint-plugin-jest": "^28.3.0",
"eslint-plugin-jest": "^28.5.0",
"eslint-plugin-jsonc": "^2.15.1",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"js-yaml": "^4.1.0",
"markdownlint-cli": "^0.39.0",
"markdownlint-cli": "^0.40.0",
"nock": "^13.5.4",
"prettier": "^3.2.5",
"prettier-eslint": "^16.3.0",

View File

@@ -13,16 +13,30 @@ 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 MAX_SUBJECT_COUNT = 64
const OCI_TIMEOUT = 2000
const OCI_RETRY = 3
/* istanbul ignore next */
const logHandler = (level: string, ...args: unknown[]): void => {
// Send any HTTP-related log events to the GitHub Actions debug log
if (level === 'http') {
core.debug(args.join(' '))
}
}
/**
* The main function for the action.
* @returns {Promise<void>} Resolves when the action is complete.
*/
export async function run(): Promise<void> {
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.
@@ -86,14 +100,19 @@ export async function run(): Promise<void> {
} catch (err) {
// Fail the workflow run if an error occurs
core.setFailed(
err instanceof Error ? err.message : /* istanbul ignore next */ `${err}`
err instanceof Error ? err : /* istanbul ignore next */ `${err}`
)
// Log the cause of the error if one is available
/* istanbul ignore if */
if (err instanceof Error && 'cause' in err) {
const innerErr = err.cause
core.debug(innerErr instanceof Error ? innerErr.message : `${innerErr}}`)
core.info(
mute(innerErr instanceof Error ? innerErr.toString() : `${innerErr}`)
)
}
} finally {
process.removeListener('log', logHandler)
}
}
@@ -147,7 +166,8 @@ const createAttestation = async (
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}`)
@@ -156,8 +176,13 @@ const createAttestation = async (
return attestation
}
// Emphasis string using ANSI color codes
const highlight = (str: string): string => `${COLOR_CYAN}${str}${COLOR_DEFAULT}`
// De-emphasize string using ANSI color codes
/* istanbul ignore next */
const mute = (str: string): string => `${COLOR_GRAY}${str}${COLOR_DEFAULT}`
const tempDir = (): string => {
const basePath = process.env['RUNNER_TEMP']

View File

@@ -17,6 +17,9 @@ 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
})
if (!subjectPath && !subjectDigest) {
throw new Error('One of subject-path or subject-digest must be provided')
@@ -32,10 +35,14 @@ export const subjectFromInputs = async (): Promise<Subject[]> => {
throw new Error('subject-name must be provided when using subject-digest')
}
// 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
if (subjectPath) {
return await getSubjectFromPath(subjectPath, subjectName)
return await getSubjectFromPath(subjectPath, name)
} else {
return [getSubjectFromDigest(subjectDigest, subjectName)]
return [getSubjectFromDigest(subjectDigest, name)]
}
}