Skip to content

Commit

Permalink
cli support for v0.3 bundles (#1113)
Browse files Browse the repository at this point in the history
Signed-off-by: Brian DeHamer <bdehamer@github.com>
  • Loading branch information
bdehamer committed Apr 5, 2024
1 parent 518dfb0 commit 66d28b0
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 33 deletions.
5 changes: 5 additions & 0 deletions .changeset/thick-buttons-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sigstore/cli": minor
---

Update `verify` command to verify v0.3 Sigstore bundles
5 changes: 5 additions & 0 deletions .changeset/warm-snakes-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sigstore/cli": minor
---

Update `attest` command to generate v0.3 Sigstore bundles
31 changes: 28 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ $ npm install -g @sigstore/cli
$ sigstore COMMAND
running command...
$ sigstore (--version)
@sigstore/cli/0.6.0 darwin-arm64 node-v18.12.1
@sigstore/cli/0.7.1 darwin-arm64 node-v20.8.1
$ sigstore --help [COMMAND]
USAGE
$ sigstore COMMAND
Expand Down Expand Up @@ -95,10 +95,10 @@ Display help for sigstore.

```
USAGE
$ sigstore help [COMMAND] [-n]
$ sigstore help [COMMAND...] [-n]
ARGUMENTS
COMMAND Command to show help for.
COMMAND... Command to show help for.
FLAGS
-n, --nested-commands Include all nested commands in the output.
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@
"@oclif/color": "^1.0.13",
"@oclif/core": "^3",
"@oclif/plugin-help": "^6",
"@sigstore/bundle": "^2.3.1",
"@sigstore/oci": "^0.3.0",
"@sigstore/sign": "^2.3.0",
"open": "^8.4.2",
"openid-client": "^5.6.5",
"sigstore": "^2.2.0"
"sigstore": "^2.3.0"
},
"devDependencies": {
"make-fetch-happen": "^13.0.0",
Expand Down
102 changes: 77 additions & 25 deletions packages/cli/src/commands/attest.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import color from '@oclif/color';
import { Args, Command, Flags } from '@oclif/core';
import { SerializedBundle, bundleToJSON } from '@sigstore/bundle';
import {
Bundle,
BundleBuilder,
CIContextProvider,
DEFAULT_REKOR_URL,
DSSEBundleBuilder,
FulcioSigner,
IdentityProvider,
RekorWitness,
TSAWitness,
Witness,
} from '@sigstore/sign';
import fs from 'fs/promises';
import * as sigstore from 'sigstore';
import { OAuthIdentityProvider } from '../oauth';

import type { IdentityProvider } from 'sigstore';
const OIDC_AUDIENCE = 'sigstore';

type SignOptions = {
fulcioURL: string;
identityProvider: IdentityProvider;
rekorURL?: string;
tsaServerURL?: string;
timeout?: number;
};

export default class Attest extends Command {
static override description = 'attest the supplied file';
Expand Down Expand Up @@ -81,53 +101,52 @@ export default class Attest extends Command {
}),
};

public async run(): Promise<sigstore.Bundle> {
public async run(): Promise<SerializedBundle> {
const { args, flags } = await this.parse(Attest);
let identityProvider: IdentityProvider | undefined;

// If we're running in CI, we don't want to try to authenticate with an
// OAuth provider because we won't be able to interactively authenticate.
// Leaving the identity provider undefined will cause the attest function
// to use the default identity provider, which will retrieve an OIDC
// token from the environment.
if (!('CI' in process.env) || process.env.CI === 'false') {
identityProvider = new OAuthIdentityProvider({
issuer: flags['oidc-issuer'],
clientID: flags['oidc-client-id'],
clientSecret: flags['oidc-client-secret'],
redirectURL: flags['oidc-redirect-url'],
});
}
const identityProvider =
'CI' in process.env && process.env.CI !== 'false'
? new CIContextProvider(OIDC_AUDIENCE)
: new OAuthIdentityProvider({
issuer: flags['oidc-issuer'],
clientID: flags['oidc-client-id'],
clientSecret: flags['oidc-client-secret'],
redirectURL: flags['oidc-redirect-url'],
});

const options: Parameters<typeof sigstore.attest>[2] = {
rekorURL: flags['rekor-url'],
const options: SignOptions = {
fulcioURL: flags['fulcio-url'],
tsaServerURL: flags['tsa-server-url'],
tlogUpload: flags['tlog-upload'],
rekorURL: flags['tlog-upload'] ? flags['rekor-url'] : undefined,
identityProvider,
timeout: flags.timeout * 1000,
};

const bundler = initBundleBuilder(options);

const bundle = await fs
.readFile(args.file)
.then((data) => sigstore.attest(data, flags['payload-type'], options));
.then((data) => bundler.create({ data, type: flags['payload-type'] }));

const jsonBundle = bundleToJSON(bundle);
if (uploadedToTLog(bundle)) {
this.printRekorEntry(flags['rekor-url'], bundle);
}

if (flags['output-file']) {
await fs.writeFile(flags['output-file'], JSON.stringify(bundle));
await fs.writeFile(flags['output-file'], JSON.stringify(jsonBundle));
} else {
this.log(JSON.stringify(bundle));
this.log(JSON.stringify(jsonBundle));
}

return bundle;
return jsonBundle;
}

private printRekorEntry(baseURL: string, bundle: sigstore.Bundle) {
private printRekorEntry(baseURL: string, bundle: Bundle) {
const url =
baseURL === sigstore.DEFAULT_REKOR_URL
baseURL === DEFAULT_REKOR_URL
? `https://search.sigstore.dev`
: `${baseURL}/api/v1/log/entries`;
const logIndex = bundle.verificationMaterial.tlogEntries[0].logIndex;
Expand All @@ -138,6 +157,39 @@ export default class Attest extends Command {
}
}

function uploadedToTLog(bundle: sigstore.Bundle): boolean {
function uploadedToTLog(bundle: Bundle): boolean {
return bundle.verificationMaterial.tlogEntries.length > 0;
}

const initBundleBuilder = (opts: SignOptions): BundleBuilder => {
const witnesses: Witness[] = [];

const signer = new FulcioSigner({
identityProvider: opts.identityProvider,
fulcioBaseURL: opts.fulcioURL,
timeout: opts.timeout,
});

if (opts.rekorURL) {
witnesses.push(
new RekorWitness({
rekorBaseURL: opts.rekorURL,
entryType: 'intoto',
timeout: opts.timeout,
})
);
}

if (opts.tsaServerURL) {
witnesses.push(
new TSAWitness({
tsaBaseURL: opts.tsaServerURL,
timeout: opts.timeout,
})
);
}

// Build the bundle with the singleCertificate option which will
// trigger the creation of v0.3 DSSE bundles
return new DSSEBundleBuilder({ signer, witnesses, singleCertificate: true });
};
4 changes: 3 additions & 1 deletion packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
},
"exclude": ["./dist"],
"references": [
{ "path": "../bundle" },
{ "path": "../client" },
{ "path": "../oci" }
{ "path": "../oci" },
{ "path": "../sign" }
]
}

0 comments on commit 66d28b0

Please sign in to comment.