From 3b0aea8cb5d5f79a8b51c6499c1877866f7f9b03 Mon Sep 17 00:00:00 2001 From: Billy Lynch Date: Sat, 26 Nov 2022 14:10:50 -0500 Subject: [PATCH] Port gitsign-attest to cobra subcommand. Signed-off-by: Billy Lynch --- cmd/gitsign-attest/main.go | 85 ------------- docs/cli/gitsign.md | 3 +- docs/cli/gitsign_attest.md | 22 ++++ docs/cli/gitsign_show.md | 2 +- docs/cli/gitsign_version.md | 2 +- .../internal => internal}/attest/attest.go | 0 .../attest/attest_test.go | 0 .../attest/testdata/bar.txt | 0 .../attest/testdata/foo.txt | 0 .../attest/testdata/test.json | 0 .../attest/testdata/test.json.provenance | 0 .../commands/attest}/README.md | 4 +- internal/commands/attest/attest.go | 114 ++++++++++++++++++ internal/commands/root/root.go | 2 + 14 files changed, 144 insertions(+), 90 deletions(-) delete mode 100644 cmd/gitsign-attest/main.go create mode 100644 docs/cli/gitsign_attest.md rename {cmd/gitsign-attest/internal => internal}/attest/attest.go (100%) rename {cmd/gitsign-attest/internal => internal}/attest/attest_test.go (100%) rename {cmd/gitsign-attest/internal => internal}/attest/testdata/bar.txt (100%) rename {cmd/gitsign-attest/internal => internal}/attest/testdata/foo.txt (100%) rename {cmd/gitsign-attest/internal => internal}/attest/testdata/test.json (100%) rename {cmd/gitsign-attest/internal => internal}/attest/testdata/test.json.provenance (100%) rename {cmd/gitsign-attest => internal/commands/attest}/README.md (95%) create mode 100644 internal/commands/attest/attest.go diff --git a/cmd/gitsign-attest/main.go b/cmd/gitsign-attest/main.go deleted file mode 100644 index 2ea3594a..00000000 --- a/cmd/gitsign-attest/main.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2022 The Sigstore Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "flag" - "fmt" - "log" - - "github.com/go-git/go-git/v5" - "github.com/sigstore/cosign/cmd/cosign/cli/options" - "github.com/sigstore/cosign/cmd/cosign/cli/sign" - "github.com/sigstore/cosign/pkg/cosign" - "github.com/sigstore/gitsign/cmd/gitsign-attest/internal/attest" -) - -var ( - tree = flag.Bool("t", false, "sign tree instead of commit") - path = flag.String("f", "", "file to attest") - attType = flag.String("type", "", "attestation type") -) - -const ( - attCommitRef = "refs/attestations/commits" - attTreeRef = "refs/attestations/trees" -) - -func main() { - flag.Parse() - ctx := context.Background() - - repo, err := git.PlainOpen(".") - if err != nil { - log.Fatal(err) - } - - head, err := repo.Head() - if err != nil { - log.Fatal(err) - } - fmt.Println(head) - - // If we're attaching the attestation to a tree, resolve the tree SHA. - sha := head.Hash() - refName := attCommitRef - if *tree { - commit, err := repo.CommitObject(head.Hash()) - if err != nil { - log.Fatal(err) - } - sha = commit.TreeHash - - refName = attTreeRef - } - - // TODO: read values from config. - sv, err := sign.SignerFromKeyOpts(ctx, "", "", options.KeyOpts{ - FulcioURL: "https://fulcio.sigstore.dev", - RekorURL: "https://rekor.sigstore.dev", - OIDCIssuer: "https://oauth2.sigstore.dev/auth", - OIDCClientID: "sigstore", - }) - if err != nil { - log.Fatalf("getting signer: %v", err) - } - defer sv.Close() - - attestor := attest.NewAttestor(repo, sv, cosign.TLogUploadInTotoAttestation) - - out, err := attestor.WriteFile(ctx, refName, sha, *path, *attType) - fmt.Println(out, err) -} diff --git a/docs/cli/gitsign.md b/docs/cli/gitsign.md index 14da86cc..3546fe67 100644 --- a/docs/cli/gitsign.md +++ b/docs/cli/gitsign.md @@ -22,7 +22,8 @@ gitsign [flags] ### SEE ALSO +* [gitsign attest](gitsign_attest.md) - add attestations to Git objects * [gitsign show](gitsign_show.md) - Show source predicate information * [gitsign version](gitsign_version.md) - print Gitsign version -###### Auto generated by spf13/cobra on 21-Nov-2022 +###### Auto generated by spf13/cobra on 26-Nov-2022 diff --git a/docs/cli/gitsign_attest.md b/docs/cli/gitsign_attest.md new file mode 100644 index 00000000..a4c9a269 --- /dev/null +++ b/docs/cli/gitsign_attest.md @@ -0,0 +1,22 @@ +## gitsign attest + +add attestations to Git objects + +``` +gitsign attest [flags] +``` + +### Options + +``` + -f, --filepath string attestation filepath + -h, --help help for attest + --objtype string [commit | tree] - Git object type to attest (default "commit") + --type string specify a predicate type (slsaprovenance|link|spdx|spdxjson|cyclonedx|vuln|custom) or an URI (default "custom") +``` + +### SEE ALSO + +* [gitsign](gitsign.md) - Keyless Git signing with Sigstore! + +###### Auto generated by spf13/cobra on 26-Nov-2022 diff --git a/docs/cli/gitsign_show.md b/docs/cli/gitsign_show.md index ee8083ce..67396db9 100644 --- a/docs/cli/gitsign_show.md +++ b/docs/cli/gitsign_show.md @@ -26,4 +26,4 @@ gitsign show [revision] [flags] * [gitsign](gitsign.md) - Keyless Git signing with Sigstore! -###### Auto generated by spf13/cobra on 21-Nov-2022 +###### Auto generated by spf13/cobra on 26-Nov-2022 diff --git a/docs/cli/gitsign_version.md b/docs/cli/gitsign_version.md index 077e9b48..f84bf83d 100644 --- a/docs/cli/gitsign_version.md +++ b/docs/cli/gitsign_version.md @@ -16,4 +16,4 @@ gitsign version [flags] * [gitsign](gitsign.md) - Keyless Git signing with Sigstore! -###### Auto generated by spf13/cobra on 21-Nov-2022 +###### Auto generated by spf13/cobra on 26-Nov-2022 diff --git a/cmd/gitsign-attest/internal/attest/attest.go b/internal/attest/attest.go similarity index 100% rename from cmd/gitsign-attest/internal/attest/attest.go rename to internal/attest/attest.go diff --git a/cmd/gitsign-attest/internal/attest/attest_test.go b/internal/attest/attest_test.go similarity index 100% rename from cmd/gitsign-attest/internal/attest/attest_test.go rename to internal/attest/attest_test.go diff --git a/cmd/gitsign-attest/internal/attest/testdata/bar.txt b/internal/attest/testdata/bar.txt similarity index 100% rename from cmd/gitsign-attest/internal/attest/testdata/bar.txt rename to internal/attest/testdata/bar.txt diff --git a/cmd/gitsign-attest/internal/attest/testdata/foo.txt b/internal/attest/testdata/foo.txt similarity index 100% rename from cmd/gitsign-attest/internal/attest/testdata/foo.txt rename to internal/attest/testdata/foo.txt diff --git a/cmd/gitsign-attest/internal/attest/testdata/test.json b/internal/attest/testdata/test.json similarity index 100% rename from cmd/gitsign-attest/internal/attest/testdata/test.json rename to internal/attest/testdata/test.json diff --git a/cmd/gitsign-attest/internal/attest/testdata/test.json.provenance b/internal/attest/testdata/test.json.provenance similarity index 100% rename from cmd/gitsign-attest/internal/attest/testdata/test.json.provenance rename to internal/attest/testdata/test.json.provenance diff --git a/cmd/gitsign-attest/README.md b/internal/commands/attest/README.md similarity index 95% rename from cmd/gitsign-attest/README.md rename to internal/commands/attest/README.md index e0dd9686..509f5a9b 100644 --- a/cmd/gitsign-attest/README.md +++ b/internal/commands/attest/README.md @@ -30,7 +30,7 @@ f44de7a (HEAD -> main) commit 2b0ff1e commit 1 760568f initial commit $ gitsign-attest -f test.json -$ gitsign-attest -f spdx.sbom -type spdx +$ gitsign-attest -f spdx.sbom --type spdx $ git checkout refs/attestations/commits $ tree . @@ -51,7 +51,7 @@ preserve attestations for squash commits, or between sub-directories. ```sh $ git log --oneline --format="Commit: %h Tree: %t" -1 Commit: edd19d9 Tree: 853a6ca -$ gitsign-attest -f test.json -t +$ gitsign-attest -f test.json --objtype tree $ git checkout refs/attestations/trees $ tree . . diff --git a/internal/commands/attest/attest.go b/internal/commands/attest/attest.go new file mode 100644 index 00000000..6a7b25ad --- /dev/null +++ b/internal/commands/attest/attest.go @@ -0,0 +1,114 @@ +// Copyright 2022 The Sigstore Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package attest + +import ( + "context" + "fmt" + + "github.com/go-git/go-git/v5" + cosignopts "github.com/sigstore/cosign/cmd/cosign/cli/options" + "github.com/sigstore/cosign/cmd/cosign/cli/sign" + "github.com/sigstore/cosign/pkg/cosign" + "github.com/sigstore/gitsign/internal/attest" + "github.com/sigstore/gitsign/internal/config" + "github.com/spf13/cobra" +) + +const ( + attCommitRef = "refs/attestations/commits" + attTreeRef = "refs/attestations/trees" + + FlagObjectTypeCommit = "commit" + FlagObjectTypeTree = "tree" +) + +type options struct { + Config *config.Config + + FlagObjectType string + FlagPath string + FlagAttestationType string +} + +func (o *options) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.FlagObjectType, "objtype", FlagObjectTypeCommit, "[commit | tree] - Git object type to attest") + cmd.Flags().StringVarP(&o.FlagPath, "filepath", "f", "", "attestation filepath") + cmd.Flags().StringVar(&o.FlagAttestationType, "type", "", `specify a predicate type (slsaprovenance|link|spdx|spdxjson|cyclonedx|vuln|custom) or an URI (default "custom")`) +} + +func (o *options) Run(ctx context.Context) error { + repo, err := git.PlainOpen(".") + if err != nil { + return fmt.Errorf("error opening repo: %w", err) + } + + head, err := repo.Head() + if err != nil { + return fmt.Errorf("error getting repository head: %w", err) + } + + // If we're attaching the attestation to a tree, resolve the tree SHA. + sha := head.Hash() + refName := attCommitRef + if o.FlagObjectType == FlagObjectTypeTree { + commit, err := repo.CommitObject(head.Hash()) + if err != nil { + return fmt.Errorf("error getting tree: %w", err) + } + sha = commit.TreeHash + + refName = attTreeRef + } + + sv, err := sign.SignerFromKeyOpts(ctx, "", "", cosignopts.KeyOpts{ + FulcioURL: o.Config.Fulcio, + RekorURL: o.Config.Rekor, + OIDCIssuer: o.Config.Issuer, + OIDCClientID: o.Config.ClientID, + }) + if err != nil { + return fmt.Errorf("getting signer: %w", err) + } + defer sv.Close() + + attestor := attest.NewAttestor(repo, sv, cosign.TLogUploadInTotoAttestation) + + out, err := attestor.WriteFile(ctx, refName, sha, o.FlagPath, o.FlagAttestationType) + if err != nil { + return err + } + fmt.Println(out) + + return nil +} + +func New(cfg *config.Config) *cobra.Command { + o := &options{ + Config: cfg, + } + cmd := &cobra.Command{ + Use: "attest", + Short: "add attestations to Git objects", + Args: cobra.ArbitraryArgs, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + return o.Run(ctx) + }, + } + o.AddFlags(cmd) + + return cmd +} diff --git a/internal/commands/root/root.go b/internal/commands/root/root.go index fdd0e293..57d7a22d 100644 --- a/internal/commands/root/root.go +++ b/internal/commands/root/root.go @@ -18,6 +18,7 @@ package root import ( "github.com/spf13/cobra" + "github.com/sigstore/gitsign/internal/commands/attest" "github.com/sigstore/gitsign/internal/commands/show" "github.com/sigstore/gitsign/internal/commands/version" "github.com/sigstore/gitsign/internal/config" @@ -82,6 +83,7 @@ func New(cfg *config.Config) *cobra.Command { rootCmd.AddCommand(version.New(cfg)) rootCmd.AddCommand(show.New(cfg)) + rootCmd.AddCommand(attest.New(cfg)) o.AddFlags(rootCmd) return rootCmd