From 737f99d41ab30c160590077bd890e1bddb740813 Mon Sep 17 00:00:00 2001 From: Benji Visser Date: Fri, 4 Aug 2023 15:36:03 -0600 Subject: [PATCH] Notary signing policy (#93) Signed-off-by: Benji Visser --- cmd/root.go | 37 ++- go.mod | 77 +++-- go.sum | 155 ++++++---- .../sigverifier/notary/auth/credentials.go | 44 +++ internal/sigverifier/notary/common.go | 24 ++ internal/sigverifier/notary/errors/errors.go | 39 +++ internal/sigverifier/notary/manifest.go | 121 ++++++++ internal/sigverifier/notary/notary.go | 90 ++++++ internal/sigverifier/notary/registry.go | 131 ++++++++ .../sigverifier/notary/version/version.go | 24 ++ internal/ui/common_event_handlers.go | 37 ++- internal/ui/ephemeral_terminal_ui.go | 14 +- internal/ui/logger_ui.go | 8 +- internal/xeolio/request.go | 87 +----- xeol/event/event.go | 3 +- xeol/event/parsers/parsers.go | 20 +- xeol/policy/eol/eol.go | 279 ++++++++++++++++++ .../{policy_test.go => eol/eol_test.go} | 207 +++++++------ xeol/policy/notary/notary.go | 142 +++++++++ xeol/policy/notary/notary_test.go | 66 +++++ xeol/policy/policy.go | 255 +++------------- xeol/policy/types/types.go | 69 +++++ xeol/report/model.go | 14 +- 23 files changed, 1426 insertions(+), 517 deletions(-) create mode 100644 internal/sigverifier/notary/auth/credentials.go create mode 100644 internal/sigverifier/notary/common.go create mode 100644 internal/sigverifier/notary/errors/errors.go create mode 100644 internal/sigverifier/notary/manifest.go create mode 100644 internal/sigverifier/notary/notary.go create mode 100644 internal/sigverifier/notary/registry.go create mode 100644 internal/sigverifier/notary/version/version.go create mode 100644 xeol/policy/eol/eol.go rename xeol/policy/{policy_test.go => eol/eol_test.go} (72%) create mode 100644 xeol/policy/notary/notary.go create mode 100644 xeol/policy/notary/notary_test.go create mode 100644 xeol/policy/types/types.go diff --git a/cmd/root.go b/cmd/root.go index d03a9cef..7666c233 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -35,6 +35,7 @@ import ( pkgMatcher "github.com/xeol-io/xeol/xeol/matcher/packages" "github.com/xeol-io/xeol/xeol/pkg" "github.com/xeol-io/xeol/xeol/policy" + "github.com/xeol-io/xeol/xeol/policy/types" "github.com/xeol-io/xeol/xeol/presenter" "github.com/xeol-io/xeol/xeol/presenter/models" "github.com/xeol-io/xeol/xeol/report" @@ -258,7 +259,7 @@ func startWorker(userInput string, failOnEolFound bool, eolMatchDate time.Time) var pkgContext pkg.Context var wg = &sync.WaitGroup{} var loadedDB, gatheredPackages bool - var policies []xeolio.Policy + var policies []policy.Policy x := xeolio.NewXeolClient(appConfig.APIKey) wg.Add(3) @@ -326,6 +327,24 @@ func startWorker(userInput string, failOnEolFound bool, eolMatchDate time.Time) DBStatus: status, } + var failScan bool + var imageVerified bool + for _, p := range policies { + switch p.GetPolicyType() { + case types.PolicyTypeNotary: + shouldFailScan, res := p.Evaluate(allMatches, appConfig.ProjectName, userInput) + imageVerified = res.GetVerified() + if shouldFailScan { + failScan = true + } + case types.PolicyTypeEol: + shouldFailScan, _ := p.Evaluate(allMatches, appConfig.ProjectName, userInput) + if shouldFailScan { + failScan = true + } + } + } + if appConfig.APIKey != "" { buf := new(bytes.Buffer) bom := cyclonedxhelpers.ToFormatModel(*sbom) @@ -336,20 +355,20 @@ func startWorker(userInput string, failOnEolFound bool, eolMatchDate time.Time) } if err := x.SendEvent(report.XeolEventPayload{ - Matches: allMatches.Sorted(), - Packages: packages, - Context: pkgContext, - AppConfig: appConfig, - ImageName: sbom.Source.ImageMetadata.UserInput, - Sbom: base64.StdEncoding.EncodeToString(buf.Bytes()), + Matches: allMatches.Sorted(), + Packages: packages, + Context: pkgContext, + AppConfig: appConfig, + ImageName: sbom.Source.ImageMetadata.UserInput, + ImageDigest: sbom.Source.ImageMetadata.ManifestDigest, + ImageVerified: imageVerified, + Sbom: base64.StdEncoding.EncodeToString(buf.Bytes()), }); err != nil { errs <- fmt.Errorf("failed to send eol event: %w", err) return } } - failScan := policy.Evaluate(policies, allMatches, appConfig.ProjectName) - bus.Publish(partybus.Event{ Type: event.EolScanningFinished, Value: presenter.GetPresenter(presenterConfig, pb), diff --git a/go.mod b/go.mod index 5f978261..b5c99e1b 100644 --- a/go.mod +++ b/go.mod @@ -15,10 +15,11 @@ require ( github.com/anchore/stereoscope v0.0.0-20230609190519-5b5049bf4d3a github.com/anchore/syft v0.83.1 github.com/bmatcuk/doublestar/v2 v2.0.4 + github.com/docker/distribution v2.8.2+incompatible github.com/docker/docker v24.0.2+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 - github.com/gabriel-vasile/mimetype v1.4.0 + github.com/gabriel-vasile/mimetype v1.4.2 github.com/go-git/go-git/v5 v5.7.0 github.com/go-test/deep v1.1.0 github.com/google/go-cmp v0.5.9 @@ -34,7 +35,12 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/mapstructure v1.5.0 + github.com/notaryproject/notation v1.0.0-rc.7 + github.com/notaryproject/notation-go v1.0.0 github.com/olekukonko/tablewriter v0.0.5 + github.com/opencontainers/go-digest v1.0.0 + github.com/opencontainers/image-spec v1.1.0-rc4 + github.com/oras-project/oras-credentials-go v0.3.0 github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e github.com/sergi/go-diff v1.3.1 github.com/sirupsen/logrus v1.9.3 @@ -46,39 +52,41 @@ require ( github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5 github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb - golang.org/x/term v0.9.0 + golang.org/x/term v0.10.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/gorm v1.23.5 + oras.land/oras-go/v2 v2.2.1 ) require ( cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.19.1 // indirect + cloud.google.com/go/compute v1.19.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.13.0 // indirect + cloud.google.com/go/iam v1.0.1 // indirect cloud.google.com/go/storage v1.28.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230626094100-7e9e0395ebec // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/andybalholm/brotli v1.0.4 // indirect - github.com/aws/aws-sdk-go v1.44.114 // indirect + github.com/aws/aws-sdk-go v1.44.268 // indirect github.com/becheran/wildmatch-go v1.0.0 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/containerd/containerd v1.7.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect github.com/docker/cli v23.0.5+incompatible // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -86,23 +94,27 @@ require ( github.com/emirpasic/gods v1.18.1 // indirect github.com/etdub/goparsetime v0.0.0-20160315173935-ea17b0ac3318 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fxamacker/cbor/v2 v2.4.0 // indirect github.com/github/go-spdx/v2 v2.1.2 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect + github.com/go-ldap/ldap/v3 v3.4.5 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-containerregistry v0.15.2 // indirect github.com/google/licensecheck v0.3.1 // indirect - github.com/google/s2a-go v0.1.3 // indirect + github.com/google/s2a-go v0.1.4 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.8.0 // indirect + github.com/googleapis/gax-go/v2 v2.9.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huandu/xstrings v1.3.3 // indirect + github.com/hashicorp/hcl v1.0.1-vault-5 // indirect + github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -118,26 +130,26 @@ require ( github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/nwaples/rardecode v1.1.0 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc3 // indirect + github.com/notaryproject/notation-core-go v1.0.0 // indirect + github.com/nwaples/rardecode v1.1.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pierrec/lz4/v4 v4.1.15 // indirect + github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/sassoftware/go-rpmutils v0.2.0 // indirect - github.com/shopspring/decimal v1.2.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect github.com/skeema/knownhosts v1.1.1 // indirect github.com/spdx/tools-golang v0.5.2 // indirect github.com/spf13/cast v1.5.1 // indirect @@ -150,31 +162,34 @@ require ( github.com/ulikunitz/xz v0.5.10 // indirect github.com/vbatts/go-mtree v0.5.3 // indirect github.com/vbatts/tar-split v0.11.3 // indirect + github.com/veraison/go-cose v1.1.0 // indirect github.com/vifraa/gopom v0.2.1 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.opencensus.io v0.24.0 // indirect - go.uber.org/goleak v1.2.0 // indirect - golang.org/x/crypto v0.10.0 // indirect - golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b // indirect - golang.org/x/mod v0.10.0 // indirect + go.uber.org/goleak v1.2.1 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.11.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.9.0 // indirect - golang.org/x/text v0.10.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect + golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.8.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.122.0 // indirect + google.golang.org/api v0.124.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/genproto v0.0.0-20230525154841-bd750badd5c6 // indirect google.golang.org/grpc v1.55.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.1.0 // indirect + gotest.tools/v3 v3.4.0 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect diff --git a/go.sum b/go.sum index 35dc3d57..de6bc19a 100644 --- a/go.sum +++ b/go.sum @@ -37,16 +37,15 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= +cloud.google.com/go/iam v1.0.1 h1:lyeCAU6jpnVNrE9zGQkTl3WgNgK/X+uWwaw0kynZJMU= +cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -60,7 +59,10 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI= cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -74,16 +76,17 @@ github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJ github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= -github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= +github.com/ProtonMail/go-crypto v0.0.0-20230626094100-7e9e0395ebec h1:vV3RryLxt42+ZIVOFbYJCH1jsZNTNmj2NYru5zfx+4E= +github.com/ProtonMail/go-crypto v0.0.0-20230626094100-7e9e0395ebec/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= @@ -96,6 +99,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 h1:imgMA0gN0TZx7PSa/pdWqXadBvrz8WsN6zySzCe4XX0= github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8/go.mod h1:+gPap4jha079qzRTUaehv+UZ6sSdaNwkH0D3b6zhTuk= github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU= @@ -127,8 +132,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= -github.com/aws/aws-sdk-go v1.44.114 h1:plIkWc/RsHr3DXBj4MEw9sEW4CcL/e2ryokc+CKyq1I= -github.com/aws/aws-sdk-go v1.44.114/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.268 h1:WoK20tlAvsvQzTcE6TajoprbXmTbcud6MjhErL4P/38= +github.com/aws/aws-sdk-go v1.44.268/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA= github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -142,7 +147,7 @@ github.com/bmatcuk/doublestar/v2 v2.0.4/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQm github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -155,7 +160,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -178,8 +182,9 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M= github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk= github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= @@ -229,13 +234,17 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= -github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= +github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= +github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/github/go-spdx/v2 v2.1.2 h1:p+Tv0yMgcuO0/vnMe9Qh4tmUgYhI6AsLVlakZ/Sx+DM= github.com/github/go-spdx/v2 v2.1.2/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w= github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= +github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= @@ -248,6 +257,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= +github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc= @@ -260,6 +271,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -342,8 +355,8 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE= -github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -354,8 +367,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= -github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.9.1 h1:DpTpJqzZ3NvX9zqjhIuI1oVzYZMvboZe+3LoeEIJjHM= +github.com/googleapis/gax-go/v2 v2.9.1/go.mod h1:4FG3gMrVZlyMp5itSYKMU9z/lBE7+SbnUOvzH2HqbEY= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE= @@ -397,8 +410,9 @@ github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= +github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= @@ -406,8 +420,9 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -492,8 +507,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -539,14 +554,23 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= +github.com/notaryproject/notation v1.0.0-rc.7 h1:AlqhyRdSXN+oZPjjAl4Dlr6qbWEFO6B8rVlQ+b4hYJM= +github.com/notaryproject/notation v1.0.0-rc.7/go.mod h1:KdJSsYETE/Nvt+leH18UInDaaCXJy1mKZzlBt2rOKiU= +github.com/notaryproject/notation-core-go v1.0.0 h1:FgOAihtFW4XU9JYyTzItg1xW3OaN4eCasw5Bp00Ydu4= +github.com/notaryproject/notation-core-go v1.0.0/go.mod h1:eoHFJ2e6b31GZO9hckCms5kfXvHLTySvJ1QwRLB9ZCk= +github.com/notaryproject/notation-go v1.0.0 h1:pH+0NVmZu1IhE8zUhK9Oxna3OlHNdy+crNntnuCiThs= +github.com/notaryproject/notation-go v1.0.0/go.mod h1:NpfUnDt94vLSCJ8fAWplgTbf3fmq3JLSEnjDFl7j16U= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M= +github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= -github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= +github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/oras-project/oras-credentials-go v0.3.0 h1:Bg1d9iAmgo50RlaIy2XI5MQs7qL00DB3R9Q4JRP1VWs= +github.com/oras-project/oras-credentials-go v0.3.0/go.mod h1:fFCebDQo0Do+gnM96uV9YUnRay0pwuRQupypvofsp4s= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -555,8 +579,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= +github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -565,8 +589,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -588,7 +613,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= @@ -602,8 +628,9 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -629,7 +656,6 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= @@ -671,6 +697,8 @@ github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w github.com/vbatts/go-mtree v0.5.3/go.mod h1:eXsdoPMdL2jcJx6HweWi9lYQxBsTp4lNhqqAjgkZUg8= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= +github.com/veraison/go-cose v1.1.0 h1:AalPS4VGiKavpAzIlBjrn7bhqXiXi4jbMYY/2+UC+4o= +github.com/veraison/go-cose v1.1.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= github.com/vifraa/gopom v0.2.1 h1:MYVMAMyiGzXPPy10EwojzKIL670kl5Zbae+o3fFvQEM= github.com/vifraa/gopom v0.2.1/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5 h1:phTLPgMRDYTizrBSKsNSOa2zthoC2KsJsaY/8sg3rD8= @@ -679,6 +707,8 @@ github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 h1:lwgTsTy18 github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA= github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb h1:Yz6VVOcLuWLAHYlJzTw7JKnWxdV/WXpug2X0quEzRnY= github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb/go.mod h1:nDi3BAC5nEbVbg+WSJDHLbjHv0ZToq8nMPA97XMxF3E= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= @@ -706,8 +736,8 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -728,9 +758,10 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -741,8 +772,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b h1:EqBVA+nNsObCwQoBEHy4wLU0pi7i8a4AL3pbItPdPkE= -golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -771,8 +802,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -814,11 +845,10 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= @@ -841,8 +871,8 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -855,8 +885,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -926,7 +957,6 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -935,21 +965,23 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -963,12 +995,13 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1068,8 +1101,8 @@ google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.122.0 h1:zDobeejm3E7pEG1mNHvdxvjs5XJoCMzyNH+CmwL94Es= -google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= +google.golang.org/api v0.124.0 h1:dP6Ef1VgOGqQ8eiv4GiY8RhmeyqzovcXBYPDUYG8Syo= +google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1143,8 +1176,8 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20230525154841-bd750badd5c6 h1:62QuyPXKEkZpjZesyj5K5jABl6MnSnWl+vNuT5oz90E= +google.golang.org/genproto v0.0.0-20230525154841-bd750badd5c6/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1219,8 +1252,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM= gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= -gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1274,6 +1307,8 @@ modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +oras.land/oras-go/v2 v2.2.1 h1:3VJTYqy5KfelEF9c2jo1MLSpr+TM3mX8K42wzZcd6qE= +oras.land/oras-go/v2 v2.2.1/go.mod h1:GeAwLuC4G/JpNwkd+bSZ6SkDMGaaYglt6YK2WvZP7uQ= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/sigverifier/notary/auth/credentials.go b/internal/sigverifier/notary/auth/credentials.go new file mode 100644 index 00000000..c803dab1 --- /dev/null +++ b/internal/sigverifier/notary/auth/credentials.go @@ -0,0 +1,44 @@ +package auth + +import ( + "fmt" + + "github.com/notaryproject/notation-go/dir" + credentials "github.com/oras-project/oras-credentials-go" +) + +// NewCredentialsStore returns a new credentials store from the settings in the +// configuration file. +func NewCredentialsStore() (credentials.Store, error) { + configPath, err := dir.ConfigFS().SysPath(dir.PathConfigFile) + if err != nil { + return nil, fmt.Errorf("failed to load config file: %w", err) + } + + // use notation config + opts := credentials.StoreOptions{AllowPlaintextPut: false} + notationStore, err := credentials.NewStore(configPath, opts) + if err != nil { + return nil, fmt.Errorf("failed to create credential store from config file: %w", err) + } + if notationStore.IsAuthConfigured() { + return notationStore, nil + } + + // use docker config + dockerStore, err := credentials.NewStoreFromDocker(opts) + if err != nil { + return nil, fmt.Errorf("failed to create credential store from docker config file: %w", err) + } + if dockerStore.IsAuthConfigured() { + return dockerStore, nil + } + + // detect platform-default native store + if osDefaultStore, ok := credentials.NewDefaultNativeStore(); ok { + return osDefaultStore, nil + } + // if the default store is not available, still use notation store so that + // there won't be errors when getting credentials + return notationStore, nil +} diff --git a/internal/sigverifier/notary/common.go b/internal/sigverifier/notary/common.go new file mode 100644 index 00000000..76998795 --- /dev/null +++ b/internal/sigverifier/notary/common.go @@ -0,0 +1,24 @@ +package notary + +import ( + "oras.land/oras-go/v2/registry/remote/auth" +) + +type SecureFlagOpts struct { + Username string + Password string + InsecureRegistry bool +} + +// Credential returns an auth.Credential from opts.Username and opts.Password. +func (opts *SecureFlagOpts) Credential() auth.Credential { + if opts.Username == "" { + return auth.Credential{ + RefreshToken: opts.Password, + } + } + return auth.Credential{ + Username: opts.Username, + Password: opts.Password, + } +} diff --git a/internal/sigverifier/notary/errors/errors.go b/internal/sigverifier/notary/errors/errors.go new file mode 100644 index 00000000..6d56cb21 --- /dev/null +++ b/internal/sigverifier/notary/errors/errors.go @@ -0,0 +1,39 @@ +package errors + +import "fmt" + +// ErrorReferrersAPINotSupported is used when the target registry does not +// support the Referrers API +type ErrorReferrersAPINotSupported struct { + Msg string +} + +func (e ErrorReferrersAPINotSupported) Error() string { + if e.Msg != "" { + return e.Msg + } + return "referrers API not supported" +} + +// ErrorOCILayoutMissingReference is used when signing local content in oci +// layout folder but missing input tag or digest. +type ErrorOCILayoutMissingReference struct { + Msg string +} + +func (e ErrorOCILayoutMissingReference) Error() string { + if e.Msg != "" { + return e.Msg + } + return "reference is missing either digest or tag" +} + +// ErrorExceedMaxSignatures is used when the number of signatures has surpassed +// the maximum limit that can be evaluated. +type ErrorExceedMaxSignatures struct { + MaxSignatures int +} + +func (e ErrorExceedMaxSignatures) Error() string { + return fmt.Sprintf("exceeded configured limit of max signatures %d to examine", e.MaxSignatures) +} diff --git a/internal/sigverifier/notary/manifest.go b/internal/sigverifier/notary/manifest.go new file mode 100644 index 00000000..fdb4c855 --- /dev/null +++ b/internal/sigverifier/notary/manifest.go @@ -0,0 +1,121 @@ +package notary + +import ( + "context" + "errors" + "fmt" + "os" + "strings" + "unicode" + + notationregistry "github.com/notaryproject/notation-go/registry" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "oras.land/oras-go/v2/registry" + + "github.com/xeol-io/xeol/internal/log" + notationerrors "github.com/xeol-io/xeol/internal/sigverifier/notary/errors" +) + +func resolveReferenceWithWarning(ctx context.Context, inputType inputType, reference string, sigRepo notationregistry.Repository, operation string) (ocispec.Descriptor, string, error) { + return resolveReference(ctx, inputType, reference, sigRepo, func(ref string, manifestDesc ocispec.Descriptor) { + fmt.Fprintf(os.Stderr, "Warning: Always %s the artifact using digest(@sha256:...) rather than a tag(:%s) because resolved digest may not point to the same signed artifact, as tags are mutable.\n", operation, ref) + }) +} + +// resolveReference resolves user input reference based on user input type. +// Returns the resolved manifest descriptor and resolvedRef in digest +func resolveReference(ctx context.Context, inputType inputType, reference string, sigRepo notationregistry.Repository, fn func(string, ocispec.Descriptor)) (ocispec.Descriptor, string, error) { + // sanity check + if reference == "" { + return ocispec.Descriptor{}, "", errors.New("missing user input reference") + } + var tagOrDigestRef string + var resolvedRef string + switch inputType { + case inputTypeRegistry: + ref, err := registry.ParseReference(reference) + if err != nil { + return ocispec.Descriptor{}, "", fmt.Errorf("failed to resolve user input reference: %w", err) + } + tagOrDigestRef = ref.Reference + resolvedRef = ref.Registry + "/" + ref.Repository + case inputTypeOCILayout: + layoutPath, layoutReference, err := parseOCILayoutReference(reference) + if err != nil { + return ocispec.Descriptor{}, "", fmt.Errorf("failed to resolve user input reference: %w", err) + } + layoutPathInfo, err := os.Stat(layoutPath) + if err != nil { + return ocispec.Descriptor{}, "", fmt.Errorf("failed to resolve user input reference: %w", err) + } + if !layoutPathInfo.IsDir() { + return ocispec.Descriptor{}, "", errors.New("failed to resolve user input reference: input path is not a dir") + } + tagOrDigestRef = layoutReference + resolvedRef = layoutPath + default: + return ocispec.Descriptor{}, "", fmt.Errorf("unsupported user inputType: %d", inputType) + } + + manifestDesc, err := getManifestDescriptor(ctx, tagOrDigestRef, sigRepo) + if err != nil { + return ocispec.Descriptor{}, "", fmt.Errorf("failed to get manifest descriptor: %w", err) + } + resolvedRef = resolvedRef + "@" + manifestDesc.Digest.String() + if _, err := digest.Parse(tagOrDigestRef); err == nil { + // tagOrDigestRef is a digest reference + if tagOrDigestRef != manifestDesc.Digest.String() { + // tagOrDigestRef does not match the resolved digest + return ocispec.Descriptor{}, "", fmt.Errorf("user input digest %s does not match the resolved digest %s", tagOrDigestRef, manifestDesc.Digest.String()) + } + return manifestDesc, resolvedRef, nil + } + // tagOrDigestRef is a tag reference + if fn != nil { + fn(tagOrDigestRef, manifestDesc) + } + return manifestDesc, resolvedRef, nil +} + +// parseOCILayoutReference parses the raw in format of [:|@]. +// Returns the path to the OCI layout and the reference (tag or digest). +func parseOCILayoutReference(raw string) (string, string, error) { + var path string + var ref string + + if idx := strings.LastIndex(raw, "@"); idx != -1 { + // `digest` found + path, ref = raw[:idx], raw[idx+1:] + } else { + // find `tag` + idx := strings.LastIndex(raw, ":") + if idx == -1 || (idx == 1 && len(raw) > 2 && unicode.IsLetter(rune(raw[0])) && raw[2] == '\\') { + return "", "", notationerrors.ErrorOCILayoutMissingReference{} + } + path, ref = raw[:idx], raw[idx+1:] + } + + if path == "" { + return "", "", fmt.Errorf("found empty file path in %q", raw) + } + if ref == "" { + return "", "", fmt.Errorf("found empty reference in %q", raw) + } + + return path, ref, nil +} + +// getManifestDescriptor returns target artifact's manifest descriptor given +// reference (digest or tag) and Repository. +func getManifestDescriptor(ctx context.Context, reference string, sigRepo notationregistry.Repository) (ocispec.Descriptor, error) { + if reference == "" { + return ocispec.Descriptor{}, errors.New("reference cannot be empty") + } + manifestDesc, err := sigRepo.Resolve(ctx, reference) + if err != nil { + return ocispec.Descriptor{}, err + } + log.Infof("Reference %s resolved to manifest descriptor: %+v", reference, manifestDesc) + return manifestDesc, nil +} diff --git a/internal/sigverifier/notary/notary.go b/internal/sigverifier/notary/notary.go new file mode 100644 index 00000000..a6ab9237 --- /dev/null +++ b/internal/sigverifier/notary/notary.go @@ -0,0 +1,90 @@ +package notary + +import ( + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "strings" + + "github.com/notaryproject/notation-go" + "github.com/notaryproject/notation-go/dir" + "github.com/notaryproject/notation-go/plugin" + "github.com/notaryproject/notation-go/verifier" + "github.com/notaryproject/notation-go/verifier/trustpolicy" + "github.com/notaryproject/notation-go/verifier/truststore" +) + +const ( + DefaultRegistry = "docker.io" +) + +func decodeBase64NotaryJSON(encoded string) (trustpolicy.Document, error) { + decoded, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return trustpolicy.Document{}, err + } + + var policies trustpolicy.Document + err = json.Unmarshal(decoded, &policies) + if err != nil { + return trustpolicy.Document{}, err + } + + return policies, nil +} + +func Verify(ctx context.Context, reference string, policy string) error { + // add default docker registry if a registry is not specified + if !strings.Contains(reference, "/") { + reference = fmt.Sprintf("%s/%s", DefaultRegistry, reference) + } + + trustpolicy, err := decodeBase64NotaryJSON(policy) + if err != nil { + return err + } + x509TrustStore := truststore.NewX509TrustStore(dir.ConfigFS()) + plugins := plugin.NewCLIManager(dir.PluginFS()) + + sigVerifier, err := verifier.New(&trustpolicy, x509TrustStore, plugins) + if err != nil { + return err + } + + secureFlagOpts := &SecureFlagOpts{} + inputType := inputTypeRegistry // remote registry by default + sigRepo, err := getRepository(ctx, inputType, reference, secureFlagOpts, false) + if err != nil { + return err + } + _, resolvedRef, err := resolveReferenceWithWarning(ctx, inputType, reference, sigRepo, "inspect") + if err != nil { + return err + } + + verifyOpts := notation.VerifyOptions{ + ArtifactReference: resolvedRef, + MaxSignatureAttempts: 100, + } + _, outcomes, err := notation.Verify(ctx, sigVerifier, sigRepo, verifyOpts) + err = checkVerificationFailure(outcomes, resolvedRef, err) + if err != nil { + return err + } + return nil +} + +func checkVerificationFailure(outcomes []*notation.VerificationOutcome, printOut string, err error) error { + if err != nil || len(outcomes) == 0 { + if err != nil { + var errorVerificationFailed notation.ErrorVerificationFailed + if !errors.As(err, &errorVerificationFailed) { + return fmt.Errorf("signature verification failed: %w", err) + } + } + return fmt.Errorf("signature verification failed for all the signatures associated with %s", printOut) + } + return nil +} diff --git a/internal/sigverifier/notary/registry.go b/internal/sigverifier/notary/registry.go new file mode 100644 index 00000000..b1188a3c --- /dev/null +++ b/internal/sigverifier/notary/registry.go @@ -0,0 +1,131 @@ +package notary + +import ( + "context" + "errors" + "fmt" + "net" + + notationregistry "github.com/notaryproject/notation-go/registry" + "github.com/notaryproject/notation/pkg/configutil" + credentials "github.com/oras-project/oras-credentials-go" + "oras.land/oras-go/v2/registry" + "oras.land/oras-go/v2/registry/remote" + "oras.land/oras-go/v2/registry/remote/auth" + + notationauth "github.com/xeol-io/xeol/internal/sigverifier/notary/auth" + "github.com/xeol-io/xeol/internal/sigverifier/notary/version" +) + +// inputType denotes the user input type +type inputType int + +const ( + inputTypeRegistry inputType = 1 + iota // inputType remote registry + inputTypeOCILayout // inputType oci-layout +) + +// getRepository returns a notationregistry.Repository given user input +// type and user input reference +func getRepository(ctx context.Context, inputType inputType, reference string, opts *SecureFlagOpts, allowReferrersAPI bool) (notationregistry.Repository, error) { + switch inputType { + case inputTypeRegistry: + return getRemoteRepository(ctx, opts, reference, allowReferrersAPI) + case inputTypeOCILayout: + layoutPath, _, err := parseOCILayoutReference(reference) + if err != nil { + return nil, err + } + return notationregistry.NewOCIRepository(layoutPath, notationregistry.RepositoryOptions{}) + default: + return nil, errors.New("unsupported input type") + } +} + +// getRemoteRepository returns a registry.Repository. +// When experimental feature is disabled OR allowReferrersAPI is not set, +// Notation always uses referrers tag schema to store and consume signatures +// by default. +// When experimental feature is enabled AND allowReferrersAPI is set, Notation +// tries the Referrers API, if not supported, fallback to use the Referrers +// tag schema. +// +// References: +// https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#listing-referrers +// https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema +func getRemoteRepository(ctx context.Context, opts *SecureFlagOpts, reference string, _ bool) (notationregistry.Repository, error) { + ref, err := registry.ParseReference(reference) + if err != nil { + return nil, err + } + + // generate notation repository + remoteRepo, err := getRepositoryClient(ctx, opts, ref) + if err != nil { + return nil, err + } + + if err := remoteRepo.SetReferrersCapability(false); err != nil { + return nil, err + } + return notationregistry.NewRepository(remoteRepo), nil +} + +func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference) (*remote.Repository, error) { + authClient, insecureRegistry, err := getAuthClient(ctx, opts, ref, true) + if err != nil { + return nil, err + } + + return &remote.Repository{ + Client: authClient, + Reference: ref, + PlainHTTP: insecureRegistry, + }, nil +} + +// getAuthClient returns an *auth.Client and a bool indicating if the registry +// is insecure. +// +// If withCredential is true, the returned *auth.Client will have its Credential +// function configured. +// +// If withCredential is false, the returned *auth.Client will have a nil +// Credential function. +func getAuthClient(_ context.Context, opts *SecureFlagOpts, ref registry.Reference, withCredential bool) (*auth.Client, bool, error) { + var insecureRegistry bool + if opts.InsecureRegistry { + insecureRegistry = opts.InsecureRegistry + } else { + insecureRegistry = configutil.IsRegistryInsecure(ref.Registry) + if !insecureRegistry { + if host, _, _ := net.SplitHostPort(ref.Registry); host == "localhost" { + insecureRegistry = true + } + } + } + + // build authClient + authClient := &auth.Client{ + Cache: auth.NewCache(), + ClientID: "notation", + } + authClient.SetUserAgent("notation/" + version.GetVersion()) + if !withCredential { + return authClient, insecureRegistry, nil + } + + cred := opts.Credential() + if cred != auth.EmptyCredential { + // use the specified credential + authClient.Credential = auth.StaticCredential(ref.Host(), cred) + } else { + // use saved credentials + credsStore, err := notationauth.NewCredentialsStore() + if err != nil { + return nil, false, fmt.Errorf("failed to get credentials store: %w", err) + } + authClient.Credential = credentials.Credential(credsStore) + } + return authClient, insecureRegistry, nil +} diff --git a/internal/sigverifier/notary/version/version.go b/internal/sigverifier/notary/version/version.go new file mode 100644 index 00000000..6b696285 --- /dev/null +++ b/internal/sigverifier/notary/version/version.go @@ -0,0 +1,24 @@ +package version + +var ( + // Version shows the current notation version, optionally with pre-release. + Version = "v1.0.0-rc.7" + + // BuildMetadata stores the build metadata. + // + // When execute `make build` command, it will be overridden by + // environment variable `BUILD_METADATA`. If commit tag was set, + // BuildMetadata will be empty. + BuildMetadata = "unreleased" + + // GitCommit stores the git HEAD commit id + GitCommit = "" +) + +// GetVersion returns the version string in SemVer 2. +func GetVersion() string { + if BuildMetadata == "" { + return Version + } + return Version + "+" + BuildMetadata +} diff --git a/internal/ui/common_event_handlers.go b/internal/ui/common_event_handlers.go index 90629435..9a89f973 100644 --- a/internal/ui/common_event_handlers.go +++ b/internal/ui/common_event_handlers.go @@ -8,24 +8,47 @@ import ( "github.com/wagoodman/go-partybus" xeolEventParsers "github.com/xeol-io/xeol/xeol/event/parsers" - "github.com/xeol-io/xeol/xeol/policy" + policyTypes "github.com/xeol-io/xeol/xeol/policy/types" ) -func handlePolicyEvaluationMessage(event partybus.Event, reportOutput io.Writer) error { +func handleNotaryPolicyEvaluationMessage(event partybus.Event, reportOutput io.Writer) error { // show the report to stdout - pt, err := xeolEventParsers.ParsePolicyEvaluationMessage(event) + nt, err := xeolEventParsers.ParseNotaryPolicyEvaluationMessage(event) if err != nil { return fmt.Errorf("bad %s event: %w", event.Type, err) } var message string - if pt.Type == policy.PolicyTypeDeny { - message = color.Red.Sprintf("[%s] Policy Violation: %s (v%s) needs to upgraded to a newer version. This scan will now exit non-zero.\n", pt.Type, pt.ProductName, pt.Cycle) + if nt.Action == policyTypes.PolicyActionDeny { + message = color.Red.Sprintf("[%s][%s] Policy Violation: image '%s' is not signed by a trusted party.\n", nt.Action, nt.Type, nt.ImageReference) + } else { + if nt.FailDate != "" { + message = color.Yellow.Sprintf("[%s][%s] Policy Violation: image '%s' is not signed by a trusted party. This policy will fail builds starting on %s.\n", nt.Action, nt.Type, nt.ImageReference, nt.FailDate) + } else { + message = color.Yellow.Sprintf("[%s][%s] Policy Violation: image '%s' is not signed by a trusted party.\n", nt.Action, nt.Type, nt.ImageReference) + } + } + if _, err := reportOutput.Write([]byte(message)); err != nil { + return fmt.Errorf("unable to show policy evaluation message: %w", err) + } + return nil +} + +func handleEolPolicyEvaluationMessage(event partybus.Event, reportOutput io.Writer) error { + // show the report to stdout + pt, err := xeolEventParsers.ParseEolPolicyEvaluationMessage(event) + if err != nil { + return fmt.Errorf("bad %s event: %w", event.Type, err) + } + + var message string + if pt.Action == policyTypes.PolicyActionDeny { + message = color.Red.Sprintf("[%s][%s] Policy Violation: %s (v%s) needs to be upgraded to a newer version.\n", pt.Action, pt.Type, pt.ProductName, pt.Cycle) } else { if pt.FailDate != "" { - message = color.Yellow.Sprintf("[%s] Policy Violation: %s (v%s) needs to be upgraded to a newer version. This policy will fail builds starting on %s.\n", pt.Type, pt.ProductName, pt.Cycle, pt.FailDate) + message = color.Yellow.Sprintf("[%s][%s] Policy Violation: %s (v%s) needs to be upgraded to a newer version. This policy will fail builds starting on %s.\n", pt.Action, pt.Type, pt.ProductName, pt.Cycle, pt.FailDate) } else { - message = color.Yellow.Sprintf("[%s] Policy Violation: %s (v%s) needs to be upgraded to a newer version.\n", pt.Type, pt.ProductName, pt.Cycle) + message = color.Yellow.Sprintf("[%s][%s] Policy Violation: %s (v%s) needs to be upgraded to a newer version.\n", pt.Action, pt.Type, pt.ProductName, pt.Cycle) } } if _, err := reportOutput.Write([]byte(message)); err != nil { diff --git a/internal/ui/ephemeral_terminal_ui.go b/internal/ui/ephemeral_terminal_ui.go index 43b6860b..fcfe684f 100644 --- a/internal/ui/ephemeral_terminal_ui.go +++ b/internal/ui/ephemeral_terminal_ui.go @@ -77,12 +77,22 @@ func (h *ephemeralTerminalUI) Handle(event partybus.Event) error { if err := h.handler.Handle(ctx, h.frame, event, h.waitGroup); err != nil { log.Errorf("unable to show %s event: %+v", event.Type, err) } - case event.Type == xeolEvent.PolicyEvaluationMessage: + + case event.Type == xeolEvent.NotaryPolicyEvaluationMessage: + // we need to close the screen now since signaling the the presenter is ready means that we + // are about to write bytes to stdout, so we should reset the terminal state first + h.closeScreen(false) + + if err := handleNotaryPolicyEvaluationMessage(event, h.reportOutput); err != nil { + log.Errorf("unable to show %s event: %+v", event.Type, err) + } + + case event.Type == xeolEvent.EolPolicyEvaluationMessage: // we need to close the screen now since signaling the the presenter is ready means that we // are about to write bytes to stdout, so we should reset the terminal state first h.closeScreen(false) - if err := handlePolicyEvaluationMessage(event, h.reportOutput); err != nil { + if err := handleEolPolicyEvaluationMessage(event, h.reportOutput); err != nil { log.Errorf("unable to show %s event: %+v", event.Type, err) } diff --git a/internal/ui/logger_ui.go b/internal/ui/logger_ui.go index 4b7f72b8..fb44a853 100644 --- a/internal/ui/logger_ui.go +++ b/internal/ui/logger_ui.go @@ -28,8 +28,12 @@ func (l *loggerUI) Setup(unsubscribe func() error) error { func (l loggerUI) Handle(event partybus.Event) error { switch event.Type { - case xeolEvent.PolicyEvaluationMessage: - if err := handlePolicyEvaluationMessage(event, l.reportOutput); err != nil { + case xeolEvent.NotaryPolicyEvaluationMessage: + if err := handleNotaryPolicyEvaluationMessage(event, l.reportOutput); err != nil { + log.Warnf("unable to show policy evaluation message event: %+v", err) + } + case xeolEvent.EolPolicyEvaluationMessage: + if err := handleEolPolicyEvaluationMessage(event, l.reportOutput); err != nil { log.Warnf("unable to show policy evaluation message event: %+v", err) } case xeolEvent.EolScanningFinished: diff --git a/internal/xeolio/request.go b/internal/xeolio/request.go index c229b4ef..a8189295 100644 --- a/internal/xeolio/request.go +++ b/internal/xeolio/request.go @@ -6,93 +6,18 @@ import ( "fmt" "io" "net/http" - "strings" "time" "github.com/xeol-io/xeol/internal/log" + "github.com/xeol-io/xeol/xeol/policy" "github.com/xeol-io/xeol/xeol/report" ) -type PolicyType string -type PolicyScope string -type CycleOperator string - const ( XeolAPIURL = "https://api.xeol.io" XeolEngineURL = "https://engine.xeol.io" - - PolicyTypeEol PolicyType = "EOL" - - PolicyScopeGlobal PolicyScope = "global" - PolicyScopeProject PolicyScope = "project" - PolicyScopeSoftware PolicyScope = "software" - - CycleOperatorLessThan CycleOperator = "LT" - CycleOperatorLessThanOrEqual CycleOperator = "LTE" - CycleOperatorEqual CycleOperator = "EQ" ) -type Policy struct { - ID string `json:"id"` - // the policy scope can be one of: global, project, software - // global: the policy applies to all projects and software - // project: the policy applies to all software in a project - // software: the policy applies to a specific software - PolicyScope PolicyScope `json:"policy_scope"` - // the type of policy [eol] - PolicyType PolicyType `json:"policy_type"` - // the date which to start warning xeol scans - WarnDate string `json:"warn_date,omitempty"` - // the date which to start failing xeol scans - DenyDate string `json:"deny_date,omitempty"` - // the days before eol to start warning xeol scans - WarnDays *int `json:"warn_days,omitempty"` - // the days before eol to start failing xeol scans - DenyDays *int `json:"deny_days,omitempty"` - // the project name to match policy against. Valid when PolicyScope is 'project' - ProjectName string `json:"project_name,omitempty"` - // - // the following fields are only used when PolicyScope is 'software' - // - // the product name to match policy against. - ProductName string `json:"product_name,omitempty"` - // the cycle to match policy against. - Cycle string `json:"cycle,omitempty"` - // the cycle operator to match policy against. - CycleOperator CycleOperator `json:"cycle_operator,omitempty"` -} - -func (ps *PolicyScope) UnmarshalJSON(b []byte) error { - str := strings.Trim(string(b), "\"") - switch str { - case string(PolicyScopeGlobal), string(PolicyScopeProject), string(PolicyScopeSoftware): - *ps = PolicyScope(str) - default: - return fmt.Errorf("invalid PolicyScope %s", str) - } - return nil -} - -func (pt *PolicyType) UnmarshalJSON(b []byte) error { - str := strings.Trim(string(b), "\"") - if str != string(PolicyTypeEol) { - return fmt.Errorf("invalid PolicyType %s", str) - } - *pt = PolicyType(str) - return nil -} - -func (co *CycleOperator) UnmarshalJSON(b []byte) error { - str := strings.Trim(string(b), "\"") - switch str { - case string(CycleOperatorLessThan), string(CycleOperatorLessThanOrEqual), string(CycleOperatorEqual): - *co = CycleOperator(str) - default: - return nil - } - return nil -} - type XeolClient struct { APIKey string } @@ -130,20 +55,20 @@ func (x *XeolClient) makeRequest(method, url, path string, body io.Reader, out i return fmt.Errorf("xeol.io API response decode failed: %v", err) } } else { - log.Debug("sent event to xeol.io API at %s", req.URL.String()) + log.Debugf("sent event to xeol.io API at %s", req.URL.String()) } return nil } -func (x *XeolClient) FetchPolicies() ([]Policy, error) { - var policies []Policy - err := x.makeRequest("GET", XeolAPIURL, "v1/policy", nil, &policies) +func (x *XeolClient) FetchPolicies() ([]policy.Policy, error) { + var raw json.RawMessage + err := x.makeRequest("GET", XeolAPIURL, "v2/policy", nil, &raw) if err != nil { return nil, err } - return policies, nil + return policy.UnmarshalPolicies(raw) } func (x *XeolClient) SendEvent(payload report.XeolEventPayload) error { diff --git a/xeol/event/event.go b/xeol/event/event.go index e8f7a242..964feacd 100644 --- a/xeol/event/event.go +++ b/xeol/event/event.go @@ -7,7 +7,8 @@ const ( UpdateEolDatabase partybus.EventType = "xeol-update-eol-database" EolScanningStarted partybus.EventType = "xeol-eol-scanning-started" EolScanningFinished partybus.EventType = "xeol-eol-scanning-finished" - PolicyEvaluationMessage partybus.EventType = "xeol-policy-evaluation-message" + EolPolicyEvaluationMessage partybus.EventType = "xeol-eol-policy-evaluation-message" + NotaryPolicyEvaluationMessage partybus.EventType = "xeol-notary-policy-evaluation-message" AttestationVerified partybus.EventType = "xeol-attestation-signature-passed" AttestationVerificationSkipped partybus.EventType = "xeol-attestation-verification-skipped" NonRootCommandFinished partybus.EventType = "xeol-non-root-command-finished" diff --git a/xeol/event/parsers/parsers.go b/xeol/event/parsers/parsers.go index 915a740b..faed0f19 100644 --- a/xeol/event/parsers/parsers.go +++ b/xeol/event/parsers/parsers.go @@ -8,7 +8,7 @@ import ( "github.com/xeol-io/xeol/xeol/event" "github.com/xeol-io/xeol/xeol/matcher" - "github.com/xeol-io/xeol/xeol/policy" + policyTypes "github.com/xeol-io/xeol/xeol/policy/types" "github.com/xeol-io/xeol/xeol/presenter" ) @@ -37,12 +37,24 @@ func checkEventType(actual, expected partybus.EventType) error { return nil } -func ParsePolicyEvaluationMessage(e partybus.Event) (*policy.EvaluationResult, error) { - if err := checkEventType(e.Type, event.PolicyEvaluationMessage); err != nil { +func ParseNotaryPolicyEvaluationMessage(e partybus.Event) (*policyTypes.NotaryEvaluationResult, error) { + if err := checkEventType(e.Type, event.NotaryPolicyEvaluationMessage); err != nil { return nil, err } - pt, ok := e.Value.(policy.EvaluationResult) + pt, ok := e.Value.(policyTypes.NotaryEvaluationResult) + if !ok { + return nil, newPayloadErr(e.Type, "Value", e.Value) + } + return &pt, nil +} + +func ParseEolPolicyEvaluationMessage(e partybus.Event) (*policyTypes.EolEvaluationResult, error) { + if err := checkEventType(e.Type, event.EolPolicyEvaluationMessage); err != nil { + return nil, err + } + + pt, ok := e.Value.(policyTypes.EolEvaluationResult) if !ok { return nil, newPayloadErr(e.Type, "Value", e.Value) } diff --git a/xeol/policy/eol/eol.go b/xeol/policy/eol/eol.go new file mode 100644 index 00000000..dd603c42 --- /dev/null +++ b/xeol/policy/eol/eol.go @@ -0,0 +1,279 @@ +package eol + +import ( + "math" + "sort" + "time" + + "github.com/Masterminds/semver" + "github.com/wagoodman/go-partybus" + + "github.com/xeol-io/xeol/internal/bus" + "github.com/xeol-io/xeol/internal/log" + "github.com/xeol-io/xeol/xeol/event" + "github.com/xeol-io/xeol/xeol/match" + "github.com/xeol-io/xeol/xeol/policy/types" +) + +var timeNow = time.Now + +const ( + DateLayout = "2006-01-02" + CycleOperatorLessThan CycleOperator = "LT" + CycleOperatorLessThanOrEqual CycleOperator = "LTE" + CycleOperatorEqual CycleOperator = "EQ" + + PolicyScopeGlobal PolicyScope = "global" + PolicyScopeProject PolicyScope = "project" + PolicyScopeSoftware PolicyScope = "software" + + // set a max days for deny/warn policies + // to avoid overflow/underflow errors when + // calculating dates. 10 years should be + // more than enough for most use cases + MaxNumDays = 10 * 365 +) + +type PolicyWrapper struct { + PolicyType types.PolicyType `json:"PolicyType"` + Policies []Policy `json:"Policies"` +} + +type Policy struct { + ID string `json:"ID"` + PolicyType types.PolicyType `json:"PolicyType"` + // the policy scope can be one of: global, project, software + // global: the policy applies to all projects and software + // project: the policy applies to all software in a project + // software: the policy applies to a specific software + PolicyScope PolicyScope `json:"PolicyScope"` + // the date which to start warning xeol scans + WarnDate string `json:"WarnDate,omitempty"` + // the date which to start failing xeol scans + DenyDate string `json:"DenyDate,omitempty"` + // the days before eol to start warning xeol scans + WarnDays *int `json:"WarnDays,omitempty"` + // the days before eol to start failing xeol scans + DenyDays *int `json:"DenyDays,omitempty"` + // the project name to match policy against. Valid when PolicyScope is 'project' + ProjectName string `json:"ProjectName,omitempty"` + // + // the following fields are only used when PolicyScope is 'software' + // + // the product name to match policy against. + ProductName string `json:"ProductName,omitempty"` + // the cycle to match policy against. + Cycle string `json:"Cycle,omitempty"` + // the cycle operator to match policy against. + CycleOperator CycleOperator `json:"CycleOperator,omitempty"` +} + +type CycleOperator string +type PolicyScope string + +type ByPolicyScope []Policy + +func (a ByPolicyScope) Len() int { return len(a) } +func (a ByPolicyScope) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByPolicyScope) Less(i, j int) bool { + // The priority will be: software > project > global + switch a[i].PolicyScope { + case PolicyScopeSoftware: + return true + case PolicyScopeProject: + return a[j].PolicyScope != PolicyScopeSoftware + case PolicyScopeGlobal: + return a[j].PolicyScope == PolicyScopeGlobal + default: + // Handle unknown cases + return false + } +} + +func (e PolicyWrapper) GetPolicyType() types.PolicyType { + return e.PolicyType +} + +func (e PolicyWrapper) Evaluate(matches match.Matches, projectName string, _ string) (bool, types.PolicyEvaluationResult) { + policyMatches := evaluateMatches(e.Policies, matches, projectName) + + // whether we should fail the scan or not + failScan := false + + for _, policyMatch := range policyMatches { + if policyMatch.Action == types.PolicyActionDeny { + failScan = true + } + bus.Publish(partybus.Event{ + Type: event.EolPolicyEvaluationMessage, + Value: policyMatch, + }) + } + + return failScan, types.EolEvaluationResult{} +} + +func evaluateMatches(policies []Policy, matches match.Matches, projectName string) []types.EolEvaluationResult { + var results []types.EolEvaluationResult + + // keep track of which matches have been evaluated + // so we don't evaluate the same match twice + evaluatedMatches := make(map[string]bool) + + // policies are first sorted by scope according to + // this order: software > project > global + // software policies are evaluated first, then project + // policies, then global policies + sort.Stable(ByPolicyScope(policies)) + + for _, policy := range policies { + policyCopy := policy + for _, match := range matches.Sorted() { + if evaluatedMatches[match.Cycle.ProductName] { + continue + } + + switch policy.PolicyScope { + case PolicyScopeSoftware: + if !cycleOperatorMatch(match, policy) { + continue + } + case PolicyScopeProject: + if policy.ProjectName != projectName { + continue + } + } + + // deny policy takes precedence over warn policy, so order is important here + if denyMatch(&policyCopy, match) { + results = append(results, createEolEvaluationResult(policyCopy, match, types.PolicyActionDeny)) + evaluatedMatches[match.Cycle.ProductName] = true + continue + } + if warnMatch(&policyCopy, match) { + results = append(results, createEolEvaluationResult(policyCopy, match, types.PolicyActionWarn)) + evaluatedMatches[match.Cycle.ProductName] = true + } + } + } + return results +} + +func cycleOperatorMatch(m match.Match, policy Policy) bool { + if m.Cycle.ProductName != policy.ProductName { + return false + } + + pv, err := semver.NewVersion(policy.Cycle) + if err != nil { + log.Debugf("Invalid policy cycle version: %s", policy.Cycle) + return false + } + + mv, err := semver.NewVersion(m.Cycle.ReleaseCycle) + if err != nil { + log.Debugf("Invalid match cycle version: %s", m.Cycle.ReleaseCycle) + return false + } + + switch policy.CycleOperator { + case CycleOperatorLessThan: + return mv.LessThan(pv) + case CycleOperatorLessThanOrEqual: + return !mv.GreaterThan(pv) // equivalent to mv <= pv + case CycleOperatorEqual: + return mv.Equal(pv) + default: + log.Debugf("Invalid policy cycle operator: %s", policy.CycleOperator) + return false + } +} + +func warnMatch(policy *Policy, match match.Match) bool { + var warnDate time.Time + + if policy.WarnDate != "" { + var err error + warnDate, err = time.Parse(DateLayout, policy.WarnDate) + if err != nil { + log.Errorf("invalid policy warn date: %s", policy.WarnDate) + return false + } + } + + if policy.WarnDays != nil { + warnDays := *policy.WarnDays * -1 + if math.Abs(float64(warnDays)) > MaxNumDays { + log.Debugf("warn days (%d) is greater than max days, setting to max days (%d)", warnDays, MaxNumDays) + warnDays = MaxNumDays + } + eolDate, err := time.Parse(DateLayout, match.Cycle.Eol) + if err != nil { + log.Errorf("invalid eol date: %s, %s", match.Cycle.Eol, err) + return false + } + warnDate = eolDate.Add(time.Duration(warnDays) * time.Hour * 24) + } + + if warnDate.IsZero() { + return false + } + + if timeNow().After(warnDate) { + return true + } + + return false +} + +func denyMatch(policy *Policy, match match.Match) bool { + var denyDate time.Time + + if policy.DenyDate != "" { + var err error + denyDate, err = time.Parse(DateLayout, policy.DenyDate) + if err != nil { + log.Errorf("invalid policy deny date: %s", policy.DenyDate) + return false + } + } + + if policy.DenyDays != nil { + denyDays := *policy.DenyDays * -1 + if math.Abs(float64(denyDays)) > MaxNumDays { + log.Debugf("deny days (%d) is greater than max days, setting to max days (%d)", denyDays, MaxNumDays) + denyDays = MaxNumDays + } + + eolDate, err := time.Parse(DateLayout, match.Cycle.Eol) + if err != nil { + log.Errorf("invalid eol date: %s, %s", match.Cycle.Eol, err) + return false + } + denyDate = eolDate.Add(time.Duration(denyDays) * time.Hour * 24) + policy.DenyDate = denyDate.Format(DateLayout) + } + + if denyDate.IsZero() { + return false + } + + if timeNow().After(denyDate) { + return true + } + + return false +} + +func createEolEvaluationResult(policy Policy, match match.Match, policyAction types.PolicyAction) types.EolEvaluationResult { + result := types.EolEvaluationResult{ + Action: policyAction, + Type: types.PolicyTypeEol, + ProductName: match.Cycle.ProductName, + Cycle: match.Cycle.ReleaseCycle, + } + if policyAction == types.PolicyActionWarn { + result.FailDate = policy.DenyDate + } + return result +} diff --git a/xeol/policy/policy_test.go b/xeol/policy/eol/eol_test.go similarity index 72% rename from xeol/policy/policy_test.go rename to xeol/policy/eol/eol_test.go index 19f04f6f..0725a340 100644 --- a/xeol/policy/policy_test.go +++ b/xeol/policy/eol/eol_test.go @@ -1,4 +1,4 @@ -package policy +package eol import ( "reflect" @@ -9,10 +9,10 @@ import ( syftPkg "github.com/anchore/syft/syft/pkg" "github.com/google/uuid" - "github.com/xeol-io/xeol/internal/xeolio" "github.com/xeol-io/xeol/xeol/eol" "github.com/xeol-io/xeol/xeol/match" "github.com/xeol-io/xeol/xeol/pkg" + "github.com/xeol-io/xeol/xeol/policy/types" ) func Int(value int) *int { @@ -22,18 +22,18 @@ func Int(value int) *int { func TestEvaluate(t *testing.T) { tests := []struct { name string - policy []xeolio.Policy + policy []Policy matches []match.Match - want []EvaluationResult + want []types.EolEvaluationResult }{ { name: "policy with no matches", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.0.0", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorLessThan, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorLessThan, WarnDate: "2021-01-01", DenyDate: "2021-01-01", }, @@ -56,12 +56,12 @@ func TestEvaluate(t *testing.T) { }, { name: "policy with deny match", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.0", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorLessThanOrEqual, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorLessThanOrEqual, WarnDate: "2021-01-01", DenyDate: "2021-01-29", }, @@ -80,9 +80,10 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeDeny, + Action: types.PolicyActionDeny, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.0", }, @@ -90,12 +91,12 @@ func TestEvaluate(t *testing.T) { }, { name: "policy with warn match, version less than equal", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.0", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorLessThanOrEqual, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorLessThanOrEqual, WarnDate: "2021-01-01", DenyDate: "2021-03-01", }, @@ -114,9 +115,10 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeWarn, + Action: types.PolicyActionWarn, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.0", FailDate: "2021-03-01", @@ -125,12 +127,12 @@ func TestEvaluate(t *testing.T) { }, { name: "policy with warn match, version less than", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.0", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorLessThan, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorLessThan, WarnDate: "2021-01-01", DenyDate: "2021-03-01", }, @@ -149,9 +151,10 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeWarn, + Action: types.PolicyActionWarn, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "0.9", FailDate: "2021-03-01", @@ -160,12 +163,12 @@ func TestEvaluate(t *testing.T) { }, { name: "policy with warn match, version equal", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.0", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorEqual, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorEqual, WarnDate: "2021-01-01", DenyDate: "2021-03-01", }, @@ -184,9 +187,10 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeWarn, + Action: types.PolicyActionWarn, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.0", FailDate: "2021-03-01", @@ -195,20 +199,20 @@ func TestEvaluate(t *testing.T) { }, { name: "test multiple policy matches", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.3", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorEqual, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorEqual, WarnDate: "2021-01-01", DenyDate: "2021-03-01", }, { ProductName: "bar", Cycle: "2.1", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorLessThanOrEqual, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorLessThanOrEqual, WarnDate: "2021-01-01", DenyDate: "2021-01-29", }, @@ -239,14 +243,16 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeDeny, + Action: types.PolicyActionDeny, + Type: types.PolicyTypeEol, ProductName: "bar", Cycle: "2.0", }, { - Type: PolicyTypeWarn, + Action: types.PolicyActionWarn, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.3", FailDate: "2021-03-01", @@ -255,12 +261,12 @@ func TestEvaluate(t *testing.T) { }, { name: "test bad dates", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.3", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorEqual, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorEqual, WarnDate: "2021/01/01", DenyDate: "2021/03/01", }, @@ -283,12 +289,12 @@ func TestEvaluate(t *testing.T) { }, { name: "test bad cycles", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.4", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorEqual, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorEqual, WarnDate: "2021/01/01", DenyDate: "2021/03/01", }, @@ -311,12 +317,12 @@ func TestEvaluate(t *testing.T) { }, { name: "test future dates", - policy: []xeolio.Policy{ + policy: []Policy{ { ProductName: "foo", Cycle: "1.3", - PolicyScope: xeolio.PolicyScopeSoftware, - CycleOperator: xeolio.CycleOperatorEqual, + PolicyScope: PolicyScopeSoftware, + CycleOperator: CycleOperatorEqual, WarnDate: "2021-04-01", DenyDate: "2021-05-01", }, @@ -339,10 +345,10 @@ func TestEvaluate(t *testing.T) { }, { name: "test global policy", - policy: []xeolio.Policy{ + policy: []Policy{ { - PolicyScope: xeolio.PolicyScopeGlobal, - PolicyType: xeolio.PolicyTypeEol, + PolicyScope: PolicyScopeGlobal, + PolicyType: types.PolicyTypeEol, WarnDate: "2021-01-01", DenyDate: "2021-01-29", }, @@ -373,14 +379,16 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeDeny, + Action: types.PolicyActionDeny, + Type: types.PolicyTypeEol, ProductName: "bar", Cycle: "1.3", }, { - Type: PolicyTypeDeny, + Action: types.PolicyActionDeny, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.3", }, @@ -388,22 +396,22 @@ func TestEvaluate(t *testing.T) { }, { name: "test project and software precedence", - policy: []xeolio.Policy{ + policy: []Policy{ { - PolicyScope: xeolio.PolicyScopeProject, - PolicyType: xeolio.PolicyTypeEol, + PolicyScope: PolicyScopeProject, + PolicyType: types.PolicyTypeEol, WarnDate: "2021-01-17", DenyDate: "2021-01-18", ProjectName: "github//foo/bar", }, { - PolicyScope: xeolio.PolicyScopeSoftware, - PolicyType: xeolio.PolicyTypeEol, + PolicyScope: PolicyScopeSoftware, + PolicyType: types.PolicyTypeEol, WarnDate: "2021-01-29", DenyDate: "2021-02-29", ProductName: "foo", Cycle: "1.3", - CycleOperator: xeolio.CycleOperatorEqual, + CycleOperator: CycleOperatorEqual, }, }, matches: []match.Match{ @@ -432,15 +440,17 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeWarn, + Action: types.PolicyActionWarn, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.3", FailDate: "2021-02-29", }, { - Type: PolicyTypeDeny, + Action: types.PolicyActionDeny, + Type: types.PolicyTypeEol, ProductName: "bar", Cycle: "1.3", }, @@ -448,16 +458,16 @@ func TestEvaluate(t *testing.T) { }, { name: "test project and global precedence", - policy: []xeolio.Policy{ + policy: []Policy{ { - PolicyScope: xeolio.PolicyScopeGlobal, - PolicyType: xeolio.PolicyTypeEol, + PolicyScope: PolicyScopeGlobal, + PolicyType: types.PolicyTypeEol, WarnDate: "2021-01-15", DenyDate: "2021-01-16", }, { - PolicyScope: xeolio.PolicyScopeProject, - PolicyType: xeolio.PolicyTypeEol, + PolicyScope: PolicyScopeProject, + PolicyType: types.PolicyTypeEol, WarnDate: "2021-01-29", DenyDate: "2021-02-29", ProjectName: "github//foo/bar", @@ -489,15 +499,17 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeWarn, + Action: types.PolicyActionWarn, + Type: types.PolicyTypeEol, ProductName: "bar", Cycle: "1.3", FailDate: "2021-02-29", }, { - Type: PolicyTypeWarn, + Action: types.PolicyActionWarn, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.3", FailDate: "2021-02-29", @@ -506,10 +518,10 @@ func TestEvaluate(t *testing.T) { }, { name: "test sliding global policy [deny]", - policy: []xeolio.Policy{ + policy: []Policy{ { - PolicyScope: xeolio.PolicyScopeGlobal, - PolicyType: xeolio.PolicyTypeEol, + PolicyScope: PolicyScopeGlobal, + PolicyType: types.PolicyTypeEol, WarnDays: Int(60), DenyDays: Int(30), }, @@ -529,9 +541,10 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeDeny, + Action: types.PolicyActionDeny, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.3", }, @@ -539,10 +552,10 @@ func TestEvaluate(t *testing.T) { }, { name: "test sliding global policy [warn]", - policy: []xeolio.Policy{ + policy: []Policy{ { - PolicyScope: xeolio.PolicyScopeGlobal, - PolicyType: xeolio.PolicyTypeEol, + PolicyScope: PolicyScopeGlobal, + PolicyType: types.PolicyTypeEol, WarnDays: Int(60), DenyDays: Int(30), }, @@ -562,9 +575,10 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeWarn, + Action: types.PolicyActionWarn, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.3", FailDate: "2021-02-26", @@ -573,10 +587,10 @@ func TestEvaluate(t *testing.T) { }, { name: "test sliding global policy [deny, no warn]", - policy: []xeolio.Policy{ + policy: []Policy{ { - PolicyScope: xeolio.PolicyScopeGlobal, - PolicyType: xeolio.PolicyTypeEol, + PolicyScope: PolicyScopeGlobal, + PolicyType: types.PolicyTypeEol, DenyDays: Int(30), }, }, @@ -595,9 +609,10 @@ func TestEvaluate(t *testing.T) { }, }, }, - want: []EvaluationResult{ + want: []types.EolEvaluationResult{ { - Type: PolicyTypeDeny, + Action: types.PolicyActionDeny, + Type: types.PolicyTypeEol, ProductName: "foo", Cycle: "1.3", }, @@ -624,24 +639,24 @@ func TestEvaluate(t *testing.T) { } func TestByPolicyScope(t *testing.T) { - policies := []xeolio.Policy{ - {ID: "1", PolicyScope: xeolio.PolicyScopeGlobal}, - {ID: "2", PolicyScope: xeolio.PolicyScopeSoftware}, - {ID: "3", PolicyScope: xeolio.PolicyScopeProject}, - {ID: "4", PolicyScope: xeolio.PolicyScopeSoftware}, - {ID: "5", PolicyScope: xeolio.PolicyScopeGlobal}, - {ID: "6", PolicyScope: xeolio.PolicyScopeProject}, + policies := []Policy{ + {ID: "1", PolicyScope: PolicyScopeGlobal}, + {ID: "2", PolicyScope: PolicyScopeSoftware}, + {ID: "3", PolicyScope: PolicyScopeProject}, + {ID: "4", PolicyScope: PolicyScopeSoftware}, + {ID: "5", PolicyScope: PolicyScopeGlobal}, + {ID: "6", PolicyScope: PolicyScopeProject}, } sort.Stable(ByPolicyScope(policies)) - expected := []xeolio.Policy{ - {ID: "4", PolicyScope: xeolio.PolicyScopeSoftware}, - {ID: "2", PolicyScope: xeolio.PolicyScopeSoftware}, - {ID: "6", PolicyScope: xeolio.PolicyScopeProject}, - {ID: "3", PolicyScope: xeolio.PolicyScopeProject}, - {ID: "5", PolicyScope: xeolio.PolicyScopeGlobal}, - {ID: "1", PolicyScope: xeolio.PolicyScopeGlobal}, + expected := []Policy{ + {ID: "4", PolicyScope: PolicyScopeSoftware}, + {ID: "2", PolicyScope: PolicyScopeSoftware}, + {ID: "6", PolicyScope: PolicyScopeProject}, + {ID: "3", PolicyScope: PolicyScopeProject}, + {ID: "5", PolicyScope: PolicyScopeGlobal}, + {ID: "1", PolicyScope: PolicyScopeGlobal}, } if !reflect.DeepEqual(policies, expected) { diff --git a/xeol/policy/notary/notary.go b/xeol/policy/notary/notary.go new file mode 100644 index 00000000..57ccd51a --- /dev/null +++ b/xeol/policy/notary/notary.go @@ -0,0 +1,142 @@ +package notary + +import ( + "context" + "time" + + "github.com/docker/distribution/reference" + "github.com/wagoodman/go-partybus" + + "github.com/xeol-io/xeol/internal/bus" + "github.com/xeol-io/xeol/internal/log" + sigverifier "github.com/xeol-io/xeol/internal/sigverifier/notary" + "github.com/xeol-io/xeol/xeol/event" + "github.com/xeol-io/xeol/xeol/match" + "github.com/xeol-io/xeol/xeol/policy/types" +) + +var timeNow = time.Now + +const ( + DateLayout = "2006-01-02" +) + +type PolicyWrapper struct { + PolicyType types.PolicyType `json:"PolicyType"` + Policies []Policy `json:"Policies"` +} + +type Policy struct { + WarnDate string `json:"WarnDate"` + DenyDate string `json:"DenyDate"` + Policy string `json:"Policy"` +} + +func (n PolicyWrapper) GetPolicyType() types.PolicyType { + return n.PolicyType +} + +func (n Policy) warnMatch() bool { + if n.WarnDate != "" { + warnDate, err := time.Parse(DateLayout, n.WarnDate) + if err != nil { + log.Debugf("failed to parse warn date: %v", err) + return false + } + + if warnDate.IsZero() { + return false + } + + if timeNow().After(warnDate) { + return true + } + } + return false +} + +func (n Policy) denyMatch() bool { + if n.DenyDate != "" { + denyDate, err := time.Parse(DateLayout, n.DenyDate) + if err != nil { + log.Debugf("failed to parse warn date: %v", err) + return false + } + + if denyDate.IsZero() { + return false + } + + if timeNow().After(denyDate) { + return true + } + } + return false +} + +func (n PolicyWrapper) Evaluate(_ match.Matches, _ string, imageReference string) (bool, types.PolicyEvaluationResult) { + if len(n.Policies) == 0 { + log.Errorf("no notary policies provided") + return false, types.NotaryEvaluationResult{} + } + + if len(n.Policies) > 1 { + log.Errorf("invalid number of notary policies, there should only be one: %d", len(n.Policies)) + return false, types.NotaryEvaluationResult{} + } + + // validate this is a docker image reference + isValid := reference.ReferenceRegexp.MatchString(imageReference) + if !isValid { + log.Errorf("invalid Docker image reference: %s", imageReference) + return false, types.NotaryEvaluationResult{} + } + + policy := n.Policies[0] + + failBuild := false + ctx := context.Background() + err := sigverifier.Verify(ctx, imageReference, policy.Policy) + // if err is nil, then the image is verified + if err == nil { + return failBuild, types.NotaryEvaluationResult{ + Action: types.PolicyActionAllow, + Type: types.PolicyTypeNotary, + ImageReference: imageReference, + Verified: true, + } + } + log.Debugf("signature verification failed: %v", err) + + if policy.denyMatch() { + failBuild = true + result := types.NotaryEvaluationResult{ + Action: types.PolicyActionDeny, + Type: types.PolicyTypeNotary, + ImageReference: imageReference, + Verified: false, + } + bus.Publish(partybus.Event{ + Type: event.NotaryPolicyEvaluationMessage, + Value: result, + }) + return failBuild, result + } + + if policy.warnMatch() { + result := types.NotaryEvaluationResult{ + Action: types.PolicyActionWarn, + Type: types.PolicyTypeNotary, + ImageReference: imageReference, + Verified: false, + FailDate: policy.DenyDate, + } + bus.Publish(partybus.Event{ + Type: event.NotaryPolicyEvaluationMessage, + Value: result, + }) + return failBuild, result + } + + return failBuild, types.NotaryEvaluationResult{} +} diff --git a/xeol/policy/notary/notary_test.go b/xeol/policy/notary/notary_test.go new file mode 100644 index 00000000..1261b7ee --- /dev/null +++ b/xeol/policy/notary/notary_test.go @@ -0,0 +1,66 @@ +package notary + +// func TestEvaluate(t *testing.T) { +// tests := []struct { +// name string +// policyWrapper PolicyWrapper +// imageReference string +// want types.PolicyEvaluationResult +// wantFailBuild bool +// }{ +// // { +// // name: "unsigned image, deny", +// // policyWrapper: PolicyWrapper{ +// // PolicyType: types.PolicyTypeNotary, +// // Policies: []Policy{ +// // { +// // WarnDate: "", +// // DenyDate: "2023-01-01", +// // Policy: "eyJ2ZXJzaW9uIjogIjEuMCIsICJ0cnVzdFBvbGljaWVzIjogW3sibmFtZSI6ICJ3YWJiaXQtbmV0\nd29ya3MtaW1hZ2VzIiwgInRydXN0U3RvcmVzIjogWyJjYTp3YWJidC1uZXR3b3Jrcy5pbyJdLCAi\ncmVnaXN0cnlTY29wZXMiOiBbIioiXSwgInRydXN0ZWRJZGVudGl0aWVzIjogWyIqIl0sICJzaWdu\nYXR1cmVWZXJpZmljYXRpb24iOiB7ImxldmVsIjogInN0cmljdCJ9fV19", +// // }, +// // }, +// // }, +// // imageReference: "ubuntu:16.04", +// // want: types.NotaryEvaluationResult{ +// // Action: types.PolicyActionDeny, +// // Type: types.PolicyTypeNotary, +// // ImageReference: "ubuntu:16.04", +// // Verified: false, +// // }, +// // wantFailBuild: true, +// // }, +// { +// name: "signed image, allow", +// policyWrapper: PolicyWrapper{ +// PolicyType: types.PolicyTypeNotary, +// Policies: []Policy{ +// { +// WarnDate: "", +// DenyDate: "2023-01-01", +// Policy: "eyJ2ZXJzaW9uIjogIjEuMCIsICJ0cnVzdFBvbGljaWVzIjogW3sibmFtZSI6ICJ4ZW9sLXRlc3Qt\naW1hZ2VzIiwgInRydXN0U3RvcmVzIjogWyJjYTp4ZW9sLXRlc3QuaW8iXSwgInJlZ2lzdHJ5U2Nv\ncGVzIjogWyIqIl0sICJ0cnVzdGVkSWRlbnRpdGllcyI6IFsiKiJdLCAic2lnbmF0dXJlVmVyaWZp\nY2F0aW9uIjogeyJsZXZlbCI6ICJzdHJpY3QifX1dfQ==", +// }, +// }, +// }, +// imageReference: "xeolio.azurecr.io/signed:v1", +// want: types.NotaryEvaluationResult{ +// Action: types.PolicyActionAllow, +// Type: types.PolicyTypeNotary, +// ImageReference: "xeolio.azurecr.io/signed:v1", +// Verified: false, +// }, +// wantFailBuild: true, +// }, +// } + +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// got, result := tt.policyWrapper.Evaluate(match.Matches{}, "", tt.imageReference) +// if got != tt.wantFailBuild { +// t.Errorf("Evaluate() got = %v, wantFailBuild %v", got, tt.wantFailBuild) +// } +// if !reflect.DeepEqual(result, tt.want) { +// t.Errorf("Evaluate() got1 = %v, want %v", result, tt.want) +// } +// }) +// } +// } diff --git a/xeol/policy/policy.go b/xeol/policy/policy.go index d2429635..8dc2e9be 100644 --- a/xeol/policy/policy.go +++ b/xeol/policy/policy.go @@ -1,240 +1,59 @@ package policy import ( - "math" - "sort" - "time" + "encoding/json" + "fmt" - "github.com/Masterminds/semver" - "github.com/wagoodman/go-partybus" - - "github.com/xeol-io/xeol/internal/bus" - "github.com/xeol-io/xeol/internal/log" - "github.com/xeol-io/xeol/internal/xeolio" - "github.com/xeol-io/xeol/xeol/event" "github.com/xeol-io/xeol/xeol/match" + "github.com/xeol-io/xeol/xeol/policy/eol" + "github.com/xeol-io/xeol/xeol/policy/notary" + "github.com/xeol-io/xeol/xeol/policy/types" ) -const ( - DateLayout = "2006-01-02" - PolicyTypeWarn EvaluationType = "WARN" - PolicyTypeDeny EvaluationType = "DENY" - - // set a max days for deny/warn policies - // to avoid overflow/underflow errors when - // calculating dates. 10 years should be - // more than enough for most use cases - MaxNumDays = 10 * 365 -) - -var timeNow = time.Now - -type EvaluationType string - -type EvaluationResult struct { - Type EvaluationType - ProductName string - Cycle string - FailDate string -} - -type ByPolicyScope []xeolio.Policy - -func (a ByPolicyScope) Len() int { return len(a) } -func (a ByPolicyScope) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByPolicyScope) Less(i, j int) bool { - // The priority will be: software > project > global - switch a[i].PolicyScope { - case xeolio.PolicyScopeSoftware: - return true - case xeolio.PolicyScopeProject: - return a[j].PolicyScope != xeolio.PolicyScopeSoftware - case xeolio.PolicyScopeGlobal: - return a[j].PolicyScope == xeolio.PolicyScopeGlobal - default: - // Handle unknown cases - return false - } -} - -func cycleOperatorMatch(m match.Match, policy xeolio.Policy) bool { - if m.Cycle.ProductName != policy.ProductName { - return false - } - - pv, err := semver.NewVersion(policy.Cycle) - if err != nil { - log.Debugf("Invalid policy cycle version: %s", policy.Cycle) - return false - } - - mv, err := semver.NewVersion(m.Cycle.ReleaseCycle) - if err != nil { - log.Debugf("Invalid match cycle version: %s", m.Cycle.ReleaseCycle) - return false - } - - switch policy.CycleOperator { - case xeolio.CycleOperatorLessThan: - return mv.LessThan(pv) - case xeolio.CycleOperatorLessThanOrEqual: - return !mv.GreaterThan(pv) // equivalent to mv <= pv - case xeolio.CycleOperatorEqual: - return mv.Equal(pv) - default: - log.Debugf("Invalid policy cycle operator: %s", policy.CycleOperator) - return false - } -} - -func warnMatch(policy *xeolio.Policy, match match.Match) bool { - var warnDate time.Time - - if policy.WarnDate != "" { - var err error - warnDate, err = time.Parse(DateLayout, policy.WarnDate) - if err != nil { - log.Errorf("invalid policy warn date: %s", policy.WarnDate) - return false - } - } - - if policy.WarnDays != nil { - warnDays := *policy.WarnDays * -1 - if math.Abs(float64(warnDays)) > MaxNumDays { - log.Debugf("warn days (%d) is greater than max days, setting to max days (%d)", warnDays, MaxNumDays) - warnDays = MaxNumDays - } - eolDate, err := time.Parse(DateLayout, match.Cycle.Eol) - if err != nil { - log.Errorf("invalid eol date: %s, %s", match.Cycle.Eol, err) - return false - } - warnDate = eolDate.Add(time.Duration(warnDays) * time.Hour * 24) - } - - if warnDate.IsZero() { - return false - } - - if timeNow().After(warnDate) { - return true - } - - return false +type Policy interface { + Evaluate(match.Matches, string, string) (bool, types.PolicyEvaluationResult) + GetPolicyType() types.PolicyType } -func denyMatch(policy *xeolio.Policy, match match.Match) bool { - var denyDate time.Time +func UnmarshalPolicies(data []byte) ([]Policy, error) { + var rawPolicies []map[string]json.RawMessage - if policy.DenyDate != "" { - var err error - denyDate, err = time.Parse(DateLayout, policy.DenyDate) - if err != nil { - log.Errorf("invalid policy deny date: %s", policy.DenyDate) - return false - } + if err := json.Unmarshal(data, &rawPolicies); err != nil { + return nil, err } - if policy.DenyDays != nil { - denyDays := *policy.DenyDays * -1 - if math.Abs(float64(denyDays)) > MaxNumDays { - log.Debugf("deny days (%d) is greater than max days, setting to max days (%d)", denyDays, MaxNumDays) - denyDays = MaxNumDays - } - - eolDate, err := time.Parse(DateLayout, match.Cycle.Eol) - if err != nil { - log.Errorf("invalid eol date: %s, %s", match.Cycle.Eol, err) - return false + var policies []Policy + for _, rawPolicy := range rawPolicies { + var policyType string + if err := json.Unmarshal(rawPolicy["PolicyType"], &policyType); err != nil { + return nil, err } - denyDate = eolDate.Add(time.Duration(denyDays) * time.Hour * 24) - policy.DenyDate = denyDate.Format(DateLayout) - } - - if denyDate.IsZero() { - return false - } - - if timeNow().After(denyDate) { - return true - } - - return false -} - -func createEvaluationResult(policy xeolio.Policy, match match.Match, policyType EvaluationType) EvaluationResult { - result := EvaluationResult{ - Type: policyType, - ProductName: match.Cycle.ProductName, - Cycle: match.Cycle.ReleaseCycle, - } - if policyType == PolicyTypeWarn { - result.FailDate = policy.DenyDate - } - return result -} - -func evaluateMatches(policies []xeolio.Policy, matches match.Matches, projectName string) []EvaluationResult { - var results []EvaluationResult - - // keep track of which matches have been evaluated - // so we don't evaluate the same match twice - evaluatedMatches := make(map[string]bool) - // policies are first sorted by scope according to - // this order: software > project > global - // software policies are evaluated first, then project - // policies, then global policies - sort.Stable(ByPolicyScope(policies)) - - for _, policy := range policies { - policyCopy := policy - for _, match := range matches.Sorted() { - if evaluatedMatches[match.Cycle.ProductName] { - continue + switch policyType { + case "EOL": + var container eol.PolicyWrapper + rawJSON, err := json.Marshal(rawPolicy) + if err != nil { + return nil, err } - - switch policy.PolicyScope { - case xeolio.PolicyScopeSoftware: - if !cycleOperatorMatch(match, policy) { - continue - } - case xeolio.PolicyScopeProject: - if policy.ProjectName != projectName { - continue - } + if err := json.Unmarshal(rawJSON, &container); err != nil { + return nil, err } - - // deny policy takes precedence over warn policy, so order is important here - if denyMatch(&policyCopy, match) { - results = append(results, createEvaluationResult(policyCopy, match, PolicyTypeDeny)) - evaluatedMatches[match.Cycle.ProductName] = true - continue + policies = append(policies, container) + case "NOTARY": + var container notary.PolicyWrapper + rawJSON, err := json.Marshal(rawPolicy) + if err != nil { + return nil, err } - if warnMatch(&policyCopy, match) { - results = append(results, createEvaluationResult(policyCopy, match, PolicyTypeWarn)) - evaluatedMatches[match.Cycle.ProductName] = true + if err := json.Unmarshal(rawJSON, &container); err != nil { + return nil, err } + policies = append(policies, container) + default: + return nil, fmt.Errorf("unknown policy type: %s", policyType) } } - return results -} -// Evaluate evaluates a set of policies against a set of matches. -func Evaluate(policies []xeolio.Policy, matches match.Matches, projectName string) bool { - policyMatches := evaluateMatches(policies, matches, projectName) - // whether we should fail the scan or not - failScan := false - - for _, policyMatch := range policyMatches { - if policyMatch.Type == PolicyTypeDeny { - failScan = true - } - bus.Publish(partybus.Event{ - Type: event.PolicyEvaluationMessage, - Value: policyMatch, - }) - } - return failScan + return policies, nil } diff --git a/xeol/policy/types/types.go b/xeol/policy/types/types.go new file mode 100644 index 00000000..2c33c70e --- /dev/null +++ b/xeol/policy/types/types.go @@ -0,0 +1,69 @@ +package types + +type PolicyType string + +const ( + PolicyActionWarn PolicyAction = "WARN" + PolicyActionDeny PolicyAction = "DENY" + PolicyActionAllow PolicyAction = "ALLOW" + + PolicyTypeEol PolicyType = "EOL" + PolicyTypeNotary PolicyType = "NOTARY" +) + +type PolicyAction string + +type EolEvaluationResult struct { + Type PolicyType + Action PolicyAction + ProductName string + Cycle string + FailDate string +} + +type NotaryEvaluationResult struct { + Type PolicyType + Action PolicyAction + ImageReference string + Verified bool + FailDate string +} + +type PolicyEvaluationResult interface { + GetPolicyAction() PolicyAction + GetPolicyType() PolicyType + GetFailDate() string + GetVerified() bool +} + +func (n NotaryEvaluationResult) GetVerified() bool { + return n.Verified +} + +func (n NotaryEvaluationResult) GetPolicyAction() PolicyAction { + return n.Action +} + +func (n NotaryEvaluationResult) GetPolicyType() PolicyType { + return n.Type +} + +func (n NotaryEvaluationResult) GetFailDate() string { + return n.FailDate +} + +func (e EolEvaluationResult) GetVerified() bool { + return false +} + +func (e EolEvaluationResult) GetPolicyAction() PolicyAction { + return e.Action +} + +func (e EolEvaluationResult) GetPolicyType() PolicyType { + return e.Type +} + +func (e EolEvaluationResult) GetFailDate() string { + return e.FailDate +} diff --git a/xeol/report/model.go b/xeol/report/model.go index b6172804..932f301d 100644 --- a/xeol/report/model.go +++ b/xeol/report/model.go @@ -6,10 +6,12 @@ import ( ) type XeolEventPayload struct { - Matches []match.Match - Packages []pkg.Package - Context pkg.Context - AppConfig interface{} - ImageName string - Sbom string + Matches []match.Match + Packages []pkg.Package + Context pkg.Context + AppConfig interface{} + ImageVerified bool + ImageName string + ImageDigest string + Sbom string }