Skip to content

Commit 36bd584

Browse files
committed
sigstore: wire tests to explicit cosign binaries
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
1 parent 638fb92 commit 36bd584

File tree

4 files changed

+106
-153
lines changed

4 files changed

+106
-153
lines changed

__tests__/sigstore/sigstore-cosign-old.test.itg.ts

Lines changed: 0 additions & 90 deletions
This file was deleted.

__tests__/sigstore/sigstore.test.itg.ts

Lines changed: 100 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
import {beforeAll, describe, expect, it, test} from 'vitest';
1818
import fs from 'fs';
19+
import os from 'os';
1920
import * as path from 'path';
2021

2122
import {Buildx} from '../../src/buildx/buildx.js';
2223
import {Build} from '../../src/buildx/build.js';
24+
import {Cosign} from '../../src/cosign/cosign.js';
2325
import {Install as CosignInstall} from '../../src/cosign/install.js';
2426
import {Docker} from '../../src/docker/docker.js';
2527
import {Exec} from '../../src/exec.js';
@@ -33,73 +35,106 @@ const runTest = process.env.GITHUB_ACTIONS && process.env.GITHUB_ACTIONS === 'tr
3335
const maybe = runTest ? describe : describe.skip;
3436
const maybeIdToken = runTest && process.env.ACTIONS_ID_TOKEN_REQUEST_URL ? describe : describe.skip;
3537

36-
beforeAll(async () => {
37-
const cosignInstall = new CosignInstall();
38-
const cosignBinPath = await cosignInstall.download({
39-
version: 'v3.0.6'
38+
const imageName = 'ghcr.io/docker/actions-toolkit/test';
39+
const currentCosignVersion = 'v3.0.6';
40+
const signAttestationCosignVersions = ['v3.0.2', currentCosignVersion] as const;
41+
const installedCosign = new Map<string, Promise<string>>();
42+
43+
async function installCosign(version: string): Promise<string> {
44+
let installedPath = installedCosign.get(version);
45+
if (!installedPath) {
46+
installedPath = (async () => {
47+
const cosignInstall = new CosignInstall();
48+
const cosignBinPath = await cosignInstall.download({
49+
version
50+
});
51+
const installDir = fs.mkdtempSync(path.join(process.env.RUNNER_TEMP || os.tmpdir(), `sigstore-cosign-${version.replace(/[^a-zA-Z0-9]+/g, '-')}-`));
52+
return await cosignInstall.install(cosignBinPath, installDir);
53+
})();
54+
installedCosign.set(version, installedPath);
55+
}
56+
return await installedPath;
57+
}
58+
59+
for (const cosignVersion of signAttestationCosignVersions) {
60+
maybeIdToken(`signAttestationManifests with cosign ${cosignVersion}`, () => {
61+
let sigstore: Sigstore;
62+
63+
beforeAll(async () => {
64+
sigstore = new Sigstore({
65+
cosign: new Cosign({
66+
binPath: await installCosign(cosignVersion)
67+
})
68+
});
69+
}, 100000);
70+
71+
it('build, sign and verify', async () => {
72+
const buildx = new Buildx();
73+
const build = new Build({buildx: buildx});
74+
const versionTag = cosignVersion.replace(/^v/, '').replace(/\./g, '-');
75+
76+
await expect(
77+
(async () => {
78+
await Docker.getExecOutput(['login', '--password-stdin', '--username', process.env.GITHUB_REPOSITORY_OWNER || 'docker', 'ghcr.io'], {
79+
input: Buffer.from(process.env.GITHUB_TOKEN || '')
80+
});
81+
})()
82+
).resolves.not.toThrow();
83+
84+
await expect(
85+
(async () => {
86+
// prettier-ignore
87+
const buildCmd = await buildx.getCommand([
88+
'--builder', process.env.CTN_BUILDER_NAME ?? 'default',
89+
'build',
90+
'-f', path.join(fixturesDir, 'hello.Dockerfile'),
91+
'--provenance=mode=max',
92+
'--tag', `${imageName}:sigstore-itg-cosign-${versionTag}`,
93+
'--platform', 'linux/amd64,linux/arm64',
94+
'--push',
95+
'--metadata-file', build.getMetadataFilePath(),
96+
fixturesDir
97+
]);
98+
await Exec.exec(buildCmd.command, buildCmd.args);
99+
})()
100+
).resolves.not.toThrow();
101+
102+
const metadata = build.resolveMetadata();
103+
expect(metadata).toBeDefined();
104+
const buildDigest = build.resolveDigest(metadata);
105+
expect(buildDigest).toBeDefined();
106+
107+
const signResults = await sigstore.signAttestationManifests({
108+
imageNames: [imageName],
109+
imageDigest: buildDigest!
110+
});
111+
expect(Object.keys(signResults).length).toEqual(2);
112+
113+
const verifyResults = await sigstore.verifySignedManifests(signResults, {
114+
certificateIdentityRegexp: `^https://github.com/docker/actions-toolkit/.github/workflows/test.yml.*$`
115+
});
116+
expect(Object.keys(verifyResults).length).toEqual(2);
117+
}, 200000);
40118
});
41-
await cosignInstall.install(cosignBinPath);
42-
}, 100000);
43-
44-
maybeIdToken('signAttestationManifests', () => {
45-
it('build, sign and verify', async () => {
46-
const buildx = new Buildx();
47-
const build = new Build({buildx: buildx});
48-
const imageName = 'ghcr.io/docker/actions-toolkit/test';
49-
50-
await expect(
51-
(async () => {
52-
await Docker.getExecOutput(['login', '--password-stdin', '--username', process.env.GITHUB_REPOSITORY_OWNER || 'docker', 'ghcr.io'], {
53-
input: Buffer.from(process.env.GITHUB_TOKEN || '')
54-
});
55-
})()
56-
).resolves.not.toThrow();
57-
58-
await expect(
59-
(async () => {
60-
// prettier-ignore
61-
const buildCmd = await buildx.getCommand([
62-
'--builder', process.env.CTN_BUILDER_NAME ?? 'default',
63-
'build',
64-
'-f', path.join(fixturesDir, 'hello.Dockerfile'),
65-
'--provenance=mode=max',
66-
'--tag', `${imageName}:sigstore-itg`,
67-
'--platform', 'linux/amd64,linux/arm64',
68-
'--push',
69-
'--metadata-file', build.getMetadataFilePath(),
70-
fixturesDir
71-
]);
72-
await Exec.exec(buildCmd.command, buildCmd.args);
73-
})()
74-
).resolves.not.toThrow();
75-
76-
const metadata = build.resolveMetadata();
77-
expect(metadata).toBeDefined();
78-
const buildDigest = build.resolveDigest(metadata);
79-
expect(buildDigest).toBeDefined();
119+
}
80120

81-
const sigstore = new Sigstore();
82-
const signResults = await sigstore.signAttestationManifests({
83-
imageNames: [imageName],
84-
imageDigest: buildDigest!
85-
});
86-
expect(Object.keys(signResults).length).toEqual(2);
121+
maybe('verifyImageAttestations', () => {
122+
let sigstore: Sigstore;
87123

88-
const verifyResults = await sigstore.verifySignedManifests(signResults, {
89-
certificateIdentityRegexp: `^https://github.com/docker/actions-toolkit/.github/workflows/test.yml.*$`
124+
beforeAll(async () => {
125+
sigstore = new Sigstore({
126+
cosign: new Cosign({
127+
binPath: await installCosign(currentCosignVersion)
128+
})
90129
});
91-
expect(Object.keys(verifyResults).length).toEqual(2);
92130
}, 100000);
93-
});
94131

95-
maybe('verifyImageAttestations', () => {
96132
test.each([
97133
['moby/buildkit:master@sha256:84014da3581b2ff2c14cb4f60029cf9caa272b79e58f2e89c651ea6966d7a505', `^https://github.com/docker/github-builder-experimental/.github/workflows/bake.yml.*$`],
98134
['docker/dockerfile-upstream:master@sha256:3e8cd5ebf48acd1a1939649ad1c62ca44c029852b22493c16a9307b654334958', `^https://github.com/docker/github-builder-experimental/.github/workflows/bake.yml.*$`]
99135
])(
100136
'given %p',
101137
async (image, certificateIdentityRegexp) => {
102-
const sigstore = new Sigstore();
103138
const verifyResults = await sigstore.verifyImageAttestations(image, {
104139
certificateIdentityRegexp: certificateIdentityRegexp
105140
});
@@ -114,7 +149,6 @@ maybe('verifyImageAttestations', () => {
114149
);
115150

116151
it('default platform', async () => {
117-
const sigstore = new Sigstore();
118152
const verifyResults = await sigstore.verifyImageAttestations('moby/buildkit:master@sha256:84014da3581b2ff2c14cb4f60029cf9caa272b79e58f2e89c651ea6966d7a505', {
119153
certificateIdentityRegexp: `^https://github.com/docker/github-builder-experimental/.github/workflows/bake.yml.*$`,
120154
platform: OCI.defaultPlatform()
@@ -161,8 +195,17 @@ maybeIdToken('signProvenanceBlobs', () => {
161195
});
162196

163197
maybeIdToken('verifySignedArtifacts', () => {
198+
let sigstore: Sigstore;
199+
200+
beforeAll(async () => {
201+
sigstore = new Sigstore({
202+
cosign: new Cosign({
203+
binPath: await installCosign(currentCosignVersion)
204+
})
205+
});
206+
}, 100000);
207+
164208
it('sign and verify', async () => {
165-
const sigstore = new Sigstore();
166209
const signResults = await sigstore.signProvenanceBlobs({
167210
localExportDir: path.join(fixturesDir, 'sigstore', 'multi')
168211
});

src/cosign/cosign.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface CosignCommandError {
3838
}
3939

4040
export class Cosign {
41-
private readonly binPath: string;
41+
public readonly binPath: string;
4242
private _version: string;
4343
private _versionOnce: boolean;
4444

src/sigstore/sigstore.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export class Sigstore {
9797
if (noTransparencyLog) {
9898
createConfigArgs.push('--no-default-rekor=true');
9999
}
100-
await Exec.exec('cosign', createConfigArgs, {
100+
await Exec.exec(this.cosign.binPath, createConfigArgs, {
101101
env: Object.assign({}, process.env, {
102102
COSIGN_EXPERIMENTAL: '1'
103103
}) as {
@@ -133,7 +133,7 @@ export class Sigstore {
133133
...cosignExtraArgs
134134
];
135135
core.info(`[command]cosign ${[...cosignArgs, attestationRef].join(' ')}`);
136-
const execRes = await Exec.getExecOutput('cosign', ['--verbose', ...cosignArgs, attestationRef], {
136+
const execRes = await Exec.getExecOutput(this.cosign.binPath, ['--verbose', ...cosignArgs, attestationRef], {
137137
ignoreReturnCode: true,
138138
silent: true,
139139
env: Object.assign({}, process.env, {
@@ -230,7 +230,7 @@ export class Sigstore {
230230

231231
if (!opts.retryOnManifestUnknown) {
232232
core.info(`[command]cosign ${[...cosignArgs, attestationRef].join(' ')}`);
233-
const execRes = await Exec.getExecOutput('cosign', ['--verbose', ...cosignArgs, attestationRef], {
233+
const execRes = await Exec.getExecOutput(this.cosign.binPath, ['--verbose', ...cosignArgs, attestationRef], {
234234
ignoreReturnCode: true,
235235
silent: true,
236236
env: Object.assign({}, process.env, {
@@ -252,7 +252,7 @@ export class Sigstore {
252252
let lastError: Error | undefined;
253253
core.info(`[command]cosign ${[...cosignArgs, attestationRef].join(' ')}`);
254254
for (let attempt = 0; attempt < retries; attempt++) {
255-
const execRes = await Exec.getExecOutput('cosign', ['--verbose', ...cosignArgs, attestationRef], {
255+
const execRes = await Exec.getExecOutput(this.cosign.binPath, ['--verbose', ...cosignArgs, attestationRef], {
256256
ignoreReturnCode: true,
257257
silent: true,
258258
env: Object.assign({}, process.env, {
@@ -361,7 +361,7 @@ export class Sigstore {
361361
// if there is no tlog entry, we skip tlog verification but still verify the signed timestamp
362362
cosignArgs.push('--use-signed-timestamps', '--insecure-ignore-tlog');
363363
}
364-
const execRes = await Exec.getExecOutput('cosign', [...cosignArgs, '--bundle', signedRes.bundlePath, artifactPath], {
364+
const execRes = await Exec.getExecOutput(this.cosign.binPath, [...cosignArgs, '--bundle', signedRes.bundlePath, artifactPath], {
365365
ignoreReturnCode: true
366366
});
367367
if (execRes.stderr.length > 0 && execRes.exitCode != 0) {

0 commit comments

Comments
 (0)