Compare commits

..
Author SHA1 Message Date
Thomas Boop 79a47d255a fix test 2024-08-15 16:43:19 -04:00
Thomas Boop b39d573389 update tests 2024-08-15 16:39:09 -04:00
Thomas Boop 30289d37f7 fix tests 2024-08-15 16:35:15 -04:00
Thomas Boop 5deef5c6b4 update tests 2024-08-15 16:23:33 -04:00
Thomas Boop 6422c3bb5d fix vuln 2024-08-15 16:17:49 -04:00
Thomas Boop 2785fc91aa fix tests 2024-08-15 16:16:30 -04:00
18 changed files with 33 additions and 114 deletions
-4
View File
@@ -63,8 +63,6 @@ export type AttestOptions = {
// Sigstore instance to use for signing. Must be one of "public-good" or
// "github".
sigstore?: 'public-good' | 'github'
// HTTP headers to include in request to attestations API.
headers?: {[header: string]: string | number | undefined}
// Whether to skip writing the attestation to the GH attestations API.
skipWrite?: boolean
}
@@ -115,8 +113,6 @@ export type AttestProvenanceOptions = {
// Sigstore instance to use for signing. Must be one of "public-good" or
// "github".
sigstore?: 'public-good' | 'github'
// HTTP headers to include in request to attestations API.
headers?: {[header: string]: string | number | undefined}
// Whether to skip writing the attestation to the GH attestations API.
skipWrite?: boolean
// Issuer URL responsible for minting the OIDC token from which the
-5
View File
@@ -1,10 +1,5 @@
# @actions/attest Releases
### 1.4.0
- Add new `headers` parameter to the `attest` and `attestProvenance` functions.
- Update `buildSLSAProvenancePredicate`/`attestProvenance` to automatically derive default OIDC issuer URL from current execution context.
### 1.3.1
- Fix bug with proxy support when retrieving JWKS for OIDC issuer
@@ -9,7 +9,7 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
"workflow": {
"path": ".github/workflows/main.yml",
"ref": "main",
"repository": "https://foo.ghe.com/owner/repo",
"repository": "https://github.com/owner/repo",
},
},
"internalParameters": {
@@ -25,16 +25,16 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
"digest": {
"gitCommit": "babca52ab0c93ae16539e5923cb0d7403b9a093b",
},
"uri": "git+https://foo.ghe.com/owner/repo@refs/heads/main",
"uri": "git+https://github.com/owner/repo@refs/heads/main",
},
],
},
"runDetails": {
"builder": {
"id": "https://foo.ghe.com/owner/workflows/.github/workflows/publish.yml@main",
"id": "https://github.com/owner/workflows/.github/workflows/publish.yml@main",
},
"metadata": {
"invocationId": "https://foo.ghe.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
"invocationId": "https://github.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
},
},
},
+15 -10
View File
@@ -8,7 +8,7 @@ import {attestProvenance, buildSLSAProvenancePredicate} from '../src/provenance'
describe('provenance functions', () => {
const originalEnv = process.env
const issuer = 'https://token.actions.foo.ghe.com'
const issuer = 'https://example.com'
const audience = 'nobody'
const jwksPath = '/.well-known/jwks.json'
const tokenPath = '/token'
@@ -38,7 +38,7 @@ describe('provenance functions', () => {
...originalEnv,
ACTIONS_ID_TOKEN_REQUEST_URL: `${issuer}${tokenPath}?`,
ACTIONS_ID_TOKEN_REQUEST_TOKEN: 'token',
GITHUB_SERVER_URL: 'https://foo.ghe.com',
GITHUB_SERVER_URL: 'https://github.com',
GITHUB_REPOSITORY: claims.repository
}
@@ -68,7 +68,7 @@ describe('provenance functions', () => {
describe('buildSLSAProvenancePredicate', () => {
it('returns a provenance hydrated from an OIDC token', async () => {
const predicate = await buildSLSAProvenancePredicate()
const predicate = await buildSLSAProvenancePredicate(issuer)
expect(predicate).toMatchSnapshot()
})
})
@@ -96,9 +96,9 @@ describe('provenance functions', () => {
})
describe('when using the github Sigstore instance', () => {
beforeEach(async () => {
const {fulcioURL, tsaServerURL} = signingEndpoints('github')
const {fulcioURL, tsaServerURL} = signingEndpoints('github')
beforeEach(async () => {
// Mock Sigstore
await mockFulcio({baseURL: fulcioURL, strict: false})
await mockTSA({baseURL: tsaServerURL})
@@ -118,7 +118,8 @@ describe('provenance functions', () => {
subjectName,
subjectDigest,
token: 'token',
sigstore: 'github'
sigstore: 'github',
issuer
})
expect(attestation).toBeDefined()
@@ -145,7 +146,8 @@ describe('provenance functions', () => {
const attestation = await attestProvenance({
subjectName,
subjectDigest,
token: 'token'
token: 'token',
issuer
})
expect(attestation).toBeDefined()
@@ -181,7 +183,8 @@ describe('provenance functions', () => {
subjectName,
subjectDigest,
token: 'token',
sigstore: 'public-good'
sigstore: 'public-good',
issuer
})
expect(attestation).toBeDefined()
@@ -208,7 +211,8 @@ describe('provenance functions', () => {
const attestation = await attestProvenance({
subjectName,
subjectDigest,
token: 'token'
token: 'token',
issuer
})
expect(attestation).toBeDefined()
@@ -234,7 +238,8 @@ describe('provenance functions', () => {
subjectDigest,
token: 'token',
sigstore: 'public-good',
skipWrite: true
skipWrite: true,
issuer
})
expect(attestation).toBeDefined()
+2 -5
View File
@@ -5,7 +5,6 @@ describe('writeAttestation', () => {
const originalEnv = process.env
const attestation = {foo: 'bar '}
const token = 'token'
const headers = {'X-GitHub-Foo': 'true'}
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
@@ -28,16 +27,14 @@ describe('writeAttestation', () => {
.intercept({
path: '/repos/foo/bar/attestations',
method: 'POST',
headers: {authorization: `token ${token}`, ...headers},
headers: {authorization: `token ${token}`},
body: JSON.stringify({bundle: attestation})
})
.reply(201, {id: '123'})
})
it('persists the attestation', async () => {
await expect(
writeAttestation(attestation, token, {headers})
).resolves.toEqual('123')
await expect(writeAttestation(attestation, token)).resolves.toEqual('123')
})
})
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@actions/attest",
"version": "1.4.0",
"version": "1.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/attest",
"version": "1.4.0",
"version": "1.3.1",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/attest",
"version": "1.4.0",
"version": "1.3.1",
"description": "Actions attestation lib",
"keywords": [
"github",
+1 -7
View File
@@ -28,8 +28,6 @@ export type AttestOptions = {
// Sigstore instance to use for signing. Must be one of "public-good" or
// "github".
sigstore?: SigstoreInstance
// HTTP headers to include in request to attestations API.
headers?: {[header: string]: string | number | undefined}
// Whether to skip writing the attestation to the GH attestations API.
skipWrite?: boolean
}
@@ -63,11 +61,7 @@ export async function attest(options: AttestOptions): Promise<Attestation> {
// Store the attestation
let attestationID: string | undefined
if (options.skipWrite !== true) {
attestationID = await writeAttestation(
bundleToJSON(bundle),
options.token,
{headers: options.headers}
)
attestationID = await writeAttestation(bundleToJSON(bundle), options.token)
}
return toAttestation(bundle, attestationID)
+1 -25
View File
@@ -4,11 +4,6 @@ import * as jose from 'jose'
const OIDC_AUDIENCE = 'nobody'
const VALID_SERVER_URLS = [
'https://github.com',
new RegExp('^https://[a-z0-9-]+\\.ghe\\.com$')
] as const
const REQUIRED_CLAIMS = [
'iss',
'ref',
@@ -30,8 +25,7 @@ type OIDCConfig = {
jwks_uri: string
}
export const getIDTokenClaims = async (issuer?: string): Promise<ClaimSet> => {
issuer = issuer || getIssuer()
export const getIDTokenClaims = async (issuer: string): Promise<ClaimSet> => {
try {
const token = await getIDToken(OIDC_AUDIENCE)
const claims = await decodeOIDCToken(token, issuer)
@@ -88,21 +82,3 @@ function assertClaimSet(claims: jose.JWTPayload): asserts claims is ClaimSet {
throw new Error(`Missing claims: ${missingClaims.join(', ')}`)
}
}
// Derive the current OIDC issuer based on the server URL
function getIssuer(): string {
const serverURL = process.env.GITHUB_SERVER_URL || 'https://github.com'
// Ensure the server URL is a valid GitHub server URL
if (!VALID_SERVER_URLS.some(valid_url => serverURL.match(valid_url))) {
throw new Error(`Invalid server URL: ${serverURL}`)
}
let host = new URL(serverURL).hostname
if (host === 'github.com') {
host = 'githubusercontent.com'
}
return `https://token.actions.${host}`
}
+3 -1
View File
@@ -5,6 +5,8 @@ import type {Attestation, Predicate} from './shared.types'
const SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1'
const GITHUB_BUILD_TYPE = 'https://actions.github.io/buildtypes/workflow/v1'
const DEFAULT_ISSUER = 'https://token.actions.githubusercontent.com'
export type AttestProvenanceOptions = Omit<
AttestOptions,
'predicate' | 'predicateType'
@@ -22,7 +24,7 @@ export type AttestProvenanceOptions = Omit<
* @returns The SLSA provenance predicate.
*/
export const buildSLSAProvenancePredicate = async (
issuer?: string
issuer: string = DEFAULT_ISSUER
): Promise<Predicate> => {
const serverURL = process.env.GITHUB_SERVER_URL
const claims = await getIDTokenClaims(issuer)
-3
View File
@@ -1,13 +1,11 @@
import * as github from '@actions/github'
import {retry} from '@octokit/plugin-retry'
import {RequestHeaders} from '@octokit/types'
const CREATE_ATTESTATION_REQUEST = 'POST /repos/{owner}/{repo}/attestations'
const DEFAULT_RETRY_COUNT = 5
export type WriteOptions = {
retry?: number
headers?: RequestHeaders
}
/**
* Writes an attestation to the repository's attestations endpoint.
@@ -28,7 +26,6 @@ export const writeAttestation = async (
const response = await octokit.request(CREATE_ATTESTATION_REQUEST, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
headers: options.headers,
data: {bundle: attestation}
})
-3
View File
@@ -1,8 +1,5 @@
# @actions/glob Releases
### 0.5.0
- Added `excludeHiddenFiles` option, which is disabled by default to preserve existing behavior [#1791: Add glob option to ignore hidden files](https://github.com/actions/toolkit/pull/1791)
### 0.4.0
- Pass in the current workspace as a parameter to HashFiles [#1318](https://github.com/actions/toolkit/pull/1318)
@@ -708,7 +708,7 @@ describe('globber', () => {
expect(itemPaths).toEqual([])
})
it('returns hidden files by default', async () => {
it('returns hidden files', async () => {
// Create the following layout:
// <root>
// <root>/.emptyFolder
@@ -734,26 +734,6 @@ describe('globber', () => {
])
})
it('ignores hidden files when excludeHiddenFiles is set', async () => {
// Create the following layout:
// <root>
// <root>/.emptyFolder
// <root>/.file
// <root>/.folder
// <root>/.folder/file
const root = path.join(getTestTemp(), 'ignores-hidden-files')
await createHiddenDirectory(path.join(root, '.emptyFolder'))
await createHiddenDirectory(path.join(root, '.folder'))
await createHiddenFile(path.join(root, '.file'), 'test .file content')
await fs.writeFile(
path.join(root, '.folder', 'file'),
'test .folder/file content'
)
const itemPaths = await glob(root, {excludeHiddenFiles: true})
expect(itemPaths).toEqual([root])
})
it('returns normalized paths', async () => {
// Create the following layout:
// <root>/hello/world.txt
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/glob",
"version": "0.5.0",
"version": "0.4.0",
"lockfileVersion": 3,
"requires": true,
"description": "Actions glob lib",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/glob",
"version": "0.5.0",
"version": "0.4.0",
"preview": true,
"description": "Actions glob lib",
"keywords": [
@@ -9,8 +9,7 @@ export function getOptions(copy?: GlobOptions): GlobOptions {
followSymbolicLinks: true,
implicitDescendants: true,
matchDirectories: true,
omitBrokenSymbolicLinks: true,
excludeHiddenFiles: false
omitBrokenSymbolicLinks: true
}
if (copy) {
@@ -33,11 +32,6 @@ export function getOptions(copy?: GlobOptions): GlobOptions {
result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks
core.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`)
}
if (typeof copy.excludeHiddenFiles === 'boolean') {
result.excludeHiddenFiles = copy.excludeHiddenFiles
core.debug(`excludeHiddenFiles '${result.excludeHiddenFiles}'`)
}
}
return result
@@ -36,13 +36,4 @@ export interface GlobOptions {
* @default true
*/
omitBrokenSymbolicLinks?: boolean
/**
* Indicates whether to exclude hidden files (files and directories starting with a `.`).
* This does not apply to Windows files and directories with the hidden attribute unless
* they are also prefixed with a `.`.
*
* @default false
*/
excludeHiddenFiles?: boolean
}
-5
View File
@@ -128,11 +128,6 @@ export class DefaultGlobber implements Globber {
continue
}
// Hidden file or directory?
if (options.excludeHiddenFiles && path.basename(item.path).match(/^\./)) {
continue
}
// Directory
if (stats.isDirectory()) {
// Matched