diff --git a/README.md b/README.md index 323b9e9b..55392e88 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ OSS components through automated vetting in CI/CD. * [CI/CD Integration](#ci/cd-integration) * [📦 GitHub Action](#-github-action) * [🚀 GitLab CI](#-gitlab-ci) +* [🐙 Malicious Package Analysis](#-malicious-package-analysis) * [🛠️ Advanced Usage](#-advanced-usage) * [📖 Documentation](#-documentation) * [🎊 Community](#-community) @@ -276,6 +277,22 @@ execution. - `vet` can be integrated with GitLab CI, refer to [vet-gitlab-ci](https://docs.safedep.io/integrations/gitlab-ci) +## 🐙 Malicious Package Analysis + +`vet` supports scanning for malicious packages using [SafeDep Cloud API](https://docs.safedep.io/cloud/malware-analysis) + +- Run a scan and check for malicious packages + +```bash +vet scan -D /path/to/code --malware +``` + +**Note**: `vet` will submit identified packages to SafeDep Cloud for analysis and wait +for a `timeout` period for response. Not all package analysis may be completed +within the timeout period. However, subsequent scans will fetch the results if +available and lead to increased coverage over time. Adjust the timeout using +`--malware-analysis-timeout` flag. + ## 🛠️ Advanced Usage - [Threat Hunting with vet](https://docs.safedep.io/advanced/filtering) diff --git a/go.mod b/go.mod index 886dbed2..f004e8ac 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/safedep/vet go 1.23.2 require ( - buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241127172711-d314452ec756.1 - buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.2-20241127172711-d314452ec756.1 + buf.build/gen/go/safedep/api/grpc/go v1.5.1-20250120081932-370c5c54f7c9.2 + buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.3-20250120081932-370c5c54f7c9.1 github.com/AlecAivazis/survey/v2 v2.3.7 - github.com/CycloneDX/cyclonedx-go v0.9.1 - github.com/anchore/syft v1.16.0 + github.com/CycloneDX/cyclonedx-go v0.9.2 + github.com/anchore/syft v1.18.1 github.com/cayleygraph/cayley v0.7.7-0.20240706181042-81dcd7d73e45 github.com/cayleygraph/quad v1.3.0 github.com/cli/oauth v1.2.0 @@ -17,24 +17,24 @@ require ( github.com/gojek/heimdall v5.0.2+incompatible github.com/gojek/heimdall/v7 v7.0.3 github.com/golang/protobuf v1.5.4 - github.com/google/cel-go v0.22.0 + github.com/google/cel-go v0.22.1 github.com/google/go-github/v54 v54.0.0 - github.com/google/osv-scanner v1.9.1 + github.com/google/osv-scanner v1.9.2 github.com/hashicorp/hcl/v2 v2.23.0 - github.com/jedib0t/go-pretty/v6 v6.6.1 + github.com/jedib0t/go-pretty/v6 v6.6.5 github.com/kubescape/go-git-url v0.0.30 github.com/oklog/ulid/v2 v2.1.0 github.com/owenrumney/go-sarif/v2 v2.3.3 github.com/package-url/packageurl-go v0.1.3 - github.com/safedep/dry v0.0.0-20241128083908-2f8ecd48dc2c + github.com/safedep/dry v0.0.0-20250106055453-e0772cda4a25 github.com/sirupsen/logrus v1.9.3 github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82 github.com/spdx/tools-golang v0.5.5 github.com/spf13/cobra v1.8.1 - github.com/stretchr/testify v1.9.0 - golang.org/x/oauth2 v0.24.0 - google.golang.org/grpc v1.68.0 - google.golang.org/protobuf v1.35.2 + github.com/stretchr/testify v1.10.0 + golang.org/x/oauth2 v0.25.0 + google.golang.org/grpc v1.69.4 + google.golang.org/protobuf v1.36.3 gopkg.in/yaml.v2 v2.4.0 ) @@ -44,29 +44,30 @@ replace github.com/owenrumney/go-sarif/v2 v2.3.1 => github.com/safedep/go-sarif/ replace github.com/cli/oauth v1.0.1 => github.com/abhisek/oauth v1.0.1-audience require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20240920164238-5a7b106cbb87.1 // indirect - cel.dev/expr v0.18.0 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.3-20241127180247-a33202765966.1 // indirect + cel.dev/expr v0.19.1 // indirect dario.cat/mergo v1.0.1 // indirect github.com/BurntSushi/toml v1.4.0 // indirect github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect github.com/CloudyKit/jet/v6 v6.2.0 // indirect github.com/DataDog/datadog-go v4.8.3+incompatible // indirect github.com/Joker/jade v1.1.3 // indirect - github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.1.2 // indirect + github.com/ProtonMail/go-crypto v1.1.5 // indirect github.com/Shopify/goreferrer v0.0.0-20240724165105-aceaa0259138 // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/adrg/xdg v0.5.3 // indirect github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect github.com/agext/levenshtein v1.2.3 // indirect - github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837 // indirect - github.com/anchore/fangs v0.0.0-20241031222233-81506aed5251 // indirect - github.com/anchore/go-logger v0.0.0-20241005132348-65b4486fbb28 // indirect - github.com/anchore/go-macholibre v0.0.0-20241029130037-f2cd2ff1a192 // indirect + github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 // indirect + github.com/anchore/clio v0.0.0-20250115173119-036c31e4dfd7 // indirect + github.com/anchore/fangs v0.0.0-20241125225345-c73f048692d3 // indirect + github.com/anchore/go-logger v0.0.0-20241205183533-4fc29b5832e7 // indirect + github.com/anchore/go-macholibre v0.0.0-20241219195549-70b0e607b241 // indirect github.com/anchore/go-struct-converter v0.0.0-20240925125616-a0883641c664 // indirect github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f // indirect - github.com/anchore/stereoscope v0.0.8 // indirect + github.com/anchore/stereoscope v0.0.12 // indirect github.com/andybalholm/brotli v1.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect @@ -74,48 +75,48 @@ require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/becheran/wildmatch-go v1.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect + github.com/bmatcuk/doublestar/v4 v4.8.0 // indirect github.com/boltdb/bolt v1.3.1 // indirect - github.com/bytedance/sonic v1.12.4 // indirect - github.com/bytedance/sonic/loader v0.2.1 // indirect + github.com/bytedance/sonic v1.12.7 // indirect + github.com/bytedance/sonic/loader v0.2.3 // indirect github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chainguard-dev/git-urls v1.0.2 // indirect github.com/cloudflare/circl v1.5.0 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dlclark/regexp2 v1.11.4 // indirect - github.com/docker/cli v27.3.1+incompatible // indirect + github.com/docker/cli v27.5.0+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-connections v0.5.0 // indirect - github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd // indirect + github.com/dop251/goja v0.0.0-20250114131315-46d383d606d3 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/facebookincubator/nvdtools v0.1.5 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/fgprof v0.9.5 // indirect github.com/flosch/pongo2/v4 v4.0.2 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.6 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.0.0 // indirect github.com/gin-gonic/gin v1.10.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.23.0 // indirect + github.com/go-playground/validator/v10 v10.24.0 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect - github.com/goccy/go-json v0.10.3 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/gojek/valkyrie v0.0.0-20190210220504-8f62c1e7ba45 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81 // indirect + github.com/gomarkdown/markdown v0.0.0-20241205020045-f7e15b2f3e62 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/go-containerregistry v0.20.2 // indirect + github.com/google/go-containerregistry v0.20.3 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/licensecheck v0.3.1 // indirect - github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect + github.com/google/pprof v0.0.0-20250121033306-997b0b79cac0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gookit/color v1.5.4 // indirect github.com/gorilla/css v1.0.1 // indirect @@ -130,7 +131,7 @@ require ( github.com/jinzhu/copier v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kataras/blocks v0.0.8 // indirect + github.com/kataras/blocks v0.0.11 // indirect github.com/kataras/golog v0.1.12 // indirect github.com/kataras/iris/v12 v12.2.11 // indirect github.com/kataras/pio v0.0.13 // indirect @@ -140,13 +141,13 @@ require ( github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/labstack/echo/v4 v4.12.0 // indirect + github.com/labstack/echo/v4 v4.13.3 // indirect github.com/labstack/gommon v0.4.2 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect + github.com/magiconair/properties v1.8.9 // indirect github.com/mailgun/raymond/v2 v2.0.48 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect @@ -164,7 +165,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pborman/indent v1.2.1 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/piprate/json-gold v0.5.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pkg/profile v1.7.0 // indirect @@ -172,26 +173,26 @@ require ( github.com/pquerna/cachecontrol v0.2.0 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.1 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/schollz/closestmatch v2.1.0+incompatible // indirect github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/sylabs/squashfs v1.0.0 // indirect - github.com/tdewolff/minify/v2 v2.21.1 // indirect + github.com/sylabs/squashfs v1.0.4 // indirect + github.com/tdewolff/minify/v2 v2.21.3 // indirect github.com/tdewolff/parse/v2 v2.7.19 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect @@ -208,29 +209,30 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yosssi/ace v0.0.5 // indirect - github.com/zclconf/go-cty v1.15.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 // indirect - go.opentelemetry.io/otel v1.32.0 // indirect - go.opentelemetry.io/otel/metric v1.32.0 // indirect - go.opentelemetry.io/otel/trace v1.32.0 // indirect + github.com/zclconf/go-cty v1.16.1 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/arch v0.12.0 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect + golang.org/x/arch v0.13.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.33.0 // indirect + golang.org/x/net v0.34.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.8.0 // indirect - golang.org/x/tools v0.27.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.29.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 // indirect + k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index e171a592..c92973da 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,19 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20240920164238-5a7b106cbb87.1 h1:7QIeAuTdLp173vC/9JojRMDFcpmqtoYrxPmvdHAOynw= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20240920164238-5a7b106cbb87.1/go.mod h1:mnHCFccv4HwuIAOHNGdiIc5ZYbBCvbTWZcodLN5wITI= -buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241127172711-d314452ec756.1 h1:ecda01eNlmjlQKIaiFIpV/VLbG3xeCh94TiEazvaGwc= -buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241127172711-d314452ec756.1/go.mod h1:M1mSl+19TDBAGjd/L4t7UoCVQ0GlMYDwC2B3iqkpaDI= -buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.2-20241127172711-d314452ec756.1 h1:J4oZ6bbe+gZzFXnJMUcM8BrSKh0FMLdPNpDwWizLeXg= -buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.2-20241127172711-d314452ec756.1/go.mod h1:471Oa35fKGZy8W6WOLV4R80UcWKMYaXNmIfApMvimf4= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.2-20240508200655-46a4cf4ba109.1 h1:IQ7h10cY5brtPWVllkhiEd2wa6S6vmbQMbmY717Ptv0= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.2-20240508200655-46a4cf4ba109.1/go.mod h1:JnMVLi3qrNYPODVpEKG7UjHLl/d2zR221e66YCSmP2Q= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.3-20241127180247-a33202765966.1 h1:cQZXKoQ+eB0kykzfJe80RP3nc+3PWbbBrUBm8XNYAQY= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.3-20241127180247-a33202765966.1/go.mod h1:6VPKM8zbmgf9qsmkmKeH49a36Vtmidw3rG53B5mTenc= +buf.build/gen/go/safedep/api/grpc/go v1.5.1-20250113125913-f90710bfd672.2 h1:pGKPh/jHOBc2kDbYDitNoY2mr4cI1LgrIy0pNKtWNbU= +buf.build/gen/go/safedep/api/grpc/go v1.5.1-20250113125913-f90710bfd672.2/go.mod h1:7DC6BumVy4Tsw0rnJrOBL1JcpQr0/6Y0pL9KsEb3Xmg= +buf.build/gen/go/safedep/api/grpc/go v1.5.1-20250120081932-370c5c54f7c9.2 h1:8GnhglDlnetv0W40JaRxUloAALoDUNYjgUy9ZsKFGrQ= +buf.build/gen/go/safedep/api/grpc/go v1.5.1-20250120081932-370c5c54f7c9.2/go.mod h1:tJ3olHksW0Top5Uq4gagVEoEwzA3KQrBeSbKdmK/iOo= +buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.2-20250113125913-f90710bfd672.1 h1:0LzooeICg9EC7ycCJGmltiWBu/lor1RsH3i8pZWO4s8= +buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.2-20250113125913-f90710bfd672.1/go.mod h1:0imTeAc72NLqWJl8by9QHVuEEz+6jt18Nr0iINEB5Bc= +buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.3-20250120081932-370c5c54f7c9.1 h1:oxEK5wTESsdCmtdcVCI0aoXf5Hflvk7pYlKlTVPXMhM= +buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.3-20250120081932-370c5c54f7c9.1/go.mod h1:buFDzX2R5tIKP7wAw/B+iJR0WykeqRTLuNsbJA27hYQ= cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -73,6 +81,8 @@ github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oM github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= github.com/CycloneDX/cyclonedx-go v0.9.1 h1:yffaWOZsv77oTJa/SdVZYdgAgFioCeycBUKkqS2qzQM= github.com/CycloneDX/cyclonedx-go v0.9.1/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKcTwu8u0ccsACEsw= +github.com/CycloneDX/cyclonedx-go v0.9.2 h1:688QHn2X/5nRezKe2ueIVCt+NRqf7fl3AVQk+vaFcIo= +github.com/CycloneDX/cyclonedx-go v0.9.2/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.7.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= @@ -81,8 +91,11 @@ github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= @@ -92,6 +105,8 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0= github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= +github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/Shopify/goreferrer v0.0.0-20240724165105-aceaa0259138 h1:gjbp60h8IZQbN/TpDaYJedWbbD1h1aDPEwWnYWaDaUY= github.com/Shopify/goreferrer v0.0.0-20240724165105-aceaa0259138/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI= @@ -109,16 +124,26 @@ 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/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 h1:yhk+P8lF3ZiROjmaVRao9WGTRo4b/wYjoKEiAHWrKwc= +github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51/go.mod h1:nwuGSd7aZp0rtYt79YggCGafz1RYsclE7pi3fhLwvuw= github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837 h1:bIG3WsfosZsJ5LMC7PB9J/ekFM3a0j0ZEDvN3ID6GTI= github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837/go.mod h1:tRQVKkjYeejrh9AdM0s1esbwtMU7rdHAHSQWkv4qskE= +github.com/anchore/clio v0.0.0-20250115173119-036c31e4dfd7 h1:58Jkuok064peUdoOef3wHp0zc9XLcW6kyWjtKqc9K9A= +github.com/anchore/clio v0.0.0-20250115173119-036c31e4dfd7/go.mod h1:iHfut2N3hTPFR19HV7PLCkC8y0It++JRZAL/ANtEX+s= github.com/anchore/fangs v0.0.0-20241031222233-81506aed5251 h1:pY93gR/my9xIxIkud/pSeAXGNz8dUp+OQDqECWwKfUs= github.com/anchore/fangs v0.0.0-20241031222233-81506aed5251/go.mod h1:7tbilRyb93SAKRYR4AnOGCCgIn1Fy2KQWQjI42FOwW4= +github.com/anchore/fangs v0.0.0-20241125225345-c73f048692d3 h1:GaErkA967XCOkqfOgZ+ijU0rswhIJ3py9kH1LfQKDMs= +github.com/anchore/fangs v0.0.0-20241125225345-c73f048692d3/go.mod h1:PWdaRnahkGJ+56c9Q5TMmMeEEKgVT7vZHmL/wg0E8aw= github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q= github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8= github.com/anchore/go-logger v0.0.0-20241005132348-65b4486fbb28 h1:TKlTOayTJKpoLPJbeMykEwxCn0enACf06u0RSIdFG5w= github.com/anchore/go-logger v0.0.0-20241005132348-65b4486fbb28/go.mod h1:5iJIa34inbIEFRwoWxNBTnjzIcl4G3le1LppPDmpg/4= +github.com/anchore/go-logger v0.0.0-20241205183533-4fc29b5832e7 h1:H6XDQQrAT6jMsr4k0uH+bD+zza7N0K21Mfywo1H0uWM= +github.com/anchore/go-logger v0.0.0-20241205183533-4fc29b5832e7/go.mod h1:L2TJz+/eN3eOWbHiMXGddj5eRNX48XruxWVgap0bqJ0= github.com/anchore/go-macholibre v0.0.0-20241029130037-f2cd2ff1a192 h1:BSmVIbiUSS9j7YyZKe7095krnhgIpIBcas50Rk1lf5E= github.com/anchore/go-macholibre v0.0.0-20241029130037-f2cd2ff1a192/go.mod h1:Eo8ljGv2RLMmToXXFhfPHX1h8La2hpzHA7fBXAZUfpg= +github.com/anchore/go-macholibre v0.0.0-20241219195549-70b0e607b241 h1:MyZM9WIeCynOs1W9DQ7J/KDMoxYlxNwvRVhirkJzULY= +github.com/anchore/go-macholibre v0.0.0-20241219195549-70b0e607b241/go.mod h1:Eo8ljGv2RLMmToXXFhfPHX1h8La2hpzHA7fBXAZUfpg= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/anchore/go-struct-converter v0.0.0-20240925125616-a0883641c664 h1:TcUCZsLGIXJbqo9z9VX436XdpIyZ5Cs3b8XTIp+jRxs= github.com/anchore/go-struct-converter v0.0.0-20240925125616-a0883641c664/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= @@ -128,8 +153,12 @@ github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f h1:dAQPIrQ github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.0.8 h1:ma8A7SnM5WWU0HJ2p6YBq7myN7zKa0JnUQOY/4enekk= github.com/anchore/stereoscope v0.0.8/go.mod h1:o1eDg6BZwORU6nh4vRe3C2ZmAmmH+MWRLNl55uAF/v8= +github.com/anchore/stereoscope v0.0.12 h1:ovUWeyeZGml6pTGiu/uha/rCbToANFPu+cnhLbeperY= +github.com/anchore/stereoscope v0.0.12/go.mod h1:cmb/MGya7ccOd6fZZEREuhdSH2kFALBMrkY/66Sfv1o= github.com/anchore/syft v1.16.0 h1:iHPqE2q7gmvRDdmh5/897ycRbetfmLwor17/YBNVQNw= github.com/anchore/syft v1.16.0/go.mod h1:x8JNItb+Dj3xwG1tRfyCbJj9Xl/vlcBfXz7q3M2GmjA= +github.com/anchore/syft v1.18.1 h1:JZ7CLbeWrWolCZa4f6SJBLJ9qGBLFCzHrFd8c4bsm94= +github.com/anchore/syft v1.18.1/go.mod h1:ufXPZcjmoTjERaC0HTEW2+chF+fQdryhaQ9arcUO2WQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -160,15 +189,21 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.8.0 h1:DSXtrypQddoug1459viM9X9D3dp1Z7993fw36I2kNcQ= +github.com/bmatcuk/doublestar/v4 v4.8.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k= github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q= +github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E= github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0= +github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c h1:HIGF0r/56+7fuIZw2V4isE22MK6xpxWx7BbV8dJ290w= github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= github.com/cayleygraph/cayley v0.7.7-0.20240706181042-81dcd7d73e45 h1:6uJw5TMEw2l5kgu60Iq+FzRg6suiuIwxqOIXUcGEQOs= @@ -177,6 +212,7 @@ github.com/cayleygraph/quad v1.3.0 h1:xg7HOLWWPgvZ4CcvzEpfCwq42L8mzYUR+8V0jtYoBz github.com/cayleygraph/quad v1.3.0/go.mod h1:NadtM7uMm78FskmX++XiOOrNvgkq0E1KvvhQdMseMz4= 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 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -204,6 +240,8 @@ github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+py github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -222,6 +260,7 @@ github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaD github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= +github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA= github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= @@ -236,6 +275,7 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= +github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU= github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= @@ -263,10 +303,13 @@ github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yA github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ= github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= +github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -277,6 +320,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd h1:QMSNEh9uQkDjyPwu/J541GgSH+4hw+0skJDIj9HJ3mE= github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= +github.com/dop251/goja v0.0.0-20250114131315-46d383d606d3 h1:xPoFl7UDOasFiReIaL75lqkKTgIRMKBvfOyiZfq9s3Q= +github.com/dop251/goja v0.0.0-20250114131315-46d383d606d3/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= @@ -321,9 +366,13 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= +github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/github/go-spdx/v2 v2.3.2 h1:IfdyNHTqzs4zAJjXdVQfRnxt1XMfycXoHBE2Vsm1bjs= @@ -356,6 +405,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= +github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc= github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk= github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= @@ -371,6 +422,8 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofri/go-github-ratelimit v1.1.0 h1:ijQ2bcv5pjZXNil5FiwglCg8wc9s8EgjTmNkqjw8nuk= github.com/gofri/go-github-ratelimit v1.1.0/go.mod h1:OnCi5gV+hAG/LMR7llGhU7yHt44se9sYgKPnafoL7RY= @@ -424,10 +477,14 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81 h1:5lyLWsV+qCkoYqsKUDuycESh9DEIPVKN6iCFeL7ag50= github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/gomarkdown/markdown v0.0.0-20241205020045-f7e15b2f3e62 h1:pbAFUZisjG4s6sxvRJvf2N7vhpCvx2Oxb3PmS6pDO1g= +github.com/gomarkdown/markdown v0.0.0-20241205020045-f7e15b2f3e62/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= +github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40= +github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -445,6 +502,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= +github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= +github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= github.com/google/go-github/v54 v54.0.0 h1:OZdXwow4EAD5jEo5qg+dGFH2DpkyZvVsAehjvJuUL/c= github.com/google/go-github/v54 v54.0.0/go.mod h1:Sw1LXWHhXRZtzJ9LI5fyJg9wbQzYvFhW8W5P2yaAQ7s= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -458,6 +517,8 @@ github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIG github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/osv-scanner v1.9.1 h1:L/j81YXO+DuhBd+v2eYwpDTfmUpMLryItSv7SXA1Db4= github.com/google/osv-scanner v1.9.1/go.mod h1:VNJG6+N9l6Y1dcaGgK/a7D4g9hnxakn0DBKGH0Aw+mQ= +github.com/google/osv-scanner v1.9.2 h1:N5Arl9SA75afbjmX8mKURgOIaKyuK3NUjCaxDlj1KHI= +github.com/google/osv-scanner v1.9.2/go.mod h1:ZTL8Dp9z/7Jr9kkQSOGqo8z6Csqt83qMIr58aZVx+pM= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -476,6 +537,8 @@ github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8I github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs= github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250121033306-997b0b79cac0 h1:EinjE47mmVVsxcjIwVKQWNY+3P+5R2BhkbULjhEDThc= +github.com/google/pprof v0.0.0-20250121033306-997b0b79cac0/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -556,6 +619,8 @@ github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvP github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/jedib0t/go-pretty/v6 v6.6.1 h1:iJ65Xjb680rHcikRj6DSIbzCex2huitmc7bDtxYVWyc= github.com/jedib0t/go-pretty/v6 v6.6.1/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= +github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3NgSqAo= +github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -573,6 +638,8 @@ github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPci github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM= github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg= +github.com/kataras/blocks v0.0.11 h1:JJdYW0AUaJKLx5kEWs/oRVCvKVXo+6CAAeaVAiJf7wE= +github.com/kataras/blocks v0.0.11/go.mod h1:b4UySrJySEOq6drKH9U3bOpMI+dRH148mayYfS3RFb8= github.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg= github.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4= github.com/kataras/iris/v12 v12.2.11 h1:sGgo43rMPfzDft8rjVhPs6L3qDJy3TbBrMD/zGL1pzk= @@ -614,6 +681,8 @@ github.com/kubescape/go-git-url v0.0.30 h1:PIbg86ae0ftee/p/Tu/6CA1ju6VoJ51G3sQWN github.com/kubescape/go-git-url v0.0.30/go.mod h1:3ddc1HEflms1vMhD9owt/3FBES070UaYTUarcjx8jDk= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= +github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= @@ -625,10 +694,14 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8 github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -639,6 +712,8 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= @@ -714,6 +789,7 @@ github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaL github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= +github.com/owenrumney/go-sarif v1.1.1 h1:QNObu6YX1igyFKhdzd7vgzmw7XsWN3/6NMGuDzBgXmE= github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= github.com/owenrumney/go-sarif/v2 v2.3.3 h1:ubWDJcF5i3L/EIOER+ZyQ03IfplbSU1BLOE26uKQIIU= github.com/owenrumney/go-sarif/v2 v2.3.3/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w= @@ -734,6 +810,8 @@ github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xl github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/piprate/json-gold v0.5.0 h1:RmGh1PYboCFcchVFuh2pbSWAZy4XJaqTMU4KQYsApbM= github.com/piprate/json-gold v0.5.0/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -767,6 +845,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -788,9 +868,13 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safedep/dry v0.0.0-20241128083908-2f8ecd48dc2c h1:qUSfzPPlEcLKF6cKvkkXU6ddu4NGSz0UdS7Xjcrenvw= github.com/safedep/dry v0.0.0-20241128083908-2f8ecd48dc2c/go.mod h1:dtGFDAnRo+WqwEyqPc2hTwuVGwWLq2jHnP4Q8BO1u7g= +github.com/safedep/dry v0.0.0-20250106055453-e0772cda4a25 h1:vkW9YyId5WHPnnGhnrmucKL53xTNUE8mBLBdmTBOGBc= +github.com/safedep/dry v0.0.0-20250106055453-e0772cda4a25/go.mod h1:VNiIEzsaDJUncMyS+Aly7Hojf3qYNAz+J6Kmi0DALFw= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= @@ -827,9 +911,13 @@ github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY52 github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -861,15 +949,22 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/sylabs/sif/v2 v2.19.2 h1:KKcUKnbnT69rN1WWHRYoAVKFpqnXpNJ36kmQLpp86Uc= github.com/sylabs/sif/v2 v2.19.2/go.mod h1:nhX6D/CJntHDWspNLXLe+yct0cd5lm8HJ7VIW6hgKrw= +github.com/sylabs/sif/v2 v2.20.2 h1:HGEPzauCHhIosw5o6xmT3jczuKEuaFzSfdjAsH33vYw= github.com/sylabs/squashfs v1.0.0 h1:xAyMS21ogglkuR5HaY55PCfqY3H32ma9GkasTYo28Zg= github.com/sylabs/squashfs v1.0.0/go.mod h1:rhWzvgefq1X+R+LZdts10hfMsTg3g74OfGunW8tvg/4= +github.com/sylabs/squashfs v1.0.4 h1:uFSw7WXv7zjutPvU+JzY0nY494Vw8s4FAf4+7DhoMdI= +github.com/sylabs/squashfs v1.0.4/go.mod h1:PDgf8YmCntvN4d9Y8hBUBDCZL6qZOzOQwRGxnIdbERk= github.com/tdewolff/minify/v2 v2.21.1 h1:AAf5iltw6+KlUvjRNPAPrANIXl3XEJNBBzuZom5iCAM= github.com/tdewolff/minify/v2 v2.21.1/go.mod h1:PoqFH8ugcuTUvKqVM9vOqXw4msxvuhL/DTmV5ZXhSCI= +github.com/tdewolff/minify/v2 v2.21.3 h1:KmhKNGrN/dGcvb2WDdB5yA49bo37s+hcD8RiF+lioV8= +github.com/tdewolff/minify/v2 v2.21.3/go.mod h1:iGxHaGiONAnsYuo8CRyf8iPUcqRJVB/RhtEcTpqS7xw= github.com/tdewolff/parse/v2 v2.7.19 h1:7Ljh26yj+gdLFEq/7q9LT4SYyKtwQX4ocNrj45UCePg= github.com/tdewolff/parse/v2 v2.7.19/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA= github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= @@ -877,6 +972,7 @@ github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03 github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= +github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -904,6 +1000,7 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= +github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0= github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= @@ -946,6 +1043,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ= github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.1 h1:a5TZEPzBFFR53udlIKApXzj8JIF4ZNQ6abH79z5R1S0= +github.com/zclconf/go-cty v1.16.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -960,16 +1059,26 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 h1:qtFISDHKolvIxzSs0gIaiPUPR0Cucb0F2coHC7ZLdps= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0/go.mod h1:Y+Pop1Q6hCOnETWTW4NROK/q1hv50hM7yDaUTjG8lp8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= 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= @@ -984,6 +1093,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg= golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA= +golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -997,6 +1108,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= 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= @@ -1009,6 +1122,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= 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= @@ -1085,6 +1200,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1104,6 +1221,8 @@ golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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= @@ -1193,10 +1312,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 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= @@ -1214,6 +1337,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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= @@ -1274,6 +1399,8 @@ golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1385,8 +1512,12 @@ google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJ google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f h1:M65LEviCfuZTfrfzwwEoxVtgvfkFkBUbFnRbxCXuXhU= google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f/go.mod h1:Yo94eF2nj7igQt+TiJ49KxjIH8ndLYPZMIRSiRcEbg0= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f h1:C1QccEa9kUwvMgEUORqQD9S17QesQijxjZ84sO82mfo= google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= 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= @@ -1416,6 +1547,8 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= +google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1430,8 +1563,10 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1469,6 +1604,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 h1:jGnCPejIetjiy2gqaJ5V0NLwTpF4wbQ6cZIItJCSHno= k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs= moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/pkg/analyzer/malware.go b/pkg/analyzer/malware.go new file mode 100644 index 00000000..441a94b9 --- /dev/null +++ b/pkg/analyzer/malware.go @@ -0,0 +1,153 @@ +package analyzer + +import ( + "fmt" + "sync" + + malysisv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/malysis/v1" + "github.com/safedep/vet/gen/checks" + "github.com/safedep/vet/gen/filtersuite" + "github.com/safedep/vet/pkg/common/logger" + "github.com/safedep/vet/pkg/models" + "github.com/safedep/vet/pkg/readers" +) + +type MalwareAnalyzerConfig struct { + // Flag to trust automated analysis results without needing + // a verification record + TrustAutomatedAnalysis bool + + // Fail fast on malware detection + FailFast bool +} + +func DefaultMalwareAnalyzerConfig() MalwareAnalyzerConfig { + return MalwareAnalyzerConfig{ + TrustAutomatedAnalysis: false, + FailFast: true, + } +} + +type malwareAnalyzer struct { + config MalwareAnalyzerConfig + m sync.Mutex +} + +var _ Analyzer = (*malwareAnalyzer)(nil) + +func NewMalwareAnalyzer(config MalwareAnalyzerConfig) (*malwareAnalyzer, error) { + return &malwareAnalyzer{ + config: config, + }, nil +} + +func (a *malwareAnalyzer) Name() string { + return "Malware Analyzer" +} + +func (a *malwareAnalyzer) Analyze(manifest *models.PackageManifest, + handler AnalyzerEventHandler) error { + return readers.NewManifestModelReader(manifest).EnumPackages(func(pkg *models.Package) error { + err := a.applyMalwareDecision(pkg) + if err != nil { + return fmt.Errorf("MalwareAnalyzer: Failed to apply malware decision for package %s/%s/%s: %w", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion(), err) + } + + if !pkg.IsMalware() && !pkg.IsSuspicious() { + return nil + } + + var filterMsg string + if pkg.IsMalware() { + filterMsg = fmt.Sprintf("MalwareAnalyzer: Package %s/%s/%s is classified as malicious", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion()) + } else { + filterMsg = fmt.Sprintf("MalwareAnalyzer: Package %s/%s/%s is classified as suspicious", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion()) + } + + // Trigger a policy violation event so that it gets recorded + // across reports that are tracking policy violations + err = handler(&AnalyzerEvent{ + Type: ET_FilterExpressionMatched, + Source: a.Name(), + Manifest: manifest, + Package: pkg, + Message: filterMsg, + Filter: &filtersuite.Filter{ + Name: "malware", + CheckType: checks.CheckType_CheckTypeMalware, + Summary: filterMsg, + Description: fmt.Sprintf("%s analyzed the package for possible malicious behavior", a.Name()), + References: []string{"https://docs.safedep.io/cloud/malware-analysis"}, + Tags: []string{"malware-analysis"}, + }, + }) + + if err != nil { + logger.Errorf("MalwareAnalyzer: Failed to handle filter event for package %s/%s/%s: %v", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion(), err) + } + + if a.config.FailFast && pkg.IsMalware() { + return handler(&AnalyzerEvent{ + Type: ET_AnalyzerFailOnError, + Source: a.Name(), + Err: fmt.Errorf("Malware detected in package %s/%s/%s", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion()), + }) + } + + return nil + }) +} + +func (a *malwareAnalyzer) Finish() error { + return nil +} + +// The malware analyzer makes a decision based on configuration and malware analysis results. +// The decision involves: +// +// - No action if the package is not classified as malware +// - Malware if a verfication record is available to confirm +// - Malware if `TrustAutomatedAnalysis` config is enabled and confidence is high +// - Suspicious for all other cases +func (a *malwareAnalyzer) applyMalwareDecision(pkg *models.Package) error { + ma := pkg.GetMalwareAnalysisResult() + if ma == nil || ma.Report == nil { + logger.Warnf("MalwareAnalyzer: No malware analysis result found for package %s/%s/%s", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion()) + return nil + } + + a.m.Lock() + defer a.m.Unlock() + + report := ma.Report + if !report.GetInference().GetIsMalware() { + return nil + } + + vr := ma.VerificationRecord + if vr != nil && vr.GetIsMalware() { + logger.Warnf("MalwareAnalyzer: Package %s/%s/%s is classified as malwars with verification record", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion()) + ma.IsMalware = true + return nil + } + + if a.config.TrustAutomatedAnalysis && + report.GetInference().GetConfidence() == malysisv1.Report_Evidence_CONFIDENCE_HIGH { + logger.Warnf("MalwareAnalyzer: Package %s/%s/%s is classified as malware", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion()) + ma.IsMalware = true + } + + logger.Warnf("MalwareAnalyzer: Package %s/%s/%s is classified as malware with low confidence", + pkg.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion()) + ma.IsSuspicious = true + + return nil +} diff --git a/pkg/analyzer/malware_test.go b/pkg/analyzer/malware_test.go new file mode 100644 index 00000000..1defd3ff --- /dev/null +++ b/pkg/analyzer/malware_test.go @@ -0,0 +1,121 @@ +package analyzer + +import ( + "testing" + + malysisv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/malysis/v1" + "github.com/safedep/vet/pkg/models" + "github.com/stretchr/testify/assert" +) + +func TestMalwareAnalyzerDecision(t *testing.T) { + pkgDetail := models.NewPackageDetail(models.EcosystemNpm, "test", "1.0.0") + pkgManifest := models.NewPackageManifestFromLocal("test", models.EcosystemNpm) + + cases := []struct { + name string + config MalwareAnalyzerConfig + pkg *models.Package + assert func(*models.Package) + }{ + { + name: "no malware analysis result", + config: MalwareAnalyzerConfig{}, + pkg: &models.Package{ + Manifest: pkgManifest, + PackageDetails: pkgDetail, + }, + assert: func(pkg *models.Package) { + assert.Nil(t, pkg.GetMalwareAnalysisResult()) + }, + }, + { + name: "when malware with verification record", + config: MalwareAnalyzerConfig{}, + pkg: &models.Package{ + Manifest: pkgManifest, + PackageDetails: pkgDetail, + MalwareAnalysis: &models.MalwareAnalysisResult{ + Report: &malysisv1.Report{ + Inference: &malysisv1.Report_Inference{ + IsMalware: true, + }, + }, + VerificationRecord: &malysisv1.VerificationRecord{ + IsMalware: true, + }, + }, + }, + assert: func(pkg *models.Package) { + assert.True(t, pkg.GetMalwareAnalysisResult().IsMalware) + }, + }, + { + name: "when malware without verification record", + config: MalwareAnalyzerConfig{}, + pkg: &models.Package{ + Manifest: pkgManifest, + PackageDetails: pkgDetail, + MalwareAnalysis: &models.MalwareAnalysisResult{ + Report: &malysisv1.Report{ + Inference: &malysisv1.Report_Inference{ + IsMalware: true, + }, + }, + }, + }, + assert: func(pkg *models.Package) { + assert.False(t, pkg.GetMalwareAnalysisResult().IsMalware) + assert.True(t, pkg.GetMalwareAnalysisResult().IsSuspicious) + }, + }, + { + name: "when malware without verification record and trusted analysis", + config: MalwareAnalyzerConfig{TrustAutomatedAnalysis: true}, + pkg: &models.Package{ + Manifest: pkgManifest, + PackageDetails: pkgDetail, + MalwareAnalysis: &models.MalwareAnalysisResult{ + Report: &malysisv1.Report{ + Inference: &malysisv1.Report_Inference{ + Confidence: malysisv1.Report_Evidence_CONFIDENCE_HIGH, + IsMalware: true, + }, + }, + }, + }, + assert: func(pkg *models.Package) { + assert.True(t, pkg.GetMalwareAnalysisResult().IsMalware) + }, + }, + { + name: "when malware without verification record and trusted analysis with low confidence", + config: MalwareAnalyzerConfig{TrustAutomatedAnalysis: true}, + pkg: &models.Package{ + Manifest: pkgManifest, + PackageDetails: pkgDetail, + MalwareAnalysis: &models.MalwareAnalysisResult{ + Report: &malysisv1.Report{ + Inference: &malysisv1.Report_Inference{ + Confidence: malysisv1.Report_Evidence_CONFIDENCE_LOW, + IsMalware: true, + }, + }, + }, + }, + assert: func(pkg *models.Package) { + assert.False(t, pkg.GetMalwareAnalysisResult().IsMalware) + assert.True(t, pkg.GetMalwareAnalysisResult().IsSuspicious) + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + a := &malwareAnalyzer{config: tc.config} + err := a.applyMalwareDecision(tc.pkg) + assert.NoError(t, err) + tc.assert(tc.pkg) + }) + } +} diff --git a/pkg/models/models.go b/pkg/models/models.go index b1ffdee5..991ed604 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -8,6 +8,7 @@ import ( "strings" "sync" + malysisv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/malysis/v1" packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1" "github.com/google/osv-scanner/pkg/lockfile" "github.com/safedep/vet/gen/insightapi" @@ -316,6 +317,25 @@ type Provenance struct { Verified bool `json:"verified"` } +// Malware analysis results for a given package +type MalwareAnalysisResult struct { + // The analysis id for this package received from the malware analysis service + AnalysisId string `json:"analysis_id"` + + // Decision if this package is potentially risky but not enough confidence + // to classify as malware + IsSuspicious bool `json:"is_suspicious"` + + // Decision if this package is malware based on analysis data and policy + IsMalware bool `json:"is_malware"` + + // The report generated by the malware analysis service + Report *malysisv1.Report `json:"report"` + + // Verification record for the malware analysis + VerificationRecord *malysisv1.VerificationRecord +} + // Represents a package such as a version of a library defined as a dependency // in Gemfile.lock, pom.xml etc. type Package struct { @@ -336,6 +356,9 @@ type Package struct { // Optional provenances for this package Provenances []*Provenance `json:"provenances"` + // Optional malware analysis result for this package + MalwareAnalysis *MalwareAnalysisResult `json:"malware_analysis"` + // Manifest from where this package was found directly or indirectly Manifest *PackageManifest `json:"-"` } @@ -435,6 +458,30 @@ func (p *Package) GetDependencies() ([]*Package, error) { return dependencies, nil } +func (p *Package) SetMalwareAnalysisResult(result *MalwareAnalysisResult) { + p.MalwareAnalysis = result +} + +func (p *Package) GetMalwareAnalysisResult() *MalwareAnalysisResult { + return p.MalwareAnalysis +} + +func (p *Package) IsMalware() bool { + if p.MalwareAnalysis == nil { + return false + } + + return p.MalwareAnalysis.IsMalware +} + +func (p *Package) IsSuspicious() bool { + if p.MalwareAnalysis == nil { + return false + } + + return p.MalwareAnalysis.IsSuspicious +} + func NewPackageDetail(ecosystem, name, version string) lockfile.PackageDetails { return lockfile.PackageDetails{ Ecosystem: lockfile.Ecosystem(ecosystem), diff --git a/pkg/reporter/markdown/emoji.go b/pkg/reporter/markdown/emoji.go index 40343dc2..14fd9f4f 100644 --- a/pkg/reporter/markdown/emoji.go +++ b/pkg/reporter/markdown/emoji.go @@ -6,4 +6,7 @@ const ( EmojiCrossMark = ":x:" EmojiLink = ":link:" EmojiWarning = ":warning:" + EmojiRedExclamation = ":exclamation:" + EmojiOrangeCircle = ":orange_circle:" + EmojiRedCircle = ":red_circle:" ) diff --git a/pkg/reporter/markdown/markdown.go b/pkg/reporter/markdown/markdown.go index aaf62821..e3e41979 100644 --- a/pkg/reporter/markdown/markdown.go +++ b/pkg/reporter/markdown/markdown.go @@ -53,6 +53,14 @@ func (mb *MarkdownBuilder) AddCodeSnippet(code, language string) { mb.content.WriteString("```\n") } +func (mb *MarkdownBuilder) AddRaw(content string) { + mb.content.WriteString("\n" + content + "\n") +} + +func (mb *MarkdownBuilder) AddQuote(text string) { + mb.content.WriteString("> " + text + "\n") +} + // StartCollapsibleSection starts a collapsible section in the markdown document. func (mb *MarkdownBuilder) StartCollapsibleSection(title string) *MarkdownCollapsibleSection { return &MarkdownCollapsibleSection{ diff --git a/pkg/reporter/markdown_summary.go b/pkg/reporter/markdown_summary.go index a3a1872d..1e3c7569 100644 --- a/pkg/reporter/markdown_summary.go +++ b/pkg/reporter/markdown_summary.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "github.com/jedib0t/go-pretty/v6/table" "github.com/safedep/dry/log" "github.com/safedep/dry/utils" "github.com/safedep/vet/gen/checks" @@ -14,8 +15,11 @@ import ( specmodels "github.com/safedep/vet/gen/models" "github.com/safedep/vet/gen/violations" "github.com/safedep/vet/pkg/analyzer" + "github.com/safedep/vet/pkg/common/logger" + "github.com/safedep/vet/pkg/malysis" "github.com/safedep/vet/pkg/models" "github.com/safedep/vet/pkg/policy" + "github.com/safedep/vet/pkg/readers" "github.com/safedep/vet/pkg/reporter/markdown" ) @@ -25,8 +29,9 @@ const ( ) type MarkdownSummaryReporterConfig struct { - Path string - ReportTitle string + Path string + ReportTitle string + IncludeMalwareAnalysis bool } type vetResultInternalModel struct { @@ -36,10 +41,26 @@ type vetResultInternalModel struct { threats map[jsonreportspec.ReportThreat_ReportThreatId][]*jsonreportspec.ReportThreat } +type markdownSummaryPackageMalwareInfo struct { + ecosystem string + name string + version string + is_malicious bool + is_suspicious bool + referenceUrl string +} + +type markdownSummaryMalwareInfo struct { + malwareInfo map[string]*markdownSummaryPackageMalwareInfo + haveMalwarAnalysisReport int + missingMalwareAnalysis int +} + type markdownSummaryReporter struct { config MarkdownSummaryReporterConfig jsonReportPath string jsonReporter Reporter + malwareInfo *markdownSummaryMalwareInfo } // NewMarkdownSummaryReporter creates a new markdown summary reporter. This reporter @@ -70,6 +91,9 @@ func NewMarkdownSummaryReporter(config MarkdownSummaryReporterConfig) (Reporter, config: config, jsonReportPath: tmpFile.Name(), jsonReporter: jsonReporter, + malwareInfo: &markdownSummaryMalwareInfo{ + malwareInfo: make(map[string]*markdownSummaryPackageMalwareInfo), + }, }, nil } @@ -79,6 +103,21 @@ func (r *markdownSummaryReporter) Name() string { func (r *markdownSummaryReporter) AddManifest(manifest *models.PackageManifest) { r.jsonReporter.AddManifest(manifest) + + err := readers.NewManifestModelReader(manifest).EnumPackages(func(pkg *models.Package) error { + err := r.malwareInfo.handlePackage(pkg) + if err != nil { + logger.Errorf("[Markdown Reporter]: Failed to handle malware info for package %s: %v", + pkg.GetName(), err) + } + + return nil + }) + + if err != nil { + logger.Errorf("[Markdown Reporter]: Failed to enumerate packages in manifest %s: %v", + manifest.GetPath(), err) + } } func (r *markdownSummaryReporter) AddAnalyzerEvent(event *analyzer.AnalyzerEvent) { @@ -145,6 +184,13 @@ func (r *markdownSummaryReporter) buildMarkdownReport(builder *markdown.Markdown return fmt.Errorf("failed to add threats section: %w", err) } + if r.config.IncludeMalwareAnalysis { + err = r.addMalwareAnalysisReportSection(builder) + if err != nil { + return fmt.Errorf("failed to add malware analysis section: %w", err) + } + } + err = r.addChangedPackageSection(builder, internalModel) if err != nil { return fmt.Errorf("failed to add changed package section: %w", err) @@ -370,6 +416,27 @@ func (r *markdownSummaryReporter) addViolationSection(builder *markdown.Markdown return nil } +func (r *markdownSummaryReporter) addMalwareAnalysisReportSection(builder *markdown.MarkdownBuilder) error { + malwareInfoTable, err := r.malwareInfo.renderMalwareInfoTable() + if err != nil { + return fmt.Errorf("failed to render malware info table: %w", err) + } + + builder.AddHeader(2, "Malicious Package Analysis") + builder.AddParagraph("The following packages have been analyzed for malware") + builder.AddRaw(malwareInfoTable) + builder.AddParagraph(fmt.Sprintf("%d/%d packages have malware analysis reports", + r.malwareInfo.haveMalwarAnalysisReport, + r.malwareInfo.haveMalwarAnalysisReport+r.malwareInfo.missingMalwareAnalysis)) + + if r.malwareInfo.missingMalwareAnalysis > 0 { + builder.AddQuote("Note: Some of the package analysis jobs may still be running." + + "Please check back later. Consider increasing the timeout for better coverage.") + } + + return nil +} + func (r *markdownSummaryReporter) getCheckIconByCheckType(internalModel *vetResultInternalModel, ct checks.CheckType) string { if _, ok := internalModel.violations[ct]; !ok { @@ -422,3 +489,51 @@ func (r *markdownSummaryReporter) getPackageExternalReferenceUrl(pkg *specmodels url.QueryEscape(pkg.GetName()), url.QueryEscape(version)) } + +// Update the local cache of malware analysis stats +func (m *markdownSummaryMalwareInfo) handlePackage(pkg *models.Package) error { + ma := pkg.GetMalwareAnalysisResult() + if ma == nil { + m.missingMalwareAnalysis++ + return nil + } + + if _, ok := m.malwareInfo[pkg.Id()]; !ok { + m.haveMalwarAnalysisReport++ + m.malwareInfo[pkg.Id()] = &markdownSummaryPackageMalwareInfo{ + ecosystem: pkg.GetControlTowerSpecEcosystem().String(), + name: pkg.GetName(), + version: pkg.GetVersion(), + is_malicious: ma.IsMalware, + is_suspicious: ma.IsSuspicious, + referenceUrl: malysis.ReportURL(ma.AnalysisId), + } + } + + return nil +} + +// Render the malware info cache as markdown table +func (m *markdownSummaryMalwareInfo) renderMalwareInfoTable() (string, error) { + tbl := table.NewWriter() + tbl.AppendHeader(table.Row{"Ecosystem", "Package", "Version", "Status", "Report"}) + + for _, info := range m.malwareInfo { + emoji := markdown.EmojiWhiteCheckMark + if info.is_malicious { + emoji = markdown.EmojiCrossMark + } else if info.is_suspicious { + emoji = markdown.EmojiWarning + } + + tbl.AppendRow(table.Row{ + info.ecosystem, + info.name, + info.version, + emoji, + fmt.Sprintf("[%s](%s)", markdown.EmojiLink, info.referenceUrl), + }) + } + + return tbl.RenderMarkdown(), nil +} diff --git a/pkg/reporter/summary.go b/pkg/reporter/summary.go index 4c2be8f8..352f0dfc 100644 --- a/pkg/reporter/summary.go +++ b/pkg/reporter/summary.go @@ -90,6 +90,14 @@ type summaryReporter struct { verified int unverified int } + + // Stats for active malware scanning results ONLY + // Do not use these stats for OSV based malware database results + malware struct { + scanned int + malicious int + suspicious int + } } // Map of pkgId and associated meta for building remediation advice @@ -219,15 +227,37 @@ func (r *summaryReporter) processForPopularity(pkg *models.Package) { } func (r *summaryReporter) processForMalware(pkg *models.Package) { + // First we check for known malware from OSV MAL database insight := utils.SafelyGetValue(pkg.Insights) vulns := utils.SafelyGetValue(insight.Vulnerabilities) + malwareTaggerFn := func(pkg *models.Package) { + r.summary.vulns.critical += 1 + r.addPkgForRemediationAdvice(pkg, summaryWeightCriticalVuln, "malware") + } + + suspiciousTaggerFn := func(pkg *models.Package) { + r.summary.vulns.high += 1 + r.addPkgForRemediationAdvice(pkg, summaryWeightHighVuln, "suspicious") + } + for _, vuln := range vulns { // OSV API follows the convention of using MAL-YYYY-ID convention // as generated by https://github.com/ossf/malicious-packages if strings.HasPrefix(utils.SafelyGetValue(vuln.Id), "MAL-") { - r.summary.vulns.critical += 1 - r.addPkgForRemediationAdvice(pkg, summaryWeightCriticalVuln, "malware") + malwareTaggerFn(pkg) + } + } + + // Then we check for malware from Malysis package analysis service + if ma := pkg.GetMalwareAnalysisResult(); ma != nil { + r.summary.malware.scanned += 1 + if ma.IsMalware { + r.summary.malware.malicious += 1 + malwareTaggerFn(pkg) + } else if ma.IsSuspicious { + r.summary.malware.suspicious += 1 + suspiciousTaggerFn(pkg) } } } @@ -325,7 +355,6 @@ func (r *summaryReporter) addPkgForVulnerabilityRisk(pkg *models.Package, } func (r *summaryReporter) Finish() error { - fmt.Println(summaryListPrependText, text.BgBlue.Sprint(" Summary of Findings ")) fmt.Println() fmt.Println(text.FgHiRed.Sprint(summaryListPrependText, r.vulnSummaryStatement())) @@ -334,6 +363,8 @@ func (r *summaryReporter) Finish() error { fmt.Println() fmt.Println(text.FgHiYellow.Sprint(summaryListPrependText, r.provenanceStatement())) fmt.Println() + fmt.Println(text.FgHiYellow.Sprint(summaryListPrependText, r.malwareAnalysisStatement())) + fmt.Println() fmt.Println(text.FgHiYellow.Sprint(summaryListPrependText, r.majorVersionDriftStatement())) fmt.Println() fmt.Println(text.Faint.Sprint(summaryListPrependText, r.manifestCountStatement())) @@ -451,7 +482,7 @@ func (r *summaryReporter) renderRemediationAdvice() { return } - fmt.Println(text.Bold.Sprint(fmt.Sprintf("Top %d libraries to upgrade ...", r.config.MaxAdvice))) + fmt.Println(text.Bold.Sprint(fmt.Sprintf("Top %d libraries to fix ...", r.config.MaxAdvice))) fmt.Println() tbl := table.NewWriter() @@ -696,6 +727,11 @@ func (r *summaryReporter) provenanceStatement() string { r.summary.provenance.none) } +func (r *summaryReporter) malwareAnalysisStatement() string { + return fmt.Sprintf("%d/%d libraries were actively scanned for malware", + r.summary.malware.scanned, r.summary.packages) +} + func (r *summaryReporter) majorVersionDriftStatement() string { return fmt.Sprintf("%d libraries are out of date with major version drift in direct dependencies", r.summary.metrics.drifts) diff --git a/pkg/scanner/enrich.go b/pkg/scanner/enrich.go index cf0b6e46..df74040d 100644 --- a/pkg/scanner/enrich.go +++ b/pkg/scanner/enrich.go @@ -10,6 +10,12 @@ type PackageDependencyCallbackFn func(pkg *models.Package) error // Enrich meta information associated with // the package type PackageMetaEnricher interface { + // Name of the enricher Name() string + + // Enrich the package with meta information Enrich(pkg *models.Package, cb PackageDependencyCallbackFn) error + + // Wait for all the enrichments to complete + Wait() error } diff --git a/pkg/scanner/enrich_insightsv1.go b/pkg/scanner/enrich_insightsv1.go index 8d9e7d3f..259c2396 100644 --- a/pkg/scanner/enrich_insightsv1.go +++ b/pkg/scanner/enrich_insightsv1.go @@ -100,6 +100,11 @@ func (e *insightsBasedPackageEnricher) Enrich(pkg *models.Package, } } + // We should acquire a lock before mutating package? pkg.Insights = res.JSON200 return nil } + +func (e *insightsBasedPackageEnricher) Wait() error { + return nil +} diff --git a/pkg/scanner/enrich_insightsv2.go b/pkg/scanner/enrich_insightsv2.go index 737204e3..1d2f5dd3 100644 --- a/pkg/scanner/enrich_insightsv2.go +++ b/pkg/scanner/enrich_insightsv2.go @@ -300,6 +300,10 @@ func (e *insightsBasedPackageEnricherV2) convertInsightsV2ToV1(pvi *packagev1.Pa return insights, nil } +func (e *insightsBasedPackageEnricherV2) Wait() error { + return nil +} + // Should this be in models? func (e *insightsBasedPackageEnricherV2) mapEcosystem(ecosystem packagev1.Ecosystem) string { return models.GetModelEcosystem(ecosystem) diff --git a/pkg/scanner/enrich_malware.go b/pkg/scanner/enrich_malware.go new file mode 100644 index 00000000..f0b2b67b --- /dev/null +++ b/pkg/scanner/enrich_malware.go @@ -0,0 +1,364 @@ +package scanner + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "buf.build/gen/go/safedep/api/grpc/go/safedep/services/malysis/v1/malysisv1grpc" + malysisv1pb "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/malysis/v1" + packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1" + malysisv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/malysis/v1" + "github.com/safedep/vet/pkg/common/logger" + "github.com/safedep/vet/pkg/models" + "google.golang.org/grpc" +) + +var errMalysisPollRetryInFuture = errors.New("retry in future") + +type MalysisMalwareEnricherConfig struct { + // Timeout for the enricher starting from the time of initialization. + // This includes the time taken to submit the package for analysis and + // waiting for the analysis to complete. + Timeout time.Duration + + // Timeout for the gRPC operation to the Malysis service. This timeout + // is for a single gRPC operation. + GrpcOperationTimeout time.Duration + + // Number of workers to poll the analysis results + QueryWorkerCount int + + // Maximum number of retries for querying the analysis results for a given + // analysis identifier. + MaxQueryRetries int +} + +func DefaultMalysisMalwareEnricherConfig() MalysisMalwareEnricherConfig { + return MalysisMalwareEnricherConfig{ + Timeout: 5 * time.Minute, + QueryWorkerCount: 10, + GrpcOperationTimeout: 10 * time.Second, + MaxQueryRetries: 10, + } +} + +type malysisMalwareEnricher struct { + cc *grpc.ClientConn + client malysisv1grpc.MalwareAnalysisServiceClient + config MalysisMalwareEnricherConfig + + // Cache of analysis identifiers to poll and apply the results + // to all packages that were submitted for analysis. Malysis internally + // maintains a cache for analysis result for a given package. It will + // not re-analyse the package if it has already been analysed or submitted + // for analysis. + queryCache map[string][]*models.Package + qcLock sync.Mutex + + // Channel to submit analysis identifiers to the query worker + queryChannel chan *analysisQueryRequest + + // Channel to push results by the query worker + resultsChannel chan *analysisQueryResult + + // Wait group to synchronize between submissions and completions + wg sync.WaitGroup + + // Waiting context + ctx context.Context +} + +var _ PackageMetaEnricher = (*malysisMalwareEnricher)(nil) + +type analysisSubmissionResult struct { + analysisId string +} + +type analysisQueryRequest struct { + analysisId string + retryCount int + nextRetryAt time.Time +} + +type analysisQueryResult struct { + req *analysisQueryRequest + response *malysisv1.GetAnalysisReportResponse + err error +} + +func NewMalysisMalwareEnricher(cc *grpc.ClientConn, + config MalysisMalwareEnricherConfig) (*malysisMalwareEnricher, error) { + if cc == nil { + return nil, errors.New("grpc client connection is required") + } + + ctx, cancelFn := context.WithTimeout(context.Background(), config.Timeout) + go func() { + _ = time.AfterFunc(config.Timeout+(500*time.Millisecond), cancelFn) + }() + + client := malysisv1grpc.NewMalwareAnalysisServiceClient(cc) + enricher := &malysisMalwareEnricher{ + cc: cc, + client: client, + config: config, + queryCache: make(map[string][]*models.Package), + queryChannel: make(chan *analysisQueryRequest, 10000), + resultsChannel: make(chan *analysisQueryResult, 10000), + ctx: ctx, + } + + err := enricher.startResultWorker(ctx) + if err != nil { + cancelFn() + return nil, fmt.Errorf("failed to start result worker: %w", err) + } + + err = enricher.startQueryWorker(ctx) + if err != nil { + cancelFn() + return nil, fmt.Errorf("failed to start query worker: %w", err) + } + + return enricher, nil +} + +func (e *malysisMalwareEnricher) Name() string { + return "Malysis Malware Enricher" +} + +// We will submit all packages for analysis because our backend has caching. +// This will ensure that eventually the packages will get analysed and +// subsequent scans will have better coverage in terms of malware analysis. +func (e *malysisMalwareEnricher) Enrich(pkg *models.Package, + _ PackageDependencyCallbackFn) error { + // Submit for analysis + res, err := e.submitPackageForAnalysis(pkg) + if err != nil { + return fmt.Errorf("failed to submit package for analysis: %w", err) + } + + // Wait for the analysis to complete + e.wg.Add(1) + + // Add the analysis identifier to the cache for query go routine + // to poll and apply the results to all packages that were submitted + e.qcLock.Lock() + defer e.qcLock.Unlock() + + if _, ok := e.queryCache[res.analysisId]; !ok { + e.queryCache[res.analysisId] = make([]*models.Package, 0) + } + + e.queryCache[res.analysisId] = append(e.queryCache[res.analysisId], pkg) + e.enqueueAnalysisForQuery(&analysisQueryRequest{analysisId: res.analysisId}) + + return nil +} + +// Wait returns when there are no more pending enrichments +// or when timeout is reached. +func (e *malysisMalwareEnricher) Wait() error { + ch := make(chan bool) + go func() { + e.wg.Wait() + close(ch) + }() + + select { + case <-e.ctx.Done(): + return e.ctx.Err() + case <-ch: + return nil + } +} + +func (e *malysisMalwareEnricher) enqueueAnalysisForQuery(req *analysisQueryRequest) { + // If we are in the backoff period, we will schedule an enqueue + // operation in the future else we will submit the request immediately. + if time.Now().Before(req.nextRetryAt) { + // We are creating unbounded go routines here. This is fine because + // the number of retries are limited and the go routines will be + // garbage collected once the retries are exhausted. + go func(ctx context.Context) { + timer := time.NewTimer(time.Until(req.nextRetryAt.Add(100 * time.Millisecond))) + defer timer.Stop() + + select { + case <-ctx.Done(): + return + case <-timer.C: + } + + e.queryChannel <- req + }(e.ctx) + } else { + req.retryCount++ + req.nextRetryAt = time.Now().Add(5 * time.Second * time.Duration(req.retryCount)) + + e.queryChannel <- req + } +} + +func (e *malysisMalwareEnricher) submitPackageForAnalysis(pkg *models.Package) (*analysisSubmissionResult, error) { + logger.Infof("[Malware Analysis] Submitting package for malware analysis: %s/%s/%s", + pkg.Manifest.Ecosystem, pkg.PackageDetails.Name, pkg.PackageDetails.Version) + + req := malysisv1.AnalyzePackageRequest{ + Target: &malysisv1pb.PackageAnalysisTarget{ + PackageVersion: &packagev1.PackageVersion{ + Package: &packagev1.Package{ + Ecosystem: pkg.GetControlTowerSpecEcosystem(), + Name: pkg.GetName(), + }, + Version: pkg.GetVersion(), + }, + }, + } + + ctx, cancelFn := context.WithTimeout(context.Background(), e.config.GrpcOperationTimeout) + defer cancelFn() + + res, err := e.client.AnalyzePackage(ctx, &req) + if err != nil { + return nil, fmt.Errorf("failed to submit package for analysis: %w", err) + } + + return &analysisSubmissionResult{analysisId: res.AnalysisId}, nil +} + +// Receive results from query worker and apply to all packages. +// In case of error, re-submit for query. +func (e *malysisMalwareEnricher) startResultWorker(ctx context.Context) error { + go func() { + for { + select { + case <-ctx.Done(): + return + case msg, ok := <-e.resultsChannel: + if !ok { + return + } + + // Extract the response and error from the message + res, err := msg.response, msg.err + + // Check for incomplete analysis + if (res != nil) && err == nil { + // If the analysis explicitly failed at the service provider, we will + // log the error and will NOT retry the polling. + if res.Status == malysisv1.AnalysisStatus_ANALYSIS_STATUS_FAILED { + logger.Errorf("[Malware Analysis] Analysis Id: %s failed with error: %s", + msg.req.analysisId, res.GetErrorMessage()) + + e.wg.Done() + continue + } + + // For any other status, we will retry the polling by returning + // an error to the results channel. + if res.Status != malysisv1.AnalysisStatus_ANALYSIS_STATUS_COMPLETED { + err = fmt.Errorf("analysis is not completed: %s", res.Status) + } + } + + if err != nil { + if msg.req.retryCount >= e.config.MaxQueryRetries { + logger.Errorf("[Malware Analysis] Max retries exceeded for analysis: %s", msg.req.analysisId) + e.wg.Done() + continue + } + + e.enqueueAnalysisForQuery(msg.req) + continue + } + + // At this point, we will not retry the query any more + func() { + defer e.wg.Done() + + e.qcLock.Lock() + defer e.qcLock.Unlock() + + if res == nil { + logger.Errorf("[Malware Analysis] Empty response for analysis: %s", msg.req.analysisId) + return + } + + if res.GetReport() == nil { + logger.Errorf("[Malware Analysis] Empty report for analysis: %s", msg.req.analysisId) + return + } + + // Apply the results to all packages that were submitted for analysis + if packages, ok := e.queryCache[msg.req.analysisId]; ok { + for _, pkg := range packages { + // Apply the results to the package + logger.Debugf("[Malware Analysis] Applying results to package: %s/%s/%s", + pkg.Manifest.GetControlTowerSpecEcosystem(), pkg.GetName(), pkg.GetVersion()) + + // Here we only enrich the package with the malware analysis result. + // We do not make a decision based on the result. + pkg.SetMalwareAnalysisResult(&models.MalwareAnalysisResult{ + AnalysisId: msg.req.analysisId, + Report: res.GetReport(), + VerificationRecord: res.GetVerificationRecord(), + }) + } + } + }() + } + } + }() + + return nil +} + +// Poll the result for a given analysisId +func (e *malysisMalwareEnricher) startQueryWorker(ctx context.Context) error { + for i := 0; i < e.config.QueryWorkerCount; i++ { + go func() { + for { + select { + case <-ctx.Done(): + return + case req, ok := <-e.queryChannel: + if !ok { + return + } + + // If we are in the backoff period, we will not query the service + if time.Now().Before(req.nextRetryAt) { + logger.Debugf("[Malware Analysis] Retrying query for analysis report: %s in %s", + req.analysisId, time.Until(req.nextRetryAt)) + + e.resultsChannel <- &analysisQueryResult{ + req: req, + err: errMalysisPollRetryInFuture, + } + + continue + } + + ctx, cancelFn := context.WithTimeout(ctx, e.config.GrpcOperationTimeout) + defer cancelFn() + + res, err := e.client.GetAnalysisReport(ctx, &malysisv1.GetAnalysisReportRequest{ + AnalysisId: req.analysisId, + }) + + e.resultsChannel <- &analysisQueryResult{ + req: req, + response: res, + err: err, + } + } + } + }() + } + + return nil +} diff --git a/pkg/scanner/enrich_malware_test.go b/pkg/scanner/enrich_malware_test.go new file mode 100644 index 00000000..dc686613 --- /dev/null +++ b/pkg/scanner/enrich_malware_test.go @@ -0,0 +1,93 @@ +package scanner + +import ( + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" +) + +func TestMalysisMalwareEnricher(t *testing.T) { + t.Run("NewMalysisMalwareEnricher", func(t *testing.T) { + t.Run("should reject nil grpc client connection", func(t *testing.T) { + _, err := NewMalysisMalwareEnricher(nil, DefaultMalysisMalwareEnricherConfig()) + assert.Error(t, err) + assert.ErrorContains(t, err, "grpc client connection is required") + }) + + t.Run("should wait till max timeout", func(t *testing.T) { + enricher, err := NewMalysisMalwareEnricher(&grpc.ClientConn{}, MalysisMalwareEnricherConfig{ + Timeout: 1, + QueryWorkerCount: 0, + }) + + if err != nil { + t.Fatalf("failed to create enricher: %v", err) + } + + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + _ = enricher.Wait() + }() + + closer := make(chan bool) + go func() { + wg.Wait() + close(closer) + }() + + timer := time.NewTimer(2 * time.Second) + select { + case <-timer.C: + assert.Fail(t, "timeout occurred instead of completion") + case <-closer: + timer.Stop() + } + }) + }) +} + +func TestMalysisMalwareEnricherQueryWorker(t *testing.T) { + t.Run("MalysisMalwareEnricher/QueryWorker", func(t *testing.T) { + t.Run("should return error for requests with future retry", func(t *testing.T) { + enricher, err := NewMalysisMalwareEnricher(&grpc.ClientConn{}, MalysisMalwareEnricherConfig{ + Timeout: 5 * time.Second, + QueryWorkerCount: 1, + MaxQueryRetries: 3, + }) + + assert.NoError(t, err) + assert.NotNil(t, enricher) + assert.NotNil(t, enricher.queryChannel) + assert.NotNil(t, enricher.resultsChannel) + + enricher.queryChannel <- &analysisQueryRequest{ + analysisId: "test", + retryCount: 1, + nextRetryAt: time.Now().Add(5 * time.Second), + } + + enricher.wg.Add(1) + waiterCh := make(chan bool) + go func() { + err = enricher.Wait() + assert.NoError(t, err, "error occurred while waiting for enricher") + + close(waiterCh) + }() + + select { + case result := <-enricher.resultsChannel: + assert.Error(t, result.err) + assert.ErrorContains(t, result.err, errMalysisPollRetryInFuture.Error()) + case <-waiterCh: + assert.Fail(t, "timeout occurred instead of completion") + } + }) + }) +} diff --git a/pkg/scanner/scanner.go b/pkg/scanner/scanner.go index 380b3c4a..7d51e4c9 100644 --- a/pkg/scanner/scanner.go +++ b/pkg/scanner/scanner.go @@ -244,6 +244,25 @@ func (s *packageManifestScanner) enrichManifest(manifest *models.PackageManifest q.Wait() q.Stop() + // Finally wait for all enrichers to finish + err := s.packageEnricherWait() + if err != nil { + return fmt.Errorf("package enricher wait failed: %w", err) + } + + return nil +} + +func (s *packageManifestScanner) packageEnricherWait() error { + for _, enricher := range s.enrichers { + logger.Debugf("Waiting for enricher %s to finish", enricher.Name()) + + err := enricher.Wait() + if err != nil { + logger.Errorf("Failed to wait for enricher %s: %v", enricher.Name(), err) + } + } + return nil } diff --git a/scan.go b/scan.go index 0ca5d60f..124e2667 100644 --- a/scan.go +++ b/scan.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "time" "github.com/google/go-github/v54/github" "github.com/safedep/dry/utils" @@ -26,6 +27,7 @@ var ( lockfileAs string enrich bool enrichUsingInsightsV2 bool + enrichMalware bool baseDirectory string purlSpec string githubRepoUrls []string @@ -60,6 +62,8 @@ var ( failFast bool trustedRegistryUrls []string scannerExperimental bool + malwareAnalyzerTrustToolResult bool + malwareAnalysisTimeout time.Duration ) func newScanCommand() *cobra.Command { @@ -85,6 +89,8 @@ func newScanCommand() *cobra.Command { "Enrich package metadata (almost always required) using Insights API") cmd.Flags().BoolVarP(&enrichUsingInsightsV2, "insights-v2", "", false, "Enrich package metadata using Insights V2 API") + cmd.Flags().BoolVarP(&enrichMalware, "malware", "", false, + "Enrich package metadata with malware analysis results") cmd.Flags().StringVarP(&baseDirectory, "directory", "D", wd, "The directory to scan for package manifests") cmd.Flags().StringArrayVarP(&scanExclude, "exclude", "", []string{}, @@ -155,6 +161,10 @@ func newScanCommand() *cobra.Command { "Trusted registry URLs to use for package manifest verification") cmd.Flags().BoolVarP(&scannerExperimental, "experimental", "", false, "Enable experimental features in scanner") + cmd.Flags().BoolVarP(&malwareAnalyzerTrustToolResult, "malware-trust-tool-result", "", false, + "Trust malicious package analysis tool result without verification record") + cmd.Flags().DurationVarP(&malwareAnalysisTimeout, "malware-analysis-timeout", "", 5*time.Minute, + "Timeout for malicious package analysis") cmd.AddCommand(listParsersCommand()) return cmd @@ -315,6 +325,19 @@ func internalStartScan() error { analyzers = append(analyzers, task) } + if enrichMalware { + config := analyzer.DefaultMalwareAnalyzerConfig() + config.TrustAutomatedAnalysis = malwareAnalyzerTrustToolResult + config.FailFast = failFast + + task, err := analyzer.NewMalwareAnalyzer(config) + if err != nil { + return err + } + + analyzers = append(analyzers, task) + } + reporters := []reporter.Reporter{} if consoleReport { rp, err := reporter.NewConsoleReporter() @@ -350,7 +373,8 @@ func internalStartScan() error { if !utils.IsEmptyString(markdownSummaryReportPath) { rp, err := reporter.NewMarkdownSummaryReporter(reporter.MarkdownSummaryReporterConfig{ - Path: markdownSummaryReportPath, + Path: markdownSummaryReportPath, + IncludeMalwareAnalysis: enrichMalware, }) if err != nil { return err @@ -465,6 +489,29 @@ func internalStartScan() error { enrichers = append(enrichers, enricher) } + if enrichMalware { + if auth.CommunityMode() { + return fmt.Errorf("Malicious Package Analysis requires an API key. " + + "For more details: https://docs.safedep.io/cloud/quickstart/") + } + + client, err := auth.MalwareAnalysisClientConnection("vet-malware-analysis") + if err != nil { + return err + } + + config := scanner.DefaultMalysisMalwareEnricherConfig() + config.Timeout = malwareAnalysisTimeout + + malwareEnricher, err := scanner.NewMalysisMalwareEnricher(client, config) + if err != nil { + return err + } + + ui.PrintMsg("Using Malysis for malware analysis") + enrichers = append(enrichers, malwareEnricher) + } + pmScanner := scanner.NewPackageManifestScanner(scanner.Config{ TransitiveAnalysis: transitiveAnalysis, TransitiveDepth: transitiveDepth, diff --git a/test/scenarios/all.sh b/test/scenarios/all.sh index a4932544..d4be7cd4 100755 --- a/test/scenarios/all.sh +++ b/test/scenarios/all.sh @@ -17,3 +17,4 @@ bash $E2E_THIS_DIR/scenario-5-gradle-depgraph-build.sh bash $E2E_THIS_DIR/scenario-6-manifest-flag.sh bash $E2E_THIS_DIR/scenario-7-rubygems-project-url.sh bash $E2E_THIS_DIR/scenario-8-summary-report.sh +bash $E2E_THIS_DIR/scenario-9-malware-analysis.sh diff --git a/test/scenarios/scenario-9-malware-analysis.sh b/test/scenarios/scenario-9-malware-analysis.sh new file mode 100644 index 00000000..49f42f47 --- /dev/null +++ b/test/scenarios/scenario-9-malware-analysis.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -ex + +# We need API key to run this test +# hence we are checking if E2E_VET_INSIGHTS_V2 is set to true +# because API key is available for Insights v2 +if [ "$E2E_VET_INSIGHTS_V2" != "true" ]; then + echo "Skipping scenario-8-summary-report.sh as E2E_INSIGHTS_V2 is not set to true" + exit 0 +fi + +# Not a malicious package +$E2E_VET_SCAN_CMD scan --purl pkg:/npm/@clerk/nextjs@6.9.6 \ + --malware --malware-analysis-timeout 60s --fail-fast + +# This is a malicious package +$E2E_VET_SCAN_CMD scan --purl pkg:/npm/llm-oracle@1.0.2 \ + --malware --malware-analysis-timeout 60s --fail-fast --malware-trust-tool-result || exit 0 + +# Fail if we don't already exit on malicious package detection +exit 1