diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f67ec338..356a9da0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -149,6 +149,7 @@ jobs: permissions: contents: read id-token: write # needed for signing with GitHub OIDC Token + packages: write # needed for pushing to GitHub Container Registry steps: - name: Checkout diff --git a/__tests__/sigstore/sigstore.test.itg.ts b/__tests__/sigstore/sigstore.test.itg.ts index 4d549fc4..6fff3f63 100644 --- a/__tests__/sigstore/sigstore.test.itg.ts +++ b/__tests__/sigstore/sigstore.test.itg.ts @@ -18,7 +18,11 @@ import {beforeAll, describe, expect, jest, it, test} from '@jest/globals'; import fs from 'fs'; import * as path from 'path'; +import {Buildx} from '../../src/buildx/buildx'; +import {Build} from '../../src/buildx/build'; import {Install as CosignInstall} from '../../src/cosign/install'; +import {Docker} from '../../src/docker/docker'; +import {Exec} from '../../src/exec'; import {Sigstore} from '../../src/sigstore/sigstore'; const fixturesDir = path.join(__dirname, '..', '.fixtures'); @@ -39,6 +43,57 @@ beforeAll(async () => { await cosignInstall.install(cosignBinPath); }, 100000); +maybeIdToken('signAttestationManifests', () => { + it('build, sign and verify', async () => { + const buildx = new Buildx(); + const build = new Build({buildx: buildx}); + const imageName = 'ghcr.io/docker/actions-toolkit/test'; + + await expect( + (async () => { + await Docker.getExecOutput(['login', '--password-stdin', '--username', process.env.GITHUB_REPOSITORY_OWNER || 'docker', 'ghcr.io'], { + input: Buffer.from(process.env.GITHUB_TOKEN || '') + }); + })() + ).resolves.not.toThrow(); + + await expect( + (async () => { + // prettier-ignore + const buildCmd = await buildx.getCommand([ + '--builder', process.env.CTN_BUILDER_NAME ?? 'default', + 'build', + '-f', path.join(fixturesDir, 'hello.Dockerfile'), + '--provenance=mode=max', + '--tag', `${imageName}:sigstore-itg`, + '--platform', 'linux/amd64,linux/arm64', + '--push', + '--metadata-file', build.getMetadataFilePath(), + fixturesDir + ]); + await Exec.exec(buildCmd.command, buildCmd.args); + })() + ).resolves.not.toThrow(); + + const metadata = build.resolveMetadata(); + expect(metadata).toBeDefined(); + const buildDigest = build.resolveDigest(metadata); + expect(buildDigest).toBeDefined(); + + const sigstore = new Sigstore(); + const signResults = await sigstore.signAttestationManifests({ + imageNames: [imageName], + imageDigest: buildDigest! + }); + expect(Object.keys(signResults).length).toEqual(2); + + const verifyResults = await sigstore.verifySignedManifests(signResults, { + certificateIdentityRegexp: `^https://github.com/docker/actions-toolkit/.github/workflows/test.yml.*$` + }); + expect(Object.keys(verifyResults).length).toEqual(2); + }, 100000); +}); + maybe('verifyImageAttestations', () => { test.each([ ['moby/buildkit:master@sha256:84014da3581b2ff2c14cb4f60029cf9caa272b79e58f2e89c651ea6966d7a505', `^https://github.com/docker/github-builder-experimental/.github/workflows/bake.yml.*$`],