mirror of
https://github.com/slsa-framework/slsa-verifier.git
synced 2026-05-06 08:37:00 +00:00
refactor: add subcommands and separate functionality from artifacts a… (#231)
* refactor: add subcommands and separate functionality from artifacts and images Signed-off-by: Asra Ali <asraa@google.com>
This commit is contained in:
@@ -1,202 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/errors"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/options"
|
||||
"github.com/slsa-framework/slsa-verifier/verifiers"
|
||||
"github.com/slsa-framework/slsa-verifier/verifiers/container"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type workflowInputs struct {
|
||||
kv map[string]string
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
provenancePath string
|
||||
builderID string
|
||||
artifactPath string
|
||||
artifactImage string
|
||||
source string
|
||||
branch string
|
||||
tag string
|
||||
versiontag string
|
||||
inputs workflowInputs
|
||||
printProvenance bool
|
||||
)
|
||||
|
||||
func experimentalEnabled() bool {
|
||||
func ExperimentalEnabled() bool {
|
||||
return os.Getenv("SLSA_VERIFIER_EXPERIMENTAL") == "1"
|
||||
}
|
||||
|
||||
func (i *workflowInputs) String() string {
|
||||
return fmt.Sprintf("%v", i.kv)
|
||||
}
|
||||
|
||||
func (i *workflowInputs) Set(value string) error {
|
||||
l := strings.Split(value, "=")
|
||||
if len(l) != 2 {
|
||||
return fmt.Errorf("%w: expected 'key=value' format, got '%s'", serrors.ErrorInvalidFormat, value)
|
||||
func rootCmd() *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "slsa-verifier",
|
||||
Short: "Verify SLSA provenance for Github Actions",
|
||||
Long: `Verify SLSA provenance for Github Actions.
|
||||
For more information on SLSA, visit https://slsa.dev`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("expected command")
|
||||
},
|
||||
}
|
||||
i.kv[l[0]] = l[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *workflowInputs) AsMap() map[string]string {
|
||||
return i.kv
|
||||
c.AddCommand(versionCmd())
|
||||
c.AddCommand(verifyArtifactCmd())
|
||||
c.AddCommand(verifyImageCmd())
|
||||
// We print our own errors and usage in the check function.
|
||||
c.SilenceErrors = true
|
||||
return c
|
||||
}
|
||||
|
||||
func main() {
|
||||
if experimentalEnabled() {
|
||||
flag.StringVar(&builderID, "builder-id", "", "EXPERIMENTAL: the unique builder ID who created the provenance")
|
||||
}
|
||||
flag.StringVar(&provenancePath, "provenance", "", "path to a provenance file")
|
||||
flag.StringVar(&artifactPath, "artifact-path", "", "path to an artifact to verify")
|
||||
flag.StringVar(&artifactImage, "artifact-image", "", "name of the OCI image to verify")
|
||||
flag.StringVar(&source, "source", "",
|
||||
"expected source repository that should have produced the binary, e.g. github.com/some/repo")
|
||||
flag.StringVar(&branch, "branch", "", "[optional] expected branch the binary was compiled from")
|
||||
flag.StringVar(&tag, "tag", "", "[optional] expected tag the binary was compiled from")
|
||||
flag.StringVar(&versiontag, "versioned-tag", "",
|
||||
"[optional] expected version the binary was compiled from. Uses semantic version to match the tag")
|
||||
flag.BoolVar(&printProvenance, "print-provenance", false,
|
||||
"print the verified provenance to std out")
|
||||
inputs.kv = make(map[string]string)
|
||||
flag.Var(&inputs, "workflow-input",
|
||||
"[optional] a workflow input provided by a user at trigger time in the format 'key=value'. (Only for 'workflow_dispatch' events).")
|
||||
flag.Parse()
|
||||
|
||||
if artifactImage != "" && artifactPath != "" {
|
||||
fmt.Fprintf(os.Stderr, "'artifact-image' and 'artifact-path' cannot be specified together\n")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if source == "" {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var pbuilderID, pbranch, ptag, pversiontag *string
|
||||
|
||||
// Note: nil tag, version-tag and builder-id means we ignore them during verification.
|
||||
if isFlagPassed("tag") {
|
||||
ptag = &tag
|
||||
}
|
||||
if isFlagPassed("versioned-tag") {
|
||||
pversiontag = &versiontag
|
||||
}
|
||||
if experimentalEnabled() && isFlagPassed("builder-id") {
|
||||
pbuilderID = &builderID
|
||||
}
|
||||
if isFlagPassed("branch") {
|
||||
pbranch = &branch
|
||||
}
|
||||
|
||||
if ptag != nil && pversiontag != nil {
|
||||
fmt.Fprintf(os.Stderr, "'version' and 'tag' options cannot be used together\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
verifiedProvenance, _, err := runVerify(artifactImage, artifactPath, provenancePath, source,
|
||||
pbranch, pbuilderID, ptag, pversiontag, inputs.AsMap(), nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FAILED: SLSA verification failed: %v\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "PASSED: Verified SLSA provenance\n")
|
||||
if printProvenance {
|
||||
fmt.Fprintf(os.Stdout, "%s\n", string(verifiedProvenance))
|
||||
}
|
||||
}
|
||||
|
||||
func isFlagPassed(name string) bool {
|
||||
found := false
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
if f.Name == name {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
type ComputeDigestFn func(string) (string, error)
|
||||
|
||||
func runVerify(artifactImage, artifactPath, provenancePath, source string,
|
||||
branch, builderID, ptag, pversiontag *string, inputs map[string]string,
|
||||
fn ComputeDigestFn,
|
||||
) ([]byte, string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Artifact hash retrieval depends on the artifact type.
|
||||
artifactHash, err := getArtifactHash(artifactImage, artifactPath, fn)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
provenanceOpts := &options.ProvenanceOpts{
|
||||
ExpectedSourceURI: source,
|
||||
ExpectedBranch: branch,
|
||||
ExpectedDigest: artifactHash,
|
||||
ExpectedVersionedTag: pversiontag,
|
||||
ExpectedTag: ptag,
|
||||
ExpectedWorkflowInputs: inputs,
|
||||
}
|
||||
|
||||
builderOpts := &options.BuilderOpts{
|
||||
ExpectedID: builderID,
|
||||
}
|
||||
|
||||
var provenance []byte
|
||||
if provenancePath != "" {
|
||||
provenance, err = os.ReadFile(provenancePath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
return verifiers.Verify(ctx, artifactImage, provenance, artifactHash, provenanceOpts, builderOpts)
|
||||
}
|
||||
|
||||
func getArtifactHash(artifactImage, artifactPath string,
|
||||
// This function is used to handle unit tests and adapt
|
||||
// digest computation to local images.
|
||||
fn ComputeDigestFn,
|
||||
) (string, error) {
|
||||
if artifactPath != "" {
|
||||
f, err := os.Open(artifactPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
// Retrieve the image digest.
|
||||
if fn == nil {
|
||||
fn = container.GetImageDigest
|
||||
}
|
||||
digest, err := fn(artifactImage)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Verify that the reference is immutable.
|
||||
if err := container.ValidateArtifactReference(artifactImage, digest); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return digest, nil
|
||||
check(rootCmd().Execute())
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/sigstore/cosign/pkg/oci"
|
||||
"github.com/sigstore/cosign/pkg/oci/layout"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/cli/slsa-verifier/verify"
|
||||
serrors "github.com/slsa-framework/slsa-verifier/errors"
|
||||
"github.com/slsa-framework/slsa-verifier/verifiers/container"
|
||||
)
|
||||
@@ -486,13 +487,20 @@ func Test_runVerifyArtifactPath(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, v := range checkVersions {
|
||||
|
||||
artifactPath := filepath.Clean(filepath.Join(TEST_DIR, v, tt.artifact))
|
||||
provenancePath := fmt.Sprintf("%s.intoto.jsonl", artifactPath)
|
||||
_, outBuilderID, err := runVerify("", artifactPath,
|
||||
provenancePath,
|
||||
tt.source, tt.pbranch, tt.pbuilderID,
|
||||
tt.ptag, tt.pversiontag, tt.inputs, nil)
|
||||
|
||||
cmd := verify.VerifyArtifactCommand{
|
||||
ProvenancePath: provenancePath,
|
||||
SourceURI: tt.source,
|
||||
SourceBranch: tt.pbranch,
|
||||
BuilderID: tt.pbuilderID,
|
||||
SourceTag: tt.ptag,
|
||||
SourceVersionTag: tt.pversiontag,
|
||||
BuildWorkflowInputs: tt.inputs,
|
||||
}
|
||||
|
||||
outBuilderID, err := cmd.Exec(context.Background(), []string{artifactPath})
|
||||
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
|
||||
@@ -505,6 +513,7 @@ func Test_runVerifyArtifactPath(t *testing.T) {
|
||||
if tt.outBuilderID != "" && outBuilderID != tt.outBuilderID {
|
||||
t.Errorf(cmp.Diff(outBuilderID, tt.outBuilderID))
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -646,9 +655,16 @@ func Test_runVerifyGHAArtifactImage(t *testing.T) {
|
||||
for _, v := range checkVersions {
|
||||
image := filepath.Clean(filepath.Join(TEST_DIR, v, tt.artifact))
|
||||
|
||||
_, outBuilderID, err := runVerify(image, "", "",
|
||||
tt.source, tt.pbranch, tt.pbuilderID,
|
||||
tt.ptag, tt.pversiontag, nil, localDigestComputeFn)
|
||||
cmd := verify.VerifyImageCommand{
|
||||
SourceURI: tt.source,
|
||||
SourceBranch: tt.pbranch,
|
||||
BuilderID: tt.pbuilderID,
|
||||
SourceTag: tt.ptag,
|
||||
SourceVersionTag: tt.pversiontag,
|
||||
DigestFn: localDigestComputeFn,
|
||||
}
|
||||
|
||||
outBuilderID, err := cmd.Exec(context.Background(), []string{image})
|
||||
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
|
||||
@@ -840,15 +856,23 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
|
||||
for _, v := range checkVersions {
|
||||
provenance := filepath.Clean(filepath.Join(TEST_DIR, v, tt.provenance))
|
||||
image := tt.artifact
|
||||
var fn ComputeDigestFn
|
||||
var fn verify.ComputeDigestFn
|
||||
if !tt.oci {
|
||||
image = filepath.Clean(filepath.Join(TEST_DIR, v, image))
|
||||
fn = localDigestComputeFn
|
||||
}
|
||||
|
||||
_, outBuilderID, err := runVerify(image, "", provenance,
|
||||
tt.source, nil, tt.pbuilderID,
|
||||
nil, nil, nil, fn)
|
||||
cmd := verify.VerifyImageCommand{
|
||||
SourceURI: tt.source,
|
||||
SourceBranch: nil,
|
||||
BuilderID: tt.pbuilderID,
|
||||
SourceTag: nil,
|
||||
SourceVersionTag: nil,
|
||||
DigestFn: fn,
|
||||
ProvenancePath: &provenance,
|
||||
}
|
||||
|
||||
outBuilderID, err := cmd.Exec(context.Background(), []string{image})
|
||||
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
|
||||
@@ -861,6 +885,7 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
|
||||
if tt.outBuilderID != "" && outBuilderID != tt.outBuilderID {
|
||||
t.Errorf(cmp.Diff(outBuilderID, tt.outBuilderID))
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
130
cli/slsa-verifier/verify.go
Normal file
130
cli/slsa-verifier/verify.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2022 SLSA 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
|
||||
//
|
||||
// https://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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/cli/slsa-verifier/verify"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
SUCCESS = "PASSED: Verified SLSA provenance"
|
||||
FAILURE = "FAILED: SLSA verification failed"
|
||||
)
|
||||
|
||||
func verifyArtifactCmd() *cobra.Command {
|
||||
o := &verify.VerifyOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "verify-artifact",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("expects a single path to an artifact")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Short: "Verifies SLSA provenance on an artifact blob",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := verify.VerifyArtifactCommand{
|
||||
ProvenancePath: o.ProvenancePath,
|
||||
SourceURI: o.SourceURI,
|
||||
PrintProvenance: o.PrintProvenance,
|
||||
BuildWorkflowInputs: o.BuildWorkflowInputs.AsMap(),
|
||||
}
|
||||
if cmd.Flags().Changed("source-branch") {
|
||||
v.SourceTag = &o.SourceBranch
|
||||
}
|
||||
if cmd.Flags().Changed("source-tag") {
|
||||
v.SourceTag = &o.SourceTag
|
||||
}
|
||||
if cmd.Flags().Changed("source-versioned-tag") {
|
||||
v.SourceVersionTag = &o.SourceVersionTag
|
||||
}
|
||||
if cmd.Flags().Changed("builder-id") {
|
||||
if !ExperimentalEnabled() {
|
||||
return fmt.Errorf("builder-id only supported with experimental flag")
|
||||
}
|
||||
v.BuilderID = &o.BuilderID
|
||||
}
|
||||
|
||||
if _, err := v.Exec(cmd.Context(), args); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", FAILURE, err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "%s\n", SUCCESS)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
o.AddFlags(cmd)
|
||||
cmd.MarkFlagRequired("provenance-path")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func verifyImageCmd() *cobra.Command {
|
||||
o := &verify.VerifyOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "verify-image",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("expects a single path to an image")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Short: "Verifies SLSA provenance on a container image",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := verify.VerifyImageCommand{
|
||||
SourceURI: o.SourceURI,
|
||||
PrintProvenance: o.PrintProvenance,
|
||||
BuildWorkflowInputs: o.BuildWorkflowInputs.AsMap(),
|
||||
}
|
||||
if cmd.Flags().Changed("provenance-path") {
|
||||
v.ProvenancePath = &o.ProvenancePath
|
||||
}
|
||||
if cmd.Flags().Changed("source-branch") {
|
||||
v.SourceTag = &o.SourceBranch
|
||||
}
|
||||
if cmd.Flags().Changed("source-tag") {
|
||||
v.SourceTag = &o.SourceTag
|
||||
}
|
||||
if cmd.Flags().Changed("source-versioned-tag") {
|
||||
v.SourceVersionTag = &o.SourceVersionTag
|
||||
}
|
||||
if cmd.Flags().Changed("builder-id") {
|
||||
if !ExperimentalEnabled() {
|
||||
return fmt.Errorf("builder-id only supported with experimental flag")
|
||||
}
|
||||
v.BuilderID = &o.BuilderID
|
||||
}
|
||||
|
||||
if _, err := v.Exec(cmd.Context(), args); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", FAILURE, err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "%s\n", SUCCESS)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
o.AddFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
100
cli/slsa-verifier/verify/options.go
Normal file
100
cli/slsa-verifier/verify/options.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2022 SLSA 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
|
||||
//
|
||||
// https://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 verify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
// AddFlags adds this options' flags to the cobra command.
|
||||
AddFlags(cmd *cobra.Command)
|
||||
}
|
||||
|
||||
// VerifyOptions is the top-level options for all `verify` commands.
|
||||
type VerifyOptions struct {
|
||||
/* Source requirements */
|
||||
SourceURI string
|
||||
SourceBranch string
|
||||
SourceTag string
|
||||
SourceVersionTag string
|
||||
/* Builder Requirements */
|
||||
BuildWorkflowInputs workflowInputs
|
||||
BuilderID string
|
||||
/* Other */
|
||||
ProvenancePath string
|
||||
PrintProvenance bool
|
||||
}
|
||||
|
||||
var _ Interface = (*VerifyOptions)(nil)
|
||||
|
||||
// AddFlags implements Interface
|
||||
func (o *VerifyOptions) AddFlags(cmd *cobra.Command) {
|
||||
/* Builder options */
|
||||
cmd.Flags().Var(&o.BuildWorkflowInputs, "build-workflow-input",
|
||||
"[optional] a workflow input provided by a user at trigger time in the format 'key=value'. (Only for 'workflow_dispatch' events on GitHub Actions).")
|
||||
|
||||
cmd.Flags().StringVar(&o.BuilderID, "builder-id", "", "EXPERIMENTAL: the unique builder ID who created the provenance")
|
||||
|
||||
/* Source options */
|
||||
cmd.Flags().StringVar(&o.SourceURI, "source-uri", "",
|
||||
"expected source repository that should have produced the binary, e.g. github.com/some/repo")
|
||||
|
||||
cmd.Flags().StringVar(&o.SourceBranch, "source-branch", "", "[optional] expected branch the binary was compiled from")
|
||||
|
||||
cmd.Flags().StringVar(&o.SourceTag, "source-tag", "", "[optional] expected tag the binary was compiled from")
|
||||
|
||||
cmd.Flags().StringVar(&o.SourceVersionTag, "source-versioned-tag", "",
|
||||
"[optional] expected version the binary was compiled from. Uses semantic version to match the tag")
|
||||
|
||||
/* Other options */
|
||||
cmd.Flags().StringVar(&o.ProvenancePath, "provenance-path", "",
|
||||
"path to a provenance file")
|
||||
|
||||
cmd.Flags().BoolVar(&o.PrintProvenance, "print-provenance", false,
|
||||
"print the verified provenance to stdout")
|
||||
|
||||
cmd.MarkFlagRequired("source-uri")
|
||||
cmd.MarkFlagsMutuallyExclusive("source-versioned-tag", "source-tag")
|
||||
}
|
||||
|
||||
type workflowInputs struct {
|
||||
kv map[string]string
|
||||
}
|
||||
|
||||
func (i *workflowInputs) Type() string {
|
||||
return fmt.Sprintf("%v", i.kv)
|
||||
}
|
||||
|
||||
func (i *workflowInputs) String() string {
|
||||
return fmt.Sprintf("%v", i.kv)
|
||||
}
|
||||
|
||||
func (i *workflowInputs) Set(value string) error {
|
||||
l := strings.Split(value, "=")
|
||||
if len(l) != 2 {
|
||||
return fmt.Errorf("%w: expected 'key=value' format, got '%s'", serrors.ErrorInvalidFormat, value)
|
||||
}
|
||||
i.kv[l[0]] = l[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *workflowInputs) AsMap() map[string]string {
|
||||
return i.kv
|
||||
}
|
||||
88
cli/slsa-verifier/verify/verify_artifact.go
Normal file
88
cli/slsa-verifier/verify/verify_artifact.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 SLSA 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
|
||||
//
|
||||
// https://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 verify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/options"
|
||||
"github.com/slsa-framework/slsa-verifier/verifiers"
|
||||
)
|
||||
|
||||
// Note: nil branch, tag, version-tag and builder-id means we ignore them during verification.
|
||||
type VerifyArtifactCommand struct {
|
||||
ProvenancePath string
|
||||
BuilderID *string
|
||||
SourceURI string
|
||||
SourceBranch *string
|
||||
SourceTag *string
|
||||
SourceVersionTag *string
|
||||
BuildWorkflowInputs map[string]string
|
||||
PrintProvenance bool
|
||||
}
|
||||
|
||||
func (c *VerifyArtifactCommand) Exec(ctx context.Context, artifacts []string) (string, error) {
|
||||
artifactHash, err := getArtifactHash(artifacts[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
provenanceOpts := &options.ProvenanceOpts{
|
||||
ExpectedSourceURI: c.SourceURI,
|
||||
ExpectedBranch: c.SourceBranch,
|
||||
ExpectedDigest: artifactHash,
|
||||
ExpectedVersionedTag: c.SourceVersionTag,
|
||||
ExpectedTag: c.SourceTag,
|
||||
ExpectedWorkflowInputs: c.BuildWorkflowInputs,
|
||||
}
|
||||
|
||||
builderOpts := &options.BuilderOpts{
|
||||
ExpectedID: c.BuilderID,
|
||||
}
|
||||
|
||||
provenance, err := os.ReadFile(c.ProvenancePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
verifiedProvenance, outBuilderID, err := verifiers.VerifyArtifact(ctx, provenance, artifactHash, provenanceOpts, builderOpts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if c.PrintProvenance {
|
||||
fmt.Fprintf(os.Stdout, "%s\n", string(verifiedProvenance))
|
||||
}
|
||||
|
||||
return outBuilderID, nil
|
||||
}
|
||||
|
||||
func getArtifactHash(artifactPath string) (string, error) {
|
||||
f, err := os.Open(artifactPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
90
cli/slsa-verifier/verify/verify_image.go
Normal file
90
cli/slsa-verifier/verify/verify_image.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2022 SLSA 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
|
||||
//
|
||||
// https://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 verify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/options"
|
||||
"github.com/slsa-framework/slsa-verifier/verifiers"
|
||||
"github.com/slsa-framework/slsa-verifier/verifiers/container"
|
||||
)
|
||||
|
||||
type ComputeDigestFn func(string) (string, error)
|
||||
|
||||
// Note: nil branch, tag, version-tag and builder-id means we ignore them during verification.
|
||||
type VerifyImageCommand struct {
|
||||
// May be nil if supplied alongside in the registry
|
||||
ProvenancePath *string
|
||||
BuilderID *string
|
||||
SourceURI string
|
||||
SourceBranch *string
|
||||
SourceTag *string
|
||||
SourceVersionTag *string
|
||||
BuildWorkflowInputs map[string]string
|
||||
PrintProvenance bool
|
||||
DigestFn ComputeDigestFn
|
||||
}
|
||||
|
||||
func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (string, error) {
|
||||
artifactImage := artifacts[0]
|
||||
// Retrieve the image digest.
|
||||
if c.DigestFn == nil {
|
||||
c.DigestFn = container.GetImageDigest
|
||||
}
|
||||
digest, err := c.DigestFn(artifactImage)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Verify that the reference is immutable.
|
||||
if err := container.ValidateArtifactReference(artifactImage, digest); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
provenanceOpts := &options.ProvenanceOpts{
|
||||
ExpectedSourceURI: c.SourceURI,
|
||||
ExpectedBranch: c.SourceBranch,
|
||||
ExpectedDigest: digest,
|
||||
ExpectedVersionedTag: c.SourceVersionTag,
|
||||
ExpectedTag: c.SourceTag,
|
||||
ExpectedWorkflowInputs: c.BuildWorkflowInputs,
|
||||
}
|
||||
|
||||
builderOpts := &options.BuilderOpts{
|
||||
ExpectedID: c.BuilderID,
|
||||
}
|
||||
|
||||
var provenance []byte
|
||||
if c.ProvenancePath != nil {
|
||||
provenance, err = os.ReadFile(*c.ProvenancePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
verifiedProvenance, outBuilderID, err := verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceOpts, builderOpts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if c.PrintProvenance {
|
||||
fmt.Fprintf(os.Stdout, "%s\n", string(verifiedProvenance))
|
||||
}
|
||||
|
||||
return outBuilderID, nil
|
||||
}
|
||||
33
cli/slsa-verifier/version.go
Normal file
33
cli/slsa-verifier/version.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2022 SLSA 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
|
||||
//
|
||||
// https://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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/version"
|
||||
)
|
||||
|
||||
func versionCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version and exit",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(version.Version)
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user