Skip to content

Commit 44e7279

Browse files
committed
cosign(install): verify binary signature with keyless verification bundle
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
1 parent 5e6dd63 commit 44e7279

File tree

4 files changed

+52
-10
lines changed

4 files changed

+52
-10
lines changed

__tests__/cosign/install.test.itg.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ describe('download', () => {
2727
'install cosign %s', async (version) => {
2828
await expect((async () => {
2929
const install = new Install();
30-
const toolPath = await install.download(version);
30+
const toolPath = await install.download({
31+
version: version,
32+
verifySignature: true
33+
});
3134
if (!fs.existsSync(toolPath)) {
3235
throw new Error('toolPath does not exist');
3336
}

__tests__/cosign/install.test.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe('download', () => {
3838
])(
3939
'acquires %p of cosign', async (version) => {
4040
const install = new Install();
41-
const toolPath = await install.download(version);
41+
const toolPath = await install.download({version});
4242
expect(fs.existsSync(toolPath)).toBe(true);
4343
const cosignBin = await install.install(toolPath, tmpDir);
4444
expect(fs.existsSync(cosignBin)).toBe(true);
@@ -52,7 +52,7 @@ describe('download', () => {
5252
])(
5353
'acquires %p of cosign with cache', async (version) => {
5454
const install = new Install();
55-
const toolPath = await install.download(version);
55+
const toolPath = await install.download({version});
5656
expect(fs.existsSync(toolPath)).toBe(true);
5757
}, 100000);
5858

@@ -63,7 +63,10 @@ describe('download', () => {
6363
])(
6464
'acquires %p of cosign without cache', async (version) => {
6565
const install = new Install();
66-
const toolPath = await install.download(version, true);
66+
const toolPath = await install.download({
67+
version: version,
68+
ghaNoCache: true
69+
});
6770
expect(fs.existsSync(toolPath)).toBe(true);
6871
}, 100000);
6972

@@ -80,7 +83,9 @@ describe('download', () => {
8083
jest.spyOn(osm, 'platform').mockImplementation(() => os as NodeJS.Platform);
8184
jest.spyOn(osm, 'arch').mockImplementation(() => arch);
8285
const install = new Install();
83-
const cosignBin = await install.download('latest');
86+
const cosignBin = await install.download({
87+
version: 'latest'
88+
});
8489
expect(fs.existsSync(cosignBin)).toBe(true);
8590
}, 100000);
8691
});

__tests__/sigstore/sigstore.test.itg.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ jest.unmock('@actions/github');
3030

3131
beforeAll(async () => {
3232
const cosignInstall = new CosignInstall();
33-
const cosignBinPath = await cosignInstall.download('v3.0.2', true);
33+
const cosignBinPath = await cosignInstall.download({
34+
version: 'v3.0.2'
35+
});
3436
await cosignInstall.install(cosignBinPath);
3537
}, 100000);
3638

src/cosign/install.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ import {DownloadVersion} from '../types/cosign/cosign';
3434
import {GitHubRelease} from '../types/github';
3535
import {dockerfileContent} from './dockerfile';
3636

37+
export interface DownloadOpts {
38+
version: string;
39+
ghaNoCache?: boolean;
40+
skipState?: boolean;
41+
verifySignature?: boolean;
42+
}
43+
3744
export interface InstallOpts {
3845
githubToken?: string;
3946
buildx?: Buildx;
@@ -48,8 +55,8 @@ export class Install {
4855
this.buildx = opts?.buildx || new Buildx();
4956
}
5057

51-
public async download(v: string, ghaNoCache?: boolean, skipState?: boolean): Promise<string> {
52-
const version: DownloadVersion = await Install.getDownloadVersion(v);
58+
public async download(opts: DownloadOpts): Promise<string> {
59+
const version: DownloadVersion = await Install.getDownloadVersion(opts.version);
5360
core.debug(`Install.download version: ${version.version}`);
5461

5562
const release: GitHubRelease = await Install.getRelease(version, this.githubToken);
@@ -68,7 +75,7 @@ export class Install {
6875
htcVersion: vspec,
6976
baseCacheDir: path.join(os.homedir(), '.bin'),
7077
cacheFile: os.platform() == 'win32' ? 'cosign.exe' : 'cosign',
71-
ghaNoCache: ghaNoCache
78+
ghaNoCache: opts.ghaNoCache
7279
});
7380

7481
const cacheFoundPath = await installCache.find();
@@ -83,7 +90,11 @@ export class Install {
8390
const htcDownloadPath = await tc.downloadTool(downloadURL, undefined, this.githubToken);
8491
core.debug(`Install.download htcDownloadPath: ${htcDownloadPath}`);
8592

86-
const cacheSavePath = await installCache.save(htcDownloadPath, skipState);
93+
if (opts.verifySignature && semver.satisfies(vspec, '>=3.0.1')) {
94+
await this.verifySignature(htcDownloadPath, downloadURL);
95+
}
96+
97+
const cacheSavePath = await installCache.save(htcDownloadPath, opts.skipState);
8798
core.info(`Cached to ${cacheSavePath}`);
8899
return cacheSavePath;
89100
}
@@ -176,6 +187,27 @@ export class Install {
176187
return await new Buildx({standalone: buildStandalone}).getCommand(args);
177188
}
178189

190+
private async verifySignature(cosignBinPath: string, downloadURL: string): Promise<void> {
191+
const cosignBootstrapPath = path.join(Context.tmpDir(), `cosign-bootstrap${os.platform() == 'win32' ? '.exe' : ''}`);
192+
fs.copyFileSync(cosignBinPath, cosignBootstrapPath);
193+
fs.chmodSync(cosignBootstrapPath, '0755');
194+
195+
const bundleURL = `${downloadURL}.sigstore.json`;
196+
core.info(`Downloading keyless verification bundle at ${bundleURL}`);
197+
const bundlePath = await tc.downloadTool(bundleURL, undefined, this.githubToken);
198+
core.debug(`Install.verifySignature bundlePath: ${bundlePath}`);
199+
200+
core.info(`Verifying cosign binary signature with keyless verification bundle`);
201+
// prettier-ignore
202+
await Exec.exec(cosignBootstrapPath, [
203+
'verify-blob',
204+
'--certificate-identity', 'keyless@projectsigstore.iam.gserviceaccount.com',
205+
'--certificate-oidc-issuer', 'https://accounts.google.com',
206+
'--bundle', bundlePath,
207+
cosignBinPath
208+
]);
209+
}
210+
179211
private filename(): string {
180212
let arch: string;
181213
switch (os.arch()) {

0 commit comments

Comments
 (0)