diff --git a/cmd/sign-cache.go b/cmd/sign-cache.go index 10abd443..b1e5655c 100644 --- a/cmd/sign-cache.go +++ b/cmd/sign-cache.go @@ -39,7 +39,7 @@ Example: RunE: func(cmd *cobra.Command, args []string) error { manifestPath, _ := cmd.Flags().GetString("from-manifest") dryRun, _ := cmd.Flags().GetBool("dry-run") - + // Get max concurrency setting (env var as default, CLI flag overrides) maxConcurrency, _ := cmd.Flags().GetInt("max-signing-concurrency") if !cmd.Flags().Changed("max-signing-concurrency") { diff --git a/go.mod b/go.mod index b24fa738..331041a8 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.24.1 require ( github.com/anchore/clio v0.0.0-20250926015255-f418e0b4892c - github.com/anchore/grype v0.90.0 - github.com/anchore/syft v1.21.0 + github.com/anchore/grype v0.91.0 + github.com/anchore/syft v1.22.0 github.com/aws/aws-sdk-go-v2 v1.38.1 github.com/aws/aws-sdk-go-v2/config v1.31.3 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.59 @@ -59,7 +59,7 @@ require ( dario.cat/mergo v1.0.2 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect github.com/CycloneDX/cyclonedx-go v0.9.2 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 // indirect @@ -73,7 +73,8 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.11.7 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect - github.com/ProtonMail/go-crypto v1.1.5 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/STARRY-S/zip v0.2.1 // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/adrg/xdg v0.5.3 // indirect github.com/agext/levenshtein v1.2.1 // indirect @@ -84,9 +85,10 @@ require ( github.com/anchore/go-logger v0.0.0-20250813181427-74728f89a619 // indirect github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect + github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6 // indirect github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 // indirect github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 // indirect - github.com/anchore/stereoscope v0.1.0 // indirect + github.com/anchore/stereoscope v0.1.2 // indirect github.com/andybalholm/brotli v1.1.1 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aquasecurity/go-pep440-version v0.0.1 // indirect @@ -111,10 +113,14 @@ require ( github.com/becheran/wildmatch-go v1.0.0 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef // indirect + github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/bmatcuk/doublestar/v2 v2.0.4 // indirect github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect + github.com/bodgit/plumbing v1.3.0 // indirect + github.com/bodgit/sevenzip v1.6.0 // indirect + github.com/bodgit/windows v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -126,7 +132,7 @@ require ( github.com/cloudflare/circl v1.6.0 // indirect github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect github.com/containerd/cgroups v1.1.0 // indirect - github.com/containerd/containerd v1.7.26 // indirect + github.com/containerd/containerd v1.7.27 // indirect github.com/containerd/containerd/api v1.8.0 // indirect github.com/containerd/continuity v0.4.4 // indirect github.com/containerd/errdefs v1.0.0 // indirect @@ -154,9 +160,8 @@ require ( github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect + github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/elliotchance/phpserialize v1.4.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect @@ -201,6 +206,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/gohugoio/hashstructure v0.5.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/certificate-transparency-go v1.3.2 // indirect @@ -248,11 +254,11 @@ require ( 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 + github.com/mholt/archives v0.1.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect @@ -265,6 +271,7 @@ require ( github.com/muesli/termenv v0.16.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nwaples/rardecode v1.1.3 // indirect + github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/gomega v1.35.1 // indirect @@ -288,13 +295,11 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect - github.com/saferwall/pe v1.5.6 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/sassoftware/go-rpmutils v0.4.0 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect - github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d // indirect github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 // indirect github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect github.com/segmentio/backo-go v1.0.0 // indirect @@ -308,6 +313,7 @@ require ( github.com/sigstore/timestamp-authority v1.2.9 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect + github.com/sorairolake/lzip-go v0.3.5 // indirect github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb // indirect github.com/spdx/tools-golang v0.5.5 // indirect github.com/spf13/afero v1.15.0 // indirect @@ -316,8 +322,8 @@ require ( github.com/spf13/viper v1.21.0 // indirect github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/sylabs/sif/v2 v2.20.2 // indirect - github.com/sylabs/squashfs v1.0.5 // indirect + github.com/sylabs/sif/v2 v2.21.1 // indirect + github.com/sylabs/squashfs v1.0.6 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect github.com/theupdateframework/go-tuf/v2 v2.2.0 // indirect @@ -349,6 +355,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect + go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect golang.org/x/net v0.47.0 // indirect diff --git a/go.sum b/go.sum index 2790f8ef..b9f336e6 100644 --- a/go.sum +++ b/go.sum @@ -649,8 +649,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJ github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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= @@ -684,8 +684,10 @@ github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -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/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg= +github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4= github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= @@ -718,18 +720,20 @@ github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIj github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb/go.mod h1:DmTY2Mfcv38hsHbG78xMiTDdxFtkHpgYNVDPsF2TgHk= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= +github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6 h1:Ha+LSCVuXYSYGi7wIkJK6G8g6jI3LH7y6LbyEVyp4Io= +github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6/go.mod h1:+9oM3XUy8iea/vWj9FhZ9bQGUBN8JpPxxJm5Wbcx9XM= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0vW0nnNKJfJieyH/TZ9UYAnTZs5/gHTdAe8= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ= github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE+o2gozGEBoUMpX27lsku+xrMwlmBZJtbg= github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= -github.com/anchore/grype v0.90.0 h1:iiaEzzlXGHoYLyWKifFuAeeWgy6gj64UBx+m9e7AJrs= -github.com/anchore/grype v0.90.0/go.mod h1:hfuMwzA7AVXTPMIaXDib8HAil3E5uazoMfm877oTRFM= +github.com/anchore/grype v0.91.0 h1:x6/jweLDNp+jy6ufyCukBJbFAlVefxSUOqb2eFsJdQY= +github.com/anchore/grype v0.91.0/go.mod h1:O+lJcLV4OWTPwA8Rvr8U1utnqqNA0WgvVgc0q0QbpJw= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= -github.com/anchore/stereoscope v0.1.0 h1:waF2C5b/XRAyShKup3VoJz8Pdv7AiDxUbABTizmJdJ0= -github.com/anchore/stereoscope v0.1.0/go.mod h1:3vasimie0IJOXvMbMpjwvwIHBDA1+192QZDWNhJRqFQ= -github.com/anchore/syft v1.21.0 h1:JHmYOnEbCJsElROCCfg+3oIODw1LQLfXGkIrmXNZYsI= -github.com/anchore/syft v1.21.0/go.mod h1:8i8Yp/MiSOdqID0+6eiwE9bOJWM7fEBYitINZyr2G6s= +github.com/anchore/stereoscope v0.1.2 h1:0+Jcf7hoImYKfrH2XzN6vvbmbpm68A/woabC43gZ4eU= +github.com/anchore/stereoscope v0.1.2/go.mod h1:kU4LkiocOimxuSnlmJgilHCfDMCR5t/UroKIpzI8jNw= +github.com/anchore/syft v1.22.0 h1:lmNuy1uQ9GZeO6HPms3ik3W0hTE9MxP46vtC3vssBe4= +github.com/anchore/syft v1.22.0/go.mod h1:5U0av1cf7i/O2VusM29HTsVYUsARjBvgPCbP4RtSEyk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -813,6 +817,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAOuWbeDDTtwXWE6z+PmpgbSsMBeV7l0ww= github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -823,6 +829,12 @@ github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= +github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= +github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A= +github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc= +github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= +github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= @@ -889,8 +901,8 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.26 h1:3cs8K2RHlMQaPifLqgRyI4VBkoldNdEw62cb7qQga7k= -github.com/containerd/containerd v1.7.26/go.mod h1:m4JU0E+h0ebbo9yXD7Hyt+sWnc8tChm7MudCjj4jRvQ= +github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII= +github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0= github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII= @@ -963,14 +975,12 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 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/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/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= +github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= -github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY= @@ -1144,6 +1154,8 @@ github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg= +github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= @@ -1498,6 +1510,8 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q= +github.com/mholt/archives v0.1.0/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -1515,8 +1529,6 @@ github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJ github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= -github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -1562,6 +1574,8 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 h1:MYzLheyVx1tJVDqfu3YnN4jtnyALNzLvwl+f58TcvQY= +github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -1662,11 +1676,10 @@ github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c h1:8 github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c/go.mod h1:kwM/7r/rVluTE8qJbHAffduuqmSv4knVQT2IajGvSiA= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/saferwall/pe v1.5.6 h1:DrRLnoQFxHWJ5lJUmrH7X2L0xeUu6SUS95Dc61eW2Yc= -github.com/saferwall/pe v1.5.6/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= @@ -1687,8 +1700,6 @@ github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd7 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY= github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= -github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d h1:RQqyEogx5J6wPdoxqL132b100j8KjcVHO1c0KLRoIhc= -github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d/go.mod h1:PegD7EVqlN88z7TpCqH92hHP+GBpfomGCCnw1PFtNOA= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 h1:RpforrEYXWkmGwJHIGnLZ3tTWStkjVVstwzNGqxX2Ds= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g= @@ -1738,6 +1749,8 @@ github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnB github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg= +github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb h1:bLo8hvc8XFm9J47r690TUKBzcjSWdJDxmjXJZ+/f92U= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= @@ -1787,10 +1800,10 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD 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.20.2 h1:HGEPzauCHhIosw5o6xmT3jczuKEuaFzSfdjAsH33vYw= -github.com/sylabs/sif/v2 v2.20.2/go.mod h1:WyYryGRaR4Wp21SAymm5pK0p45qzZCSRiZMFvUZiuhc= -github.com/sylabs/squashfs v1.0.5 h1:KExbrvScUVk/drh6gPfnZY7T7SZ+cQpcSJEOK32dppU= -github.com/sylabs/squashfs v1.0.5/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE= +github.com/sylabs/sif/v2 v2.21.1 h1:GZ0b5//AFAqJEChd8wHV/uSKx/l1iuGYwjR8nx+4wPI= +github.com/sylabs/sif/v2 v2.21.1/go.mod h1:YoqEGQnb5x/ItV653bawXHZJOXQaEWpGwHsSD3YePJI= +github.com/sylabs/squashfs v1.0.6 h1:PvJcDzxr+vIm2kH56mEMbaOzvGu79gK7P7IX+R7BDZI= +github.com/sylabs/squashfs v1.0.6/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE= github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo= github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= @@ -1939,6 +1952,8 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= +go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= 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= diff --git a/pkg/leeway/build_checksum_test.go b/pkg/leeway/build_checksum_test.go index 6b04a3aa..19a32a01 100644 --- a/pkg/leeway/build_checksum_test.go +++ b/pkg/leeway/build_checksum_test.go @@ -16,17 +16,17 @@ func TestRecordArtifactChecksum(t *testing.T) { if err != nil { t.Fatal(err) } - + ctx := &buildContext{ InFlightChecksums: true, artifactChecksums: make(map[string]string), } - + err = ctx.recordArtifactChecksum(testArtifact) if err != nil { t.Errorf("recordArtifactChecksum failed: %v", err) } - + if len(ctx.artifactChecksums) != 1 { t.Errorf("Expected 1 checksum, got %d", len(ctx.artifactChecksums)) } @@ -39,30 +39,30 @@ func TestVerifyArtifactChecksum(t *testing.T) { if err != nil { t.Fatal(err) } - + ctx := &buildContext{ InFlightChecksums: true, artifactChecksums: make(map[string]string), } - + // Record initial checksum err = ctx.recordArtifactChecksum(testArtifact) if err != nil { t.Fatal(err) } - + // Verify unmodified file passes err = ctx.verifyArtifactChecksum(testArtifact) if err != nil { t.Errorf("Verification should pass for unmodified file: %v", err) } - + // Modify file to simulate TOCTU attack err = os.WriteFile(testArtifact, []byte("tampered content"), 0644) if err != nil { t.Fatal(err) } - + // Verify modified file fails with TOCTU message err = ctx.verifyArtifactChecksum(testArtifact) if err == nil { @@ -78,13 +78,13 @@ func TestInFlightChecksumsDisabled(t *testing.T) { InFlightChecksums: false, artifactChecksums: nil, } - + // Both operations should be no-op err := ctx.recordArtifactChecksum("nonexistent") if err != nil { t.Errorf("Disabled checksumming should be no-op: %v", err) } - + err = ctx.verifyArtifactChecksum("nonexistent") if err != nil { t.Errorf("Disabled checksumming should be no-op: %v", err) @@ -93,18 +93,18 @@ func TestInFlightChecksumsDisabled(t *testing.T) { func TestVerifyAllArtifactChecksums(t *testing.T) { tmpDir := t.TempDir() - + // Create multiple test artifacts artifacts := []string{ filepath.Join(tmpDir, "pkg1.tar.gz"), filepath.Join(tmpDir, "pkg2.tar.gz"), } - + ctx := &buildContext{ InFlightChecksums: true, artifactChecksums: make(map[string]string), } - + // Record checksums for all artifacts for i, artifact := range artifacts { content := fmt.Sprintf("package %d content", i) @@ -112,25 +112,25 @@ func TestVerifyAllArtifactChecksums(t *testing.T) { if err != nil { t.Fatal(err) } - + err = ctx.recordArtifactChecksum(artifact) if err != nil { t.Fatal(err) } } - + // Verify all pass initially err := verifyAllArtifactChecksums(ctx) if err != nil { t.Errorf("All checksums should verify: %v", err) } - + // Tamper with one artifact err = os.WriteFile(artifacts[0], []byte("tampered!"), 0644) if err != nil { t.Fatal(err) } - + // Verification should fail err = verifyAllArtifactChecksums(ctx) if err == nil { @@ -139,4 +139,4 @@ func TestVerifyAllArtifactChecksums(t *testing.T) { if !strings.Contains(err.Error(), "checksum verification failures") { t.Errorf("Expected verification failure message, got: %v", err) } -} \ No newline at end of file +} diff --git a/pkg/leeway/build_oci_test.go b/pkg/leeway/build_oci_test.go index 9dd1d928..10da8adb 100644 --- a/pkg/leeway/build_oci_test.go +++ b/pkg/leeway/build_oci_test.go @@ -194,7 +194,7 @@ func TestCheckOCILayoutExists(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tmpDir := t.TempDir() - + if err := tt.setup(tmpDir); err != nil { t.Fatalf("setup failed: %v", err) } diff --git a/pkg/leeway/build_test.go b/pkg/leeway/build_test.go index 26304511..7d7885ca 100644 --- a/pkg/leeway/build_test.go +++ b/pkg/leeway/build_test.go @@ -193,15 +193,15 @@ func TestBuildDockerDeps(t *testing.T) { func TestDockerExport_PrecedenceHierarchy(t *testing.T) { tests := []struct { - name string - packageConfig *bool // nil = not set, &true = true, &false = false - workspaceEnvSet bool // Simulates workspace auto-set - userEnvSet bool // Simulates user explicit set - userEnvValue bool - cliSet bool - cliValue bool - expectedFinal bool - expectedSource string + name string + packageConfig *bool // nil = not set, &true = true, &false = false + workspaceEnvSet bool // Simulates workspace auto-set + userEnvSet bool // Simulates user explicit set + userEnvValue bool + cliSet bool + cliValue bool + expectedFinal bool + expectedSource string }{ { name: "No config, no overrides - global default", @@ -232,19 +232,19 @@ func TestDockerExport_PrecedenceHierarchy(t *testing.T) { expectedSource: "package_config", }, { - name: "User env false - overrides package true", - packageConfig: boolPtr(true), - userEnvSet: true, - userEnvValue: false, - expectedFinal: false, + name: "User env false - overrides package true", + packageConfig: boolPtr(true), + userEnvSet: true, + userEnvValue: false, + expectedFinal: false, expectedSource: "user_env_var", }, { - name: "User env true - overrides package false", - packageConfig: boolPtr(false), - userEnvSet: true, - userEnvValue: true, - expectedFinal: true, + name: "User env true - overrides package false", + packageConfig: boolPtr(false), + userEnvSet: true, + userEnvValue: true, + expectedFinal: true, expectedSource: "user_env_var", }, { @@ -469,7 +469,6 @@ func TestBuildDocker_ExportToCache(t *testing.T) { } } - func TestDockerPackage_BuildContextOverride(t *testing.T) { tests := []struct { name string @@ -641,4 +640,3 @@ func TestDockerPostProcessing(t *testing.T) { test.Run() } } - diff --git a/pkg/leeway/cache/remote/s3_provenance_test.go b/pkg/leeway/cache/remote/s3_provenance_test.go index bc34576c..80ba0526 100644 --- a/pkg/leeway/cache/remote/s3_provenance_test.go +++ b/pkg/leeway/cache/remote/s3_provenance_test.go @@ -13,11 +13,11 @@ import ( // TestS3Cache_ProvenanceUpload tests provenance bundle upload functionality func TestS3Cache_ProvenanceUpload(t *testing.T) { tests := []struct { - name string - createProvenanceFile bool - provenanceContent string - expectUpload bool - expectedLogContains string + name string + createProvenanceFile bool + provenanceContent string + expectUpload bool + expectedLogContains string }{ { name: "successful provenance upload", @@ -143,7 +143,7 @@ func TestS3Cache_ProvenanceDownload(t *testing.T) { // Create mock package pkg := &mockPackage{ - + version: "v1.0.0", } @@ -212,7 +212,7 @@ func TestS3Cache_ProvenanceRoundTrip(t *testing.T) { // Create mock package pkg := &mockPackage{ - + version: "v1.0.0", } @@ -281,7 +281,7 @@ func TestS3Cache_ProvenanceAtomicMove(t *testing.T) { tmpDir := t.TempDir() pkg := &mockPackage{ - + version: "v1.0.0", } diff --git a/pkg/leeway/cache/remote/s3_resilience_test.go b/pkg/leeway/cache/remote/s3_resilience_test.go index fba3a314..11364827 100644 --- a/pkg/leeway/cache/remote/s3_resilience_test.go +++ b/pkg/leeway/cache/remote/s3_resilience_test.go @@ -38,26 +38,26 @@ type mockS3WithFailures struct { func (m *mockS3WithFailures) GetObject(ctx context.Context, key string, dest string) (int64, error) { m.mu.Lock() defer m.mu.Unlock() - + m.calls++ - + // Simulate delay if configured if m.callDelay > 0 { time.Sleep(m.callDelay) } - + // Check for context cancellation select { case <-ctx.Done(): return 0, ctx.Err() default: } - + // Simulate failures until threshold if m.calls <= m.failUntilCall { return 0, m.failureType } - + // Return data if available if data, ok := m.data[key]; ok { // Simulate successful download @@ -69,53 +69,53 @@ func (m *mockS3WithFailures) GetObject(ctx context.Context, key string, dest str func (m *mockS3WithFailures) PutObject(ctx context.Context, key string, data []byte) error { m.mu.Lock() defer m.mu.Unlock() - + m.calls++ - + // Simulate delay if configured if m.callDelay > 0 { time.Sleep(m.callDelay) } - + // Check for context cancellation select { case <-ctx.Done(): return ctx.Err() default: } - + // Simulate failures until threshold if m.calls <= m.failUntilCall { return m.failureType } - + // Store data if m.data == nil { m.data = make(map[string][]byte) } m.data[key] = data - + return nil } func (m *mockS3WithFailures) HasObject(ctx context.Context, key string) (bool, error) { m.mu.Lock() defer m.mu.Unlock() - + m.calls++ - + // Check for context cancellation select { case <-ctx.Done(): return false, ctx.Err() default: } - + // Simulate failures until threshold if m.calls <= m.failUntilCall { return false, m.failureType } - + _, exists := m.data[key] return exists, nil } @@ -123,26 +123,26 @@ func (m *mockS3WithFailures) HasObject(ctx context.Context, key string) (bool, e func (m *mockS3WithFailures) UploadObject(ctx context.Context, key string, src string) error { m.mu.Lock() defer m.mu.Unlock() - + m.calls++ - + // Simulate delay if configured if m.callDelay > 0 { time.Sleep(m.callDelay) } - + // Check for context cancellation select { case <-ctx.Done(): return ctx.Err() default: } - + // Simulate failures until threshold if m.calls <= m.failUntilCall { return m.failureType } - + // Read source file and store if data, err := os.ReadFile(src); err == nil { if m.data == nil { @@ -150,21 +150,21 @@ func (m *mockS3WithFailures) UploadObject(ctx context.Context, key string, src s } m.data[key] = data } - + return nil } func (m *mockS3WithFailures) ListObjects(ctx context.Context, prefix string) ([]string, error) { m.mu.Lock() defer m.mu.Unlock() - + var keys []string for key := range m.data { if strings.HasPrefix(key, prefix) { keys = append(keys, key) } } - + return keys, nil } @@ -196,7 +196,7 @@ func createMockS3Cache(storage *mockS3WithFailures, config *cache.RemoteConfig) }, } } - + return &S3Cache{ storage: storage, cfg: config, @@ -233,7 +233,7 @@ func TestS3Cache_NetworkTimeout(t *testing.T) { expectSuccess: true, // Should download without verification }, } - + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Setup mock storage with transient failures @@ -245,7 +245,7 @@ func TestS3Cache_NetworkTimeout(t *testing.T) { "test-package:v1.tar.gz.att": []byte(`{"attestation":"data"}`), }, } - + config := &cache.RemoteConfig{ BucketName: "test-bucket", SLSA: &cache.SLSAConfig{ @@ -253,20 +253,20 @@ func TestS3Cache_NetworkTimeout(t *testing.T) { RequireAttestation: false, }, } - + s3Cache := createMockS3Cache(mockStorage, config) - + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + pkg := &mockPackageResilience{version: "v1"} - + _ = s3Cache.Download(ctx, localCache, []cache.Package{pkg}) - + if tt.expectSuccess { // Should succeed with retry or graceful fallback assert.NoError(t, err, "Should succeed with retry or fallback") @@ -297,7 +297,7 @@ func TestS3Cache_SigstoreOutage(t *testing.T) { expectDownload: false, }, } - + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { config := &cache.RemoteConfig{ @@ -307,28 +307,28 @@ func TestS3Cache_SigstoreOutage(t *testing.T) { RequireAttestation: tt.requireAttestation, }, } - + mockStorage := &mockS3WithFailures{ data: map[string][]byte{ "test-package:v1.tar.gz": []byte("artifact"), "test-package:v1.tar.gz.att": []byte(`{"attestation":"data"}`), }, } - + s3Cache := createMockS3Cache(mockStorage, config) // Note: SLSA verification would be tested separately in SLSA-specific tests - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + pkg := &mockPackageResilience{version: "v1"} - + _ = s3Cache.Download(context.Background(), localCache, []cache.Package{pkg}) - + // Should not fail the build assert.NoError(t, err, "Sigstore outage should not fail builds") - + // Check if download occurred artifactPath, exists := localCache.Location(pkg) if tt.expectDownload { @@ -351,26 +351,26 @@ func TestS3Cache_ContextCancellation(t *testing.T) { "test-package:v1.tar.gz": []byte("artifact data"), }, } - + s3Cache := createMockS3Cache(mockStorage, nil) - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + pkg := &mockPackageResilience{version: "v1"} - + // Create context that will be cancelled ctx, cancel := context.WithCancel(context.Background()) - + // Cancel after a short delay go func() { time.Sleep(50 * time.Millisecond) cancel() }() - + _ = s3Cache.Download(ctx, localCache, []cache.Package{pkg}) - + // Should handle cancellation gracefully // The cache system should not fail builds due to cancellation assert.NoError(t, err, "Context cancellation should not fail builds") @@ -386,24 +386,24 @@ func TestS3Cache_PartialFailure(t *testing.T) { // package2 is missing to simulate failure }, } - + s3Cache := createMockS3Cache(mockStorage, nil) - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + packages := []cache.Package{ &mockPackageResilience{version: "v1", fullName: "package1:v1"}, &mockPackageResilience{version: "v1", fullName: "package2:v1"}, // Will fail &mockPackageResilience{version: "v1", fullName: "package3:v1"}, } - + _ = s3Cache.Download(context.Background(), localCache, packages) - + // Should not fail the entire operation due to partial failures assert.NoError(t, err, "Partial failures should not fail the entire download") - + // Verify successful downloads for _, pkg := range packages { path, exists := localCache.Location(pkg) @@ -421,26 +421,26 @@ func TestS3Cache_RateLimiting(t *testing.T) { "test-package:v1.tar.gz": []byte("artifact"), }, } - + config := &cache.RemoteConfig{ BucketName: "test-bucket", } - + s3Cache := createMockS3Cache(rateLimitedStorage, config) - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + pkg := &mockPackageResilience{version: "v1"} - + start := time.Now() _ = s3Cache.Download(context.Background(), localCache, []cache.Package{pkg}) duration := time.Since(start) - + // Should eventually succeed or gracefully handle rate limiting assert.NoError(t, err, "Should handle rate limiting gracefully") - + t.Logf("Handled rate limiting in %v", duration) } @@ -449,12 +449,12 @@ func TestS3Cache_ConcurrentDownloadsRateLimit(t *testing.T) { // Configure rate limiter simulation with reduced load const maxConcurrent = 3 const packageCount = 5 - + mockStorage := &mockS3WithFailures{ data: make(map[string][]byte), callDelay: 10 * time.Millisecond, // Short delay for testing } - + // Create multiple packages packages := make([]cache.Package, packageCount) for i := 0; i < packageCount; i++ { @@ -463,30 +463,30 @@ func TestS3Cache_ConcurrentDownloadsRateLimit(t *testing.T) { packages[i] = &mockPackageResilience{version: version, fullName: fullName} mockStorage.data[fullName+".tar.gz"] = []byte(fmt.Sprintf("artifact %d", i)) } - + config := &cache.RemoteConfig{ BucketName: "test-bucket", } - + s3Cache := createMockS3Cache(mockStorage, config) - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + // Track concurrent operations (for future implementation) var maxConcurrentOps int32 = maxConcurrent - + // Download all packages with timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - + start := time.Now() _ = s3Cache.Download(ctx, localCache, packages) duration := time.Since(start) - + assert.NoError(t, err, "Should handle concurrent downloads") - + t.Logf("Downloaded %d packages in %v with max %d concurrent operations", packageCount, duration, maxConcurrentOps) } @@ -502,28 +502,28 @@ func TestS3Cache_ExponentialBackoff(t *testing.T) { "test-package:v1.tar.gz": []byte("artifact data"), }, } - + s3Cache := createMockS3Cache(mockStorage, nil) - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + pkg := &mockPackageResilience{version: "v1"} - + // Use timeout to prevent hanging ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - + start := time.Now() _ = s3Cache.Download(ctx, localCache, []cache.Package{pkg}) duration := time.Since(start) - + // Should eventually succeed with exponential backoff assert.NoError(t, err, "Should succeed with exponential backoff") - + // Verify that retries occurred (should take some time due to backoff) - t.Logf("Recovered with exponential backoff in %v after %d calls", + t.Logf("Recovered with exponential backoff in %v after %d calls", duration, mockStorage.calls) } @@ -538,27 +538,27 @@ func TestS3Cache_MaxRetryLimit(t *testing.T) { "test-package:v1.tar.gz": []byte("artifact data"), }, } - + s3Cache := createMockS3Cache(mockStorage, nil) - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + pkg := &mockPackageResilience{version: "v1"} - + // Use a shorter timeout to avoid long test runs ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() - + start := time.Now() _ = s3Cache.Download(ctx, localCache, []cache.Package{pkg}) duration := time.Since(start) - + // Should gracefully handle retry exhaustion assert.NoError(t, err, "Should gracefully handle retry exhaustion") - - t.Logf("Handled retry exhaustion in %v after %d calls", + + t.Logf("Handled retry exhaustion in %v after %d calls", duration, mockStorage.calls) } @@ -590,7 +590,7 @@ func TestS3Cache_MixedFailureTypes(t *testing.T) { expectRetry: false, }, } - + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { retryCount := 2 // Reduced from 3 @@ -602,35 +602,35 @@ func TestS3Cache_MixedFailureTypes(t *testing.T) { "test-package:v1.tar.gz": []byte("artifact data"), }, } - + s3Cache := createMockS3Cache(mockStorage, nil) - + tmpDir := t.TempDir() localCache, err := local.NewFilesystemCache(tmpDir) require.NoError(t, err) - + pkg := &mockPackageResilience{version: "v1"} - + // Use timeout to prevent hanging ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() - + start := time.Now() _ = s3Cache.Download(ctx, localCache, []cache.Package{pkg}) duration := time.Since(start) - + // Should always gracefully handle errors assert.NoError(t, err, "Should gracefully handle %s", tt.name) - + if tt.expectRetry { // Should have made multiple calls for retryable errors - t.Logf("Retryable error %s: %d calls in %v", + t.Logf("Retryable error %s: %d calls in %v", tt.name, mockStorage.calls, duration) } else { // Should have made fewer calls for non-retryable errors - t.Logf("Non-retryable error %s: %d calls in %v", + t.Logf("Non-retryable error %s: %d calls in %v", tt.name, mockStorage.calls, duration) } }) } -} \ No newline at end of file +} diff --git a/pkg/leeway/cache/remote/s3_slsa_test.go b/pkg/leeway/cache/remote/s3_slsa_test.go index 9318179e..a2eccaae 100644 --- a/pkg/leeway/cache/remote/s3_slsa_test.go +++ b/pkg/leeway/cache/remote/s3_slsa_test.go @@ -28,11 +28,11 @@ func (m *mockS3StorageWithSLSA) HasObject(ctx context.Context, key string) (bool m.mu.Lock() m.callLog = append(m.callLog, "HasObject:"+key) m.mu.Unlock() - + if err, exists := m.objectErrors[key]; exists { return false, err } - + _, exists := m.objects[key] return exists, nil } @@ -41,20 +41,20 @@ func (m *mockS3StorageWithSLSA) GetObject(ctx context.Context, key string, dest m.mu.Lock() m.callLog = append(m.callLog, "GetObject:"+key) m.mu.Unlock() - + if err, exists := m.objectErrors[key]; exists { return 0, err } - + data, exists := m.objects[key] if !exists { return 0, &mockNotFoundError{key: key} } - + if err := os.WriteFile(dest, data, 0644); err != nil { return 0, err } - + return int64(len(data)), nil } @@ -123,7 +123,7 @@ func createTestConfig(slsaEnabled bool) *cache.RemoteConfig { RequireAttestation: false, } } - + return &cache.RemoteConfig{ BucketName: "test-bucket", SLSA: slsaConfig, @@ -179,7 +179,7 @@ func TestS3Cache_DownloadWithSLSAVerification(t *testing.T) { packages: []cache.Package{ &mockPackage{version: "v1"}, }, - expectDownload: true, // Should download unverified + expectDownload: true, // Should download unverified expectVerification: false, expectedLogContains: "downloading without verification", }, @@ -222,7 +222,7 @@ func TestS3Cache_DownloadWithSLSAVerification(t *testing.T) { packages: []cache.Package{ &mockPackage{version: "v1"}, }, - expectDownload: true, // Downloads without verification when RequireAttestation=false + expectDownload: true, // Downloads without verification when RequireAttestation=false expectVerification: false, expectedLogContains: "downloading without verification", }, @@ -264,7 +264,7 @@ func TestS3Cache_DownloadWithSLSAVerification(t *testing.T) { if tt.config.SLSA != nil && tt.config.SLSA.Verification && tt.config.SLSA.SourceURI != "" { // Use mock verifier for testing mockVerifier := slsa.NewMockVerifier() - + // Configure mock verifier based on test expectations if tt.expectVerification { if strings.Contains(tt.name, "invalid") { @@ -273,7 +273,7 @@ func TestS3Cache_DownloadWithSLSAVerification(t *testing.T) { mockVerifier.SetVerifyResult(nil) // Success } } - + s3Cache.slsaVerifier = mockVerifier } @@ -308,7 +308,7 @@ func TestS3Cache_DownloadWithSLSAVerification(t *testing.T) { hasAttestationCheck = true } } - + if !hasArtifactCheck { t.Error("Expected artifact existence check when SLSA verification enabled") } @@ -381,17 +381,17 @@ func TestS3Cache_SLSAVerificationPerformance(t *testing.T) { t.Run("performance overhead validation", func(t *testing.T) { // Test both with and without verification to measure overhead // For now, we just ensure the test structure is in place - + baselineTime := measureDownloadTime(t, false) slsaTime := measureDownloadTime(t, true) - + if baselineTime == 0 || slsaTime == 0 { t.Skip("Performance measurement not implemented yet") } - + overhead := float64(slsaTime-baselineTime) / float64(baselineTime) * 100 t.Logf("Baseline: %v, SLSA: %v, Overhead: %.2f%%", baselineTime, slsaTime, overhead) - + // Note: In real implementation, we'd validate < 15% overhead // if overhead > 15.0 { // t.Errorf("SLSA verification overhead %.2f%% exceeds 15%% target", overhead) @@ -408,9 +408,9 @@ func measureDownloadTime(t *testing.T, withSLSA bool) time.Duration { // TestMockVerifierIntegration tests the mock verifier integration func TestMockVerifierIntegration(t *testing.T) { tests := []struct { - name string - setupVerifier func(*slsa.MockVerifier) - expectError bool + name string + setupVerifier func(*slsa.MockVerifier) + expectError bool expectCallCount int }{ { @@ -476,4 +476,4 @@ func TestMockVerifierIntegration(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/pkg/leeway/cache/remote/s3_test.go b/pkg/leeway/cache/remote/s3_test.go index 64f6f7d8..0379a6d9 100644 --- a/pkg/leeway/cache/remote/s3_test.go +++ b/pkg/leeway/cache/remote/s3_test.go @@ -78,12 +78,12 @@ func TestS3Cache_ExistingPackages(t *testing.T) { defer cancel() tests := []struct { - name string - packages []cache.Package - mockHeadObject func(key string) (*s3.HeadObjectOutput, error) - mockListObjects func(prefix string) (*s3.ListObjectsV2Output, error) - expectedResults map[string]struct{} - expectError bool + name string + packages []cache.Package + mockHeadObject func(key string) (*s3.HeadObjectOutput, error) + mockListObjects func(prefix string) (*s3.ListObjectsV2Output, error) + expectedResults map[string]struct{} + expectError bool }{ { name: "finds tar.gz package", diff --git a/pkg/leeway/cache/slsa/mock_verifier.go b/pkg/leeway/cache/slsa/mock_verifier.go index e36a47cb..04b8ea2d 100644 --- a/pkg/leeway/cache/slsa/mock_verifier.go +++ b/pkg/leeway/cache/slsa/mock_verifier.go @@ -10,10 +10,10 @@ import ( type MockVerifier struct { // VerifyFunc allows customizing verification behavior in tests VerifyFunc func(ctx context.Context, artifactPath, attestationPath string) error - + // CallLog tracks verification calls for test assertions CallLog []VerifyCall - + // DefaultResult controls the default verification result when VerifyFunc is nil DefaultResult error } @@ -40,12 +40,12 @@ func (m *MockVerifier) VerifyArtifact(ctx context.Context, artifactPath, attesta AttestationPath: attestationPath, Context: ctx, }) - + // Use custom verification function if provided if m.VerifyFunc != nil { return m.VerifyFunc(ctx, artifactPath, attestationPath) } - + // Return default result return m.DefaultResult } @@ -112,4 +112,4 @@ func SimulateContextCancellation() func(ctx context.Context, artifactPath, attes return nil } } -} \ No newline at end of file +} diff --git a/pkg/leeway/cache/slsa/verifier.go b/pkg/leeway/cache/slsa/verifier.go index e97083bf..c72ea3c2 100644 --- a/pkg/leeway/cache/slsa/verifier.go +++ b/pkg/leeway/cache/slsa/verifier.go @@ -50,12 +50,12 @@ func NewVerifier(sourceURI string, trustedRoots []string) *Verifier { // Sigstore Bundle format and uses embedded transparency log entries for verification. func (v *Verifier) VerifyArtifact(ctx context.Context, artifactPath, attestationPath string) error { startTime := time.Now() - + log.WithFields(log.Fields{ "artifact": artifactPath, "attestation": attestationPath, }).Debug("Starting SLSA verification") - + // Step 1: Load the Sigstore Bundle // This parses the attestation file as a Sigstore Bundle v0.3 format b, err := bundle.LoadJSONFromPath(attestationPath) @@ -177,7 +177,7 @@ func (v *Verifier) VerifyArtifact(ctx context.Context, artifactPath, attestation Reason: fmt.Sprintf("failed to reset artifact file pointer: %v", err), } } - + h := sha256.New() if _, err := io.Copy(h, artifactFile); err != nil { return VerificationFailedError{ @@ -197,7 +197,7 @@ func (v *Verifier) VerifyArtifact(ctx context.Context, artifactPath, attestation // ✅ Certificate chain is valid // ✅ Transparency log entry is valid // ✅ Hash matches - + duration := time.Since(startTime) log.WithFields(log.Fields{ "artifact": artifactPath, @@ -205,7 +205,7 @@ func (v *Verifier) VerifyArtifact(ctx context.Context, artifactPath, attestation "actualHash": actualHash, "verificationMs": duration.Milliseconds(), }).Info("SLSA verification successful") - + return nil } @@ -232,4 +232,4 @@ func (v *Verifier) calculateSHA256(filePath string) (string, error) { // AttestationKey returns the attestation key for an artifact key func AttestationKey(artifactKey string) string { return artifactKey + ".att" -} \ No newline at end of file +} diff --git a/pkg/leeway/cache/slsa/verifier_test.go b/pkg/leeway/cache/slsa/verifier_test.go index 7b64a37a..5dc98397 100644 --- a/pkg/leeway/cache/slsa/verifier_test.go +++ b/pkg/leeway/cache/slsa/verifier_test.go @@ -149,4 +149,4 @@ func TestVerifier_VerifyArtifact_InvalidAttestation(t *testing.T) { // Note: We cannot easily test successful SLSA verification without valid attestations // and artifacts, which would require complex setup. In integration tests, we would -// use mock attestations or test fixtures. \ No newline at end of file +// use mock attestations or test fixtures. diff --git a/pkg/leeway/compression_test.go b/pkg/leeway/compression_test.go index d649b0f3..16988beb 100644 --- a/pkg/leeway/compression_test.go +++ b/pkg/leeway/compression_test.go @@ -224,7 +224,7 @@ func TestIsTestEnvironment(t *testing.T) { if !isTestEnvironment() { t.Error("isTestEnvironment() should return true when running in test binary") } - + // Test with explicit environment variable originalEnv := os.Getenv("LEEWAY_TEST_MODE") defer func() { @@ -234,12 +234,12 @@ func TestIsTestEnvironment(t *testing.T) { os.Unsetenv("LEEWAY_TEST_MODE") } }() - + os.Setenv("LEEWAY_TEST_MODE", "true") if !isTestEnvironment() { t.Error("isTestEnvironment() should return true when LEEWAY_TEST_MODE=true") } - + os.Setenv("LEEWAY_TEST_MODE", "false") // Should still be true because we're in a test binary if !isTestEnvironment() { diff --git a/pkg/leeway/gitinfo.go b/pkg/leeway/gitinfo.go index 2d6c3a63..5d7d12a1 100644 --- a/pkg/leeway/gitinfo.go +++ b/pkg/leeway/gitinfo.go @@ -210,7 +210,7 @@ func GetCommitTimestamp(ctx context.Context, gitInfo *GitInfo) (time.Time, error // Execute git command with context support for cancellation cmd := exec.CommandContext(ctx, "git", "show", "-s", "--format=%ct", gitInfo.Commit) cmd.Dir = gitInfo.WorkingCopyLoc - + output, err := cmd.Output() if err != nil { return time.Time{}, &GitError{ diff --git a/pkg/leeway/provenance.go b/pkg/leeway/provenance.go index 1e16ec50..b8441391 100644 --- a/pkg/leeway/provenance.go +++ b/pkg/leeway/provenance.go @@ -68,7 +68,7 @@ func writeProvenance(p *Package, buildctx *buildContext, builddir string, subjec // Artifact doesn't exist yet - this shouldn't happen as provenance should be written after packaging log.WithField("package", p.FullName()).WithField("path", artifactPath).Warn("Writing provenance before artifact exists") } - + // Ensure we use the .tar.gz extension if strings.HasSuffix(artifactPath, ".tar") && !strings.HasSuffix(artifactPath, ".tar.gz") { artifactPath = artifactPath + ".gz" @@ -79,7 +79,7 @@ func writeProvenance(p *Package, buildctx *buildContext, builddir string, subjec // Write provenance alongside artifact: .provenance.jsonl // This keeps provenance metadata separate from the artifact for determinism provenancePath := artifactPath + ProvenanceBundleFilename - + // Ensure directory exists dir := filepath.Dir(provenancePath) if err := os.MkdirAll(dir, 0755); err != nil { diff --git a/pkg/leeway/provenance_test.go b/pkg/leeway/provenance_test.go index e9e22c36..30760614 100644 --- a/pkg/leeway/provenance_test.go +++ b/pkg/leeway/provenance_test.go @@ -25,18 +25,18 @@ func TestAccessAttestationBundleInCachedArchive(t *testing.T) { setupFunc: func(t *testing.T, dir string) string { artifactPath := filepath.Join(dir, "test.tar.gz") provenancePath := artifactPath + leeway.ProvenanceBundleFilename - + // Create empty artifact if err := os.WriteFile(artifactPath, []byte("fake tar.gz"), 0644); err != nil { t.Fatal(err) } - + // Create provenance file content := `{"test": "provenance"}` if err := os.WriteFile(provenancePath, []byte(content), 0644); err != nil { t.Fatal(err) } - + return artifactPath }, expectError: false, @@ -46,12 +46,12 @@ func TestAccessAttestationBundleInCachedArchive(t *testing.T) { name: "provenance does not exist", setupFunc: func(t *testing.T, dir string) string { artifactPath := filepath.Join(dir, "test.tar.gz") - + // Create only artifact, no provenance if err := os.WriteFile(artifactPath, []byte("fake tar.gz"), 0644); err != nil { t.Fatal(err) } - + return artifactPath }, expectError: true, @@ -277,7 +277,7 @@ func TestProvenancePathExtensionHandling(t *testing.T) { func TestProvenanceDirectoryCreation(t *testing.T) { tmpDir := t.TempDir() - + // Create nested directory structure nestedDir := filepath.Join(tmpDir, "cache", "subdir", "nested") artifactPath := filepath.Join(nestedDir, "test.tar.gz") diff --git a/pkg/leeway/reporter_otel_test.go b/pkg/leeway/reporter_otel_test.go index 922b2b31..3d9631d1 100644 --- a/pkg/leeway/reporter_otel_test.go +++ b/pkg/leeway/reporter_otel_test.go @@ -588,7 +588,7 @@ func TestOTelReporter_BuildError(t *testing.T) { } reporter.BuildStarted(pkg, status) - + // Simulate build error buildErr := fmt.Errorf("build failed: compilation error") reporter.BuildFinished(pkg, buildErr) @@ -762,9 +762,9 @@ func TestOTelReporter_TestCoverageAttributes(t *testing.T) { // Verify test coverage attributes expectedAttrs := map[string]int64{ - "leeway.package.test.coverage_percentage": 85, - "leeway.package.test.functions_with_test": 42, - "leeway.package.test.functions_without_test": 8, + "leeway.package.test.coverage_percentage": 85, + "leeway.package.test.functions_with_test": 42, + "leeway.package.test.functions_without_test": 8, } foundAttrs := make(map[string]int64) diff --git a/pkg/leeway/reporter_test.go b/pkg/leeway/reporter_test.go index f53ad6de..d16a771c 100644 --- a/pkg/leeway/reporter_test.go +++ b/pkg/leeway/reporter_test.go @@ -33,7 +33,7 @@ func TestConsoleReporter(t *testing.T) { Expect Expectation }{ { - Name: "all phases", + Name: "all phases", Func: func(t *testing.T, r *ConsoleReporter) { r.PackageBuildStarted(pkg, "/tmp/build") diff --git a/pkg/leeway/sbom_normalize_test.go b/pkg/leeway/sbom_normalize_test.go index 01ac630b..01030cfd 100644 --- a/pkg/leeway/sbom_normalize_test.go +++ b/pkg/leeway/sbom_normalize_test.go @@ -53,7 +53,7 @@ func TestGenerateDeterministicUUID(t *testing.T) { if len(uuid1) != 36 { t.Errorf("expected UUID length 36, got %d", len(uuid1)) } - + // Verify UUID format matches pattern: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx uuidPattern := `^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$` matched, err := regexp.MatchString(uuidPattern, uuid1) @@ -142,7 +142,7 @@ func TestGetCommitTimestamp_GitCommit(t *testing.T) { Commit: "HEAD", WorkingCopyLoc: wd, } - + timestamp, err := GetCommitTimestamp(ctx, gitInfo) if err != nil { t.Skipf("skipping test: not in a git repository or git not available: %v", err) @@ -178,7 +178,7 @@ func TestGetCommitTimestamp_ContextCancellation(t *testing.T) { Commit: "HEAD", WorkingCopyLoc: wd, } - + _, err := GetCommitTimestamp(ctx, gitInfo) if err == nil { t.Error("expected error with cancelled context, got nil") @@ -576,7 +576,7 @@ func TestNormalizeSPDX_FileErrors(t *testing.T) { // contains is a helper function to check if a string contains a substring func contains(s, substr string) bool { - return len(s) >= len(substr) && (s == substr || len(substr) == 0 || + return len(s) >= len(substr) && (s == substr || len(substr) == 0 || (len(s) > 0 && len(substr) > 0 && findSubstring(s, substr))) } diff --git a/pkg/leeway/signing/attestation.go b/pkg/leeway/signing/attestation.go index 6bee384e..2a832d04 100644 --- a/pkg/leeway/signing/attestation.go +++ b/pkg/leeway/signing/attestation.go @@ -91,7 +91,7 @@ func GenerateSignedSLSAAttestation(ctx context.Context, artifactPath string, git } sourceURI := fmt.Sprintf("%s/%s", githubCtx.ServerURL, githubCtx.Repository) - + // Extract builder ID from OIDC token to match certificate identity // This is critical for compatibility with reusable workflows builderID, err := extractBuilderIDFromOIDC(ctx, githubCtx) @@ -428,7 +428,7 @@ func extractBuilderIDFromOIDC(ctx context.Context, githubCtx *GitHubContext) (st } // Try to extract job_workflow_ref from the sub claim first - // + // // Context: // When we call sign.Bundle() with the OIDC token, the Sigstore library sends it to Fulcio (Sigstore's CA). // Fulcio extracts claims from the OIDC token and issues a short-lived certificate with the builder identity in the Subject Alternative Name (SAN). @@ -438,7 +438,7 @@ func extractBuilderIDFromOIDC(ctx context.Context, githubCtx *GitHubContext) (st // GitHub docs show it as top-level, but we need to confirm what Fulcio actually uses. The current // implementation tries both approaches to ensure we match Fulcio's extraction logic. jobWorkflowRef := extractJobWorkflowRef(sub) - + // If not found in sub, try the top-level job_workflow_ref claim if jobWorkflowRef == "" { if jwfRef, ok := claims["job_workflow_ref"].(string); ok && jwfRef != "" { @@ -446,14 +446,14 @@ func extractBuilderIDFromOIDC(ctx context.Context, githubCtx *GitHubContext) (st log.WithField("job_workflow_ref", jobWorkflowRef).Debug("Using top-level job_workflow_ref claim (not found in sub)") } } - + if jobWorkflowRef == "" { return "", fmt.Errorf("job_workflow_ref not found in sub claim or top-level claims: %s", sub) } // Construct the builder ID URL builderID := fmt.Sprintf("%s/%s", githubCtx.ServerURL, jobWorkflowRef) - + log.WithFields(log.Fields{ "sub_claim": sub, "job_workflow_ref": jobWorkflowRef, @@ -472,7 +472,7 @@ func extractBuilderIDFromOIDC(ctx context.Context, githubCtx *GitHubContext) (st func extractJobWorkflowRef(sub string) string { // Split by colon to parse the structured claim parts := strings.Split(sub, ":") - + // Find the job_workflow_ref field for i, part := range parts { if part == "job_workflow_ref" && i+1 < len(parts) { @@ -481,7 +481,7 @@ func extractJobWorkflowRef(sub string) string { return strings.Join(parts[i+1:], ":") } } - + // If no job_workflow_ref found, return empty string return "" } diff --git a/pkg/leeway/signing/attestation_test.go b/pkg/leeway/signing/attestation_test.go index 9c914f5f..b656dddf 100644 --- a/pkg/leeway/signing/attestation_test.go +++ b/pkg/leeway/signing/attestation_test.go @@ -1115,7 +1115,7 @@ func TestFetchGitHubOIDCToken(t *testing.T) { if tt.setupEnv != nil { tt.setupEnv(t) } - + var server *httptest.Server if tt.mockServer != nil { server = tt.mockServer(t) @@ -1245,7 +1245,7 @@ func TestExtractBuilderIDFromOIDC(t *testing.T) { }`) signature := base64EncodeForTest("fake-signature") token := fmt.Sprintf("%s.%s.%s", header, payload, signature) - + w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]string{"value": token}); err != nil { t.Errorf("Failed to encode response: %v", err) @@ -1275,7 +1275,7 @@ func TestExtractBuilderIDFromOIDC(t *testing.T) { }`) signature := base64EncodeForTest("fake-signature") token := fmt.Sprintf("%s.%s.%s", header, payload, signature) - + w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]string{"value": token}); err != nil { t.Errorf("Failed to encode response: %v", err) @@ -1324,7 +1324,7 @@ func TestExtractBuilderIDFromOIDC(t *testing.T) { payload := base64EncodeForTest(`{"aud": "sigstore"}`) signature := base64EncodeForTest("fake-signature") token := fmt.Sprintf("%s.%s.%s", header, payload, signature) - + w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]string{"value": token}); err != nil { t.Errorf("Failed to encode response: %v", err) @@ -1350,7 +1350,7 @@ func TestExtractBuilderIDFromOIDC(t *testing.T) { payload := base64EncodeForTest(`{"sub": " ", "aud": "sigstore"}`) signature := base64EncodeForTest("fake-signature") token := fmt.Sprintf("%s.%s.%s", header, payload, signature) - + w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]string{"value": token}); err != nil { t.Errorf("Failed to encode response: %v", err) @@ -1380,7 +1380,7 @@ func TestExtractBuilderIDFromOIDC(t *testing.T) { }`) signature := base64EncodeForTest("fake-signature") token := fmt.Sprintf("%s.%s.%s", header, payload, signature) - + w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]string{"value": token}); err != nil { t.Errorf("Failed to encode response: %v", err) @@ -1409,7 +1409,7 @@ func TestExtractBuilderIDFromOIDC(t *testing.T) { }`) signature := base64EncodeForTest("fake-signature") token := fmt.Sprintf("%s.%s.%s", header, payload, signature) - + w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]string{"value": token}); err != nil { t.Errorf("Failed to encode response: %v", err) @@ -1517,7 +1517,7 @@ func TestBuilderIDMatchesCertificateIdentity(t *testing.T) { }`, tt.oidcSubClaim)) signature := base64EncodeForTest("fake-signature") token := fmt.Sprintf("%s.%s.%s", header, payload, signature) - + w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]string{"value": token}); err != nil { t.Errorf("Failed to encode response: %v", err) diff --git a/pkg/leeway/signing/upload_test.go b/pkg/leeway/signing/upload_test.go index f6efd247..bfbe0729 100644 --- a/pkg/leeway/signing/upload_test.go +++ b/pkg/leeway/signing/upload_test.go @@ -415,7 +415,7 @@ func TestArtifactUploader_SkipsExistingArtifacts(t *testing.T) { // CRITICAL: Verify that the attestation was NOT uploaded when both exist assert.Equal(t, 0, mockCache.uploadCalls["existing.tar.gz.att"], "Attestation should NOT be uploaded when both artifact and attestation exist") - + // Verify existing attestation is preserved assert.Equal(t, existingAttestation, mockCache.uploadedFiles["existing.tar.gz.att"], "Existing attestation should be preserved") } @@ -486,7 +486,7 @@ func TestArtifactUploader_UploadsNewArtifacts(t *testing.T) { // where an artifact is downloaded from remote cache and then signed func TestArtifactUploader_SimulatesDownloadedArtifactWorkflow(t *testing.T) { tmpDir := t.TempDir() - + // Simulate the workflow: // 1. Build job uploads artifact to S3 // 2. Sign job downloads artifact from S3 @@ -520,19 +520,19 @@ func TestArtifactUploader_SimulatesDownloadedArtifactWorkflow(t *testing.T) { // CRITICAL: Artifact should NOT be re-uploaded // uploadCalls should still be 1 (from build job), not 2 - assert.Equal(t, 1, mockCache.uploadCalls["downloaded.tar.gz"], + assert.Equal(t, 1, mockCache.uploadCalls["downloaded.tar.gz"], "Downloaded artifact should NOT be re-uploaded by sign job") // CRITICAL: Attestation should NOT be uploaded when both exist - assert.Equal(t, 1, mockCache.uploadCalls["downloaded.tar.gz.att"], + assert.Equal(t, 1, mockCache.uploadCalls["downloaded.tar.gz.att"], "Attestation should NOT be uploaded when both artifact and attestation exist") - + // Verify existing attestation is preserved - assert.Equal(t, buildAttestation, mockCache.uploadedFiles["downloaded.tar.gz.att"], + assert.Equal(t, buildAttestation, mockCache.uploadedFiles["downloaded.tar.gz.att"], "Existing attestation should be preserved") // Artifact content should remain unchanged (not overwritten) - assert.Equal(t, buildArtifactContent, mockCache.uploadedFiles["downloaded.tar.gz"], + assert.Equal(t, buildArtifactContent, mockCache.uploadedFiles["downloaded.tar.gz"], "Artifact content should remain unchanged from build job") } @@ -540,7 +540,7 @@ func TestArtifactUploader_SimulatesDownloadedArtifactWorkflow(t *testing.T) { // where an artifact is built locally and needs to be uploaded func TestArtifactUploader_SimulatesLocallyBuiltArtifactWorkflow(t *testing.T) { tmpDir := t.TempDir() - + // Simulate the workflow: // 1. Build job builds artifact locally (not in remote cache) // 2. Sign job creates attestation @@ -565,14 +565,14 @@ func TestArtifactUploader_SimulatesLocallyBuiltArtifactWorkflow(t *testing.T) { require.NoError(t, err) // VERIFICATION: Both artifact and attestation should be uploaded - assert.Equal(t, 1, mockCache.uploadCalls["local.tar.gz"], + assert.Equal(t, 1, mockCache.uploadCalls["local.tar.gz"], "Locally built artifact should be uploaded") - assert.Equal(t, localArtifactContent, mockCache.uploadedFiles["local.tar.gz"], + assert.Equal(t, localArtifactContent, mockCache.uploadedFiles["local.tar.gz"], "Artifact content should match") - assert.Equal(t, 1, mockCache.uploadCalls["local.tar.gz.att"], + assert.Equal(t, 1, mockCache.uploadCalls["local.tar.gz.att"], "Attestation should be uploaded") - assert.Equal(t, attestation, mockCache.uploadedFiles["local.tar.gz.att"], + assert.Equal(t, attestation, mockCache.uploadedFiles["local.tar.gz.att"], "Attestation content should match") } @@ -580,7 +580,7 @@ func TestArtifactUploader_SimulatesLocallyBuiltArtifactWorkflow(t *testing.T) { // where multiple workflows building the same artifact would overwrite each other's attestations func TestArtifactUploader_PreventsAttestationOverwrite(t *testing.T) { tmpDir := t.TempDir() - + // Simulate the race condition scenario: // 1. Workflow A builds artifact (checksum A), uploads to S3, signs it, uploads attestation A // 2. Workflow B builds artifact (checksum B), finds artifact exists, signs LOCAL artifact B @@ -595,7 +595,7 @@ func TestArtifactUploader_PreventsAttestationOverwrite(t *testing.T) { // Step 1: Workflow A uploads artifact and attestation artifactAContent := []byte("artifact with checksum A") attestationA := []byte(`{"checksum":"A","workflow":"build-main"}`) - + mockCache.uploadedFiles["shared-artifact.tar.gz"] = artifactAContent mockCache.uploadedFiles["shared-artifact.tar.gz.att"] = attestationA mockCache.uploadCalls["shared-artifact.tar.gz"] = 1 @@ -616,19 +616,19 @@ func TestArtifactUploader_PreventsAttestationOverwrite(t *testing.T) { // Step 4: Verify the fix // Artifact upload count should still be 1 (only workflow A uploaded) - assert.Equal(t, 1, mockCache.uploadCalls["shared-artifact.tar.gz"], + assert.Equal(t, 1, mockCache.uploadCalls["shared-artifact.tar.gz"], "Artifact should not be re-uploaded by workflow B") - + // CRITICAL: Attestation upload count should still be 1 (only workflow A uploaded) - assert.Equal(t, 1, mockCache.uploadCalls["shared-artifact.tar.gz.att"], + assert.Equal(t, 1, mockCache.uploadCalls["shared-artifact.tar.gz.att"], "Attestation should not be overwritten by workflow B") - + // Verify original attestation A is preserved (not overwritten by attestation B) - assert.Equal(t, attestationA, mockCache.uploadedFiles["shared-artifact.tar.gz.att"], + assert.Equal(t, attestationA, mockCache.uploadedFiles["shared-artifact.tar.gz.att"], "Original attestation A should be preserved, not overwritten by attestation B") - + // Verify original artifact A is preserved - assert.Equal(t, artifactAContent, mockCache.uploadedFiles["shared-artifact.tar.gz"], + assert.Equal(t, artifactAContent, mockCache.uploadedFiles["shared-artifact.tar.gz"], "Original artifact A should be preserved") } @@ -657,9 +657,9 @@ func TestArtifactUploader_HasFileError(t *testing.T) { require.NoError(t, err) // Verify both were uploaded (fallback behavior assumes artifact doesn't exist) - assert.Equal(t, 1, mockCache.uploadCalls["test.tar.gz"], + assert.Equal(t, 1, mockCache.uploadCalls["test.tar.gz"], "Artifact should be uploaded when HasFile fails") - assert.Equal(t, 1, mockCache.uploadCalls["test.tar.gz.att"], + assert.Equal(t, 1, mockCache.uploadCalls["test.tar.gz.att"], "Attestation should be uploaded when HasFile fails") } @@ -705,11 +705,11 @@ func TestArtifactUploader_AttestationCheckError(t *testing.T) { require.NoError(t, err) // Artifact should not be re-uploaded - assert.Equal(t, 0, mockCache.uploadCalls["test.tar.gz"], + assert.Equal(t, 0, mockCache.uploadCalls["test.tar.gz"], "Artifact should not be uploaded when it exists") - + // Attestation should be uploaded (fallback behavior assumes it doesn't exist) - assert.Equal(t, 1, mockCache.uploadCalls["test.tar.gz.att"], + assert.Equal(t, 1, mockCache.uploadCalls["test.tar.gz.att"], "Attestation should be uploaded when HasFile check fails") } diff --git a/pkg/leeway/workspace_test.go b/pkg/leeway/workspace_test.go index 7b6c8624..8ac2b35e 100644 --- a/pkg/leeway/workspace_test.go +++ b/pkg/leeway/workspace_test.go @@ -329,10 +329,10 @@ func TestWorkspace_ApplySLSADefaults(t *testing.T) { gitOrigin: "github.com/gitpod-io/leeway", existingEnvVars: map[string]string{}, expectedEnvVars: map[string]string{ - leeway.EnvvarSLSACacheVerification: "true", + leeway.EnvvarSLSACacheVerification: "true", leeway.EnvvarEnableInFlightChecksums: "true", leeway.EnvvarDockerExportToCache: "true", - leeway.EnvvarSLSASourceURI: "github.com/gitpod-io/leeway", + leeway.EnvvarSLSASourceURI: "github.com/gitpod-io/leeway", }, }, { @@ -341,10 +341,10 @@ func TestWorkspace_ApplySLSADefaults(t *testing.T) { provenanceSLSA: false, existingEnvVars: map[string]string{}, expectedEnvVars: map[string]string{ - leeway.EnvvarSLSACacheVerification: "", + leeway.EnvvarSLSACacheVerification: "", leeway.EnvvarEnableInFlightChecksums: "", leeway.EnvvarDockerExportToCache: "", - leeway.EnvvarSLSASourceURI: "", + leeway.EnvvarSLSASourceURI: "", }, }, { @@ -353,10 +353,10 @@ func TestWorkspace_ApplySLSADefaults(t *testing.T) { provenanceSLSA: true, existingEnvVars: map[string]string{}, expectedEnvVars: map[string]string{ - leeway.EnvvarSLSACacheVerification: "", + leeway.EnvvarSLSACacheVerification: "", leeway.EnvvarEnableInFlightChecksums: "", leeway.EnvvarDockerExportToCache: "", - leeway.EnvvarSLSASourceURI: "", + leeway.EnvvarSLSASourceURI: "", }, }, { @@ -365,14 +365,14 @@ func TestWorkspace_ApplySLSADefaults(t *testing.T) { provenanceSLSA: true, gitOrigin: "github.com/gitpod-io/leeway", existingEnvVars: map[string]string{ - leeway.EnvvarSLSACacheVerification: "false", + leeway.EnvvarSLSACacheVerification: "false", leeway.EnvvarEnableInFlightChecksums: "false", }, expectedEnvVars: map[string]string{ - leeway.EnvvarSLSACacheVerification: "false", // Not overridden + leeway.EnvvarSLSACacheVerification: "false", // Not overridden leeway.EnvvarEnableInFlightChecksums: "false", // Not overridden leeway.EnvvarDockerExportToCache: "true", // Set (wasn't present) - leeway.EnvvarSLSASourceURI: "github.com/gitpod-io/leeway", + leeway.EnvvarSLSASourceURI: "github.com/gitpod-io/leeway", }, }, { @@ -382,10 +382,10 @@ func TestWorkspace_ApplySLSADefaults(t *testing.T) { gitOrigin: "", existingEnvVars: map[string]string{}, expectedEnvVars: map[string]string{ - leeway.EnvvarSLSACacheVerification: "true", + leeway.EnvvarSLSACacheVerification: "true", leeway.EnvvarEnableInFlightChecksums: "true", leeway.EnvvarDockerExportToCache: "true", - leeway.EnvvarSLSASourceURI: "", // Not set without Git origin + leeway.EnvvarSLSASourceURI: "", // Not set without Git origin }, }, }