diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3e2175ed..2915ec62 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -21,7 +21,7 @@ jobs: persist-credentials: false - name: Set up Go - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' check-latest: true diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index f9c291e2..58fa0852 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Set up Go - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' check-latest: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 119ec8ca..1a5d443c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,29 +23,29 @@ jobs: with: fetch-depth: 0 # this is important, otherwise it won't checkout the full tree (i.e. no previous tags) - - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' check-latest: true cache: 'false' - - uses: imjasonh/setup-crane@31b88efe9de28ae0ffa220711af4b60be9435f6e # v0.4 + - uses: imjasonh/setup-crane@6da1ae018866400525525ce74ff892880c099987 # v0.5 - - uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 + - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - - uses: anchore/sbom-action/download-syft@0b82b0b1a22399a1c542d4d656f70cd903571b5c # v0.21.1 + - uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 - name: Set env run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> "$GITHUB_ENV" - name: Login to GitHub Containers - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 + - uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1 with: version: latest args: release --clean @@ -67,7 +67,7 @@ jobs: COSIGN_YES: true - name: Generate build provenance attestation - uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3.1.0 + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: ghcr.io/sigstore/gitsign subject-digest: ${{ steps.digest.outputs.digest }} diff --git a/.github/workflows/validate-release.yml b/.github/workflows/validate-release.yml index 37e6aa17..68cc93b7 100644 --- a/.github/workflows/validate-release.yml +++ b/.github/workflows/validate-release.yml @@ -6,21 +6,27 @@ on: - "main" workflow_dispatch: +permissions: {} + jobs: validate-release: runs-on: ubuntu-latest + + permissions: + contents: read + steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' check-latest: true cache: 'false' - - uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} @@ -43,9 +49,9 @@ jobs: echo "Disk space after cleanup:" df -h - - uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 - - uses: anchore/sbom-action/download-syft@0b82b0b1a22399a1c542d4d656f70cd903571b5c # v0.21.1 - - uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 + - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 + - uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 + - uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1 with: version: latest args: release --clean --snapshot --skip=sign diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 9fe9c4ec..e5014be4 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -20,7 +20,7 @@ jobs: with: persist-credentials: false - - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' check-latest: true @@ -51,7 +51,7 @@ jobs: with: persist-credentials: false - - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' check-latest: true @@ -72,7 +72,7 @@ jobs: with: persist-credentials: false - - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' check-latest: true diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 5dc6379b..6634c3d7 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -104,12 +104,10 @@ signs: - cmd: cosign env: - COSIGN_YES=true - certificate: '${artifact}.pem' - signature: '${artifact}.sig' + signature: '${artifact}.bundle' args: - sign-blob - - '--output-certificate=${certificate}' - - '--output-signature=${signature}' + - '--bundle=${signature}' - '${artifact}' artifacts: binary output: true diff --git a/cmd/gitsign-credential-cache/README.md b/cmd/gitsign-credential-cache/README.md index eb47132c..baace744 100644 --- a/cmd/gitsign-credential-cache/README.md +++ b/cmd/gitsign-credential-cache/README.md @@ -114,10 +114,11 @@ also updated to match). ## Running it as a launchctl service on macOS -If you are a macOS user, you can run `gitsign-credential-cache` as a launchctl service by running the following commands in your terminal: +If you are a macOS user, you can run `gitsign-credential-cache` as a launchctl service using the following instructions: + +Create the file `/tmp/gitsign-credential-cache.sh` with the following contents: ```sh -cat < /tmp/gitsign-credential-cache.sh #!/bin/bash set -euo pipefail @@ -204,7 +205,11 @@ if ! grep -qF -- "${export_line}" "${shell_config_file}"; then else echo "GITSIGN_CREDENTIAL_CACHE already exists in ${shell_config_file}!" fi -EOF +``` + +Then run the following commands in your terminal: + +```sh chmod +x /tmp/gitsign-credential-cache.sh echo "Running the script to create the launchctl service..." /tmp/gitsign-credential-cache.sh @@ -221,4 +226,4 @@ and of course if you would like to tail the logs of your service you can do so b ```sh tail -f /opt/homebrew/var/log/gitsign-credential-cache.log -``` \ No newline at end of file +``` diff --git a/go.mod b/go.mod index 78747f84..b276f9cd 100644 --- a/go.mod +++ b/go.mod @@ -1,49 +1,50 @@ module github.com/sigstore/gitsign -go 1.25.5 +go 1.26 require ( - github.com/coreos/go-oidc/v3 v3.17.0 - github.com/coreos/go-systemd/v22 v22.6.0 + github.com/coreos/go-oidc/v3 v3.18.0 + github.com/coreos/go-systemd/v22 v22.7.0 github.com/github/smimesign v0.2.0 - github.com/go-git/go-billy/v5 v5.7.0 - github.com/go-git/go-git/v5 v5.16.4 - github.com/go-openapi/runtime v0.29.2 - github.com/go-openapi/strfmt v0.25.0 - github.com/go-openapi/swag/conv v0.25.4 + github.com/go-git/go-billy/v5 v5.9.0 + github.com/go-git/go-git/v5 v5.19.0 + github.com/go-openapi/runtime v0.29.4 + github.com/go-openapi/strfmt v0.26.2 + github.com/go-openapi/swag/conv v0.26.0 github.com/google/go-cmp v0.7.0 - github.com/in-toto/attestation v1.1.2 + github.com/in-toto/attestation v1.2.0 github.com/jonboulle/clockwork v0.5.0 - github.com/mattn/go-tty v0.0.7 + github.com/mattn/go-tty v0.0.8 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/secure-systems-lab/go-securesystemslib v0.10.0 - github.com/sigstore/cosign/v3 v3.0.4 + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c + github.com/secure-systems-lab/go-securesystemslib v0.11.0 + github.com/sigstore/cosign/v3 v3.0.6 github.com/sigstore/fulcio v1.8.5 - github.com/sigstore/protobuf-specs v0.5.0 - github.com/sigstore/rekor v1.5.0 - github.com/sigstore/sigstore v1.10.4 + github.com/sigstore/protobuf-specs v0.5.1 + github.com/sigstore/rekor v1.5.1 + github.com/sigstore/sigstore v1.10.5 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 - golang.org/x/crypto v0.47.0 - golang.org/x/oauth2 v0.34.0 + golang.org/x/crypto v0.51.0 + golang.org/x/oauth2 v0.36.0 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da google.golang.org/protobuf v1.36.11 ) require ( cloud.google.com/go v0.123.0 // indirect - cloud.google.com/go/auth v0.18.0 // indirect + cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.3 // indirect - cloud.google.com/go/kms v1.23.2 // indirect - cloud.google.com/go/longrunning v0.7.0 // indirect + cloud.google.com/go/kms v1.26.0 // indirect + cloud.google.com/go/longrunning v0.8.0 // indirect dario.cat/mergo v1.0.2 // indirect - filippo.io/edwards25519 v1.1.0 // indirect + filippo.io/edwards25519 v1.2.0 // indirect github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.20.0 // indirect github.com/AliyunContainerService/ack-ram-tool/pkg/ecsmetadata v0.0.10 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 // indirect @@ -59,7 +60,7 @@ require ( github.com/Azure/go-autorest/tracing v0.6.1 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.3.0 // indirect + github.com/ProtonMail/go-crypto v1.4.1 // indirect github.com/ThalesIgnite/crypto11 v1.2.5 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect @@ -75,26 +76,26 @@ require ( github.com/aliyun/credentials-go v1.4.10 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go v1.55.8 // indirect - github.com/aws/aws-sdk-go-v2 v1.41.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.6 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.6 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.55.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.8 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.49.4 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect - github.com/aws/smithy-go v1.24.0 // indirect - github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect + github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 // indirect github.com/blang/semver v3.5.1+incompatible // indirect - github.com/buildkite/agent/v3 v3.115.2 // indirect + github.com/buildkite/agent/v3 v3.118.0 // indirect github.com/buildkite/go-pipeline v0.16.0 // indirect github.com/buildkite/interpolate v0.1.5 // indirect github.com/buildkite/roko v1.4.0 // indirect @@ -103,10 +104,9 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/cloudflare/circl v1.6.2 // indirect - github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect + github.com/cloudflare/circl v1.6.3 // indirect github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect - github.com/containerd/stargz-snapshotter/estargz v0.18.1 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect @@ -114,58 +114,56 @@ require ( github.com/digitorus/pkcs7 v0.0.0-20250730155240-ffadbf3f398c // indirect github.com/digitorus/timestamp v0.0.0-20250524132541-c45532741eea // indirect github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/docker/cli v29.1.3+incompatible // indirect + github.com/docker/cli v29.3.0+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker-credential-helpers v0.9.4 // indirect + github.com/docker/docker-credential-helpers v0.9.5 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/go-chi/chi/v5 v5.2.4 // indirect + github.com/go-chi/chi/v5 v5.2.5 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.24.2 // indirect - github.com/go-openapi/errors v0.22.6 // indirect - github.com/go-openapi/jsonpointer v0.22.4 // indirect - github.com/go-openapi/jsonreference v0.21.4 // indirect - github.com/go-openapi/loads v0.23.2 // indirect - github.com/go-openapi/spec v0.22.3 // indirect - github.com/go-openapi/swag v0.25.4 // indirect - github.com/go-openapi/swag/cmdutils v0.25.4 // indirect - github.com/go-openapi/swag/fileutils v0.25.4 // indirect - github.com/go-openapi/swag/jsonname v0.25.4 // indirect - github.com/go-openapi/swag/jsonutils v0.25.4 // indirect - github.com/go-openapi/swag/loading v0.25.4 // indirect - github.com/go-openapi/swag/mangling v0.25.4 // indirect - github.com/go-openapi/swag/netutils v0.25.4 // indirect - github.com/go-openapi/swag/stringutils v0.25.4 // indirect - github.com/go-openapi/swag/typeutils v0.25.4 // indirect - github.com/go-openapi/swag/yamlutils v0.25.4 // indirect - github.com/go-openapi/validate v0.25.1 // indirect - github.com/go-piv/piv-go/v2 v2.4.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/go-openapi/analysis v0.25.0 // indirect + github.com/go-openapi/errors v0.22.7 // indirect + github.com/go-openapi/jsonpointer v0.22.5 // indirect + github.com/go-openapi/jsonreference v0.21.5 // indirect + github.com/go-openapi/loads v0.23.3 // indirect + github.com/go-openapi/spec v0.22.4 // indirect + github.com/go-openapi/swag v0.25.5 // indirect + github.com/go-openapi/swag/cmdutils v0.25.5 // indirect + github.com/go-openapi/swag/fileutils v0.26.0 // indirect + github.com/go-openapi/swag/jsonname v0.25.5 // indirect + github.com/go-openapi/swag/jsonutils v0.26.0 // indirect + github.com/go-openapi/swag/loading v0.25.5 // indirect + github.com/go-openapi/swag/mangling v0.25.5 // indirect + github.com/go-openapi/swag/netutils v0.25.5 // indirect + github.com/go-openapi/swag/stringutils v0.26.0 // indirect + github.com/go-openapi/swag/typeutils v0.26.0 // indirect + github.com/go-openapi/swag/yamlutils v0.25.5 // indirect + github.com/go-openapi/validate v0.25.2 // indirect + github.com/go-piv/piv-go/v2 v2.5.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/snappy v1.0.0 // indirect - github.com/google/certificate-transparency-go v1.3.2 // indirect + github.com/google/certificate-transparency-go v1.3.3 // indirect github.com/google/gnostic-models v0.7.1 // indirect - github.com/google/go-containerregistry v0.20.7 // indirect + github.com/google/go-containerregistry v0.21.3 // indirect github.com/google/go-github/v73 v73.0.0 // indirect github.com/google/go-querystring v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/trillian v1.7.2 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.9 // indirect - github.com/googleapis/gax-go/v2 v2.16.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.19.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -183,13 +181,13 @@ require ( github.com/jellydator/ttlcache/v3 v3.4.0 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kevinburke/ssh_config v1.4.0 // indirect - github.com/klauspost/compress v1.18.2 // indirect + github.com/kevinburke/ssh_config v1.6.0 // indirect + github.com/klauspost/compress v1.18.4 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/letsencrypt/boulder v0.20251216.0 // indirect + github.com/letsencrypt/boulder v0.20260223.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/miekg/pkcs11 v1.1.1 // indirect + github.com/miekg/pkcs11 v1.1.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/moby/term v0.5.2 // indirect @@ -199,14 +197,13 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/natefinch/atomic v1.0.1 // indirect github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect - github.com/oklog/ulid v1.3.1 // indirect + github.com/oklog/ulid/v2 v2.1.1 // indirect github.com/oleiade/reflections v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pjbgf/sha1cd v0.5.0 // indirect - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect + github.com/pjbgf/sha1cd v0.6.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect @@ -214,13 +211,13 @@ require ( github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect - github.com/sigstore/rekor-tiles/v2 v2.0.1 // indirect + github.com/sigstore/rekor-tiles/v2 v2.2.1 // indirect github.com/sigstore/sigstore-go v1.1.4 // indirect - github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.3 // indirect - github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.3 // indirect - github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.3 // indirect - github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.3 // indirect - github.com/sigstore/timestamp-authority/v2 v2.0.4 // indirect + github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.5 // indirect + github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.5 // indirect + github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.5 // indirect + github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.5 // indirect + github.com/sigstore/timestamp-authority/v2 v2.0.6 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/skeema/knownhosts v1.3.2 // indirect github.com/spf13/afero v1.15.0 // indirect @@ -242,46 +239,44 @@ require ( github.com/vbatts/tar-split v0.12.2 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - gitlab.com/gitlab-org/api/client-go v1.11.0 // indirect - go.mongodb.org/mongo-driver v1.17.6 // indirect + gitlab.com/gitlab-org/api/client-go v1.46.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect - go.step.sm/crypto v0.75.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.step.sm/crypto v0.77.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect - golang.org/x/mod v0.31.0 // indirect - golang.org/x/net v0.48.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/term v0.39.0 // indirect - golang.org/x/text v0.33.0 // indirect - golang.org/x/time v0.14.0 // indirect - google.golang.org/api v0.260.0 // indirect - google.golang.org/genproto v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/grpc v1.78.0 // indirect + golang.org/x/mod v0.35.0 // indirect + golang.org/x/net v0.54.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.44.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/api v0.272.0 // indirect + google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 // indirect + google.golang.org/grpc v1.80.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.35.0 // indirect - k8s.io/apimachinery v0.35.0 // indirect - k8s.io/client-go v0.35.0 // indirect + k8s.io/api v0.35.3 // indirect + k8s.io/apimachinery v0.35.3 // indirect + k8s.io/client-go v0.35.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/release-utils v0.12.3 // indirect + sigs.k8s.io/release-utils v0.12.4 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 484bd7ca..6843ec59 100644 --- a/go.sum +++ b/go.sum @@ -3,22 +3,22 @@ al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWt cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= -cloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0= -cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= -cloud.google.com/go/kms v1.23.2 h1:4IYDQL5hG4L+HzJBhzejUySoUOheh3Lk5YT4PCyyW6k= -cloud.google.com/go/kms v1.23.2/go.mod h1:rZ5kK0I7Kn9W4erhYVoIRPtpizjunlrfU4fUkumUp8g= -cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E= -cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY= +cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU= +cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM= github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.20.0 h1:LU830/Tuj5c6xSpEjyrymfY5fGInchwMWRp1aSBXbS8= @@ -27,8 +27,8 @@ github.com/AliyunContainerService/ack-ram-tool/pkg/ecsmetadata v0.0.10 h1:03PGMq github.com/AliyunContainerService/ack-ram-tool/pkg/ecsmetadata v0.0.10/go.mod h1:QM3VKYNyD5thMEWqKef+uOfpNmZG7RjG7wOsCdavj9w= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= @@ -78,8 +78,8 @@ github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lpr github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= +github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.2/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= @@ -141,49 +141,49 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= -github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= -github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= -github.com/aws/aws-sdk-go-v2/config v1.32.6 h1:hFLBGUKjmLAekvi1evLi5hVvFQtSo3GYwi+Bx4lpJf8= -github.com/aws/aws-sdk-go-v2/config v1.32.6/go.mod h1:lcUL/gcd8WyjCrMnxez5OXkO3/rwcNmvfno62tnXNcI= -github.com/aws/aws-sdk-go-v2/credentials v1.19.6 h1:F9vWao2TwjV2MyiyVS+duza0NIRtAslgLUM0vTA1ZaE= -github.com/aws/aws-sdk-go-v2/credentials v1.19.6/go.mod h1:SgHzKjEVsdQr6Opor0ihgWtkWdfRAIwxYzSJ8O85VHY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/service/ecr v1.55.0 h1:Mz6rvVhqmqGPzZNDLolW9IwPzhL/V+QS+dvX+vm/zh8= -github.com/aws/aws-sdk-go-v2/service/ecr v1.55.0/go.mod h1:8n8vVvu7LzveA0or4iWQwNndJStpKOX4HiVHM5jax2U= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.8 h1:2QlSMAimXfMKYRFlxGkbRMRtKN3OqIOB/CfxMcVdzjM= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.8/go.mod h1:esoP/SqS8FVryu4PPLX6ND925slId/IxPxvUBKuBqRk= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM= -github.com/aws/aws-sdk-go-v2/service/kms v1.49.4 h1:2gom8MohxN0SnhHZBYAC4S8jHG+ENEnXjyJ5xKe3vLc= -github.com/aws/aws-sdk-go-v2/service/kms v1.49.4/go.mod h1:HO31s0qt0lso/ADvZQyzKs8js/ku0fMHsfyXW8OPVYc= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 h1:aM/Q24rIlS3bRAhTyFurowU8A0SMyGDtEOY/l/s/1Uw= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.8/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= -github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= -github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0 h1:GOPttfOAf5qAgx7r6b+zCWZrvCsfKffkL4H6mSYx1kA= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0/go.mod h1:a2HN6+p7k0JLDO8514sMr0l4cnrR52z4sWoZ/Uc82ho= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 h1:RtGctYMmkTerGClvdY6bHXdtly4FeYw9wz/NPz62LF8= +github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3/go.mod h1:vBfBu24Ka3/5UZtepbTV0gnc9VPLT8ok+0oDDaYAzn4= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10 h1:1A/sI3LNMi3fhRI5TFLMwwo7ALAALSFVCSGvFlr1Iys= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10/go.mod h1:Diyyyz0b43X13pdi1mVMqlTwDjOmRbJMvDsqnduUYWM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 h1:JFWXO6QPihCknDdnL6VaQE57km4ZKheHIGd9YiOGcTo= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0/go.mod h1:046/oLyFlYdAghYQE2yHXi/E//VM5Cf3/dFmA+3CZ0c= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/buildkite/agent/v3 v3.115.2 h1:26A/dEabfzjorS3Wh/low+yOBM/u8QaT59BYWu0M92w= -github.com/buildkite/agent/v3 v3.115.2/go.mod h1:a3t090/PPxAIIPCjlXF5fhfRvG0E9huFsnMX7B76iIQ= +github.com/buildkite/agent/v3 v3.118.0 h1:wZiuvzku7oCzfzwePq7Zw8nPQxQkoEBQwLdI7b9r3CM= +github.com/buildkite/agent/v3 v3.118.0/go.mod h1:B4+s7ulBypyMmga4X8WrQmosF20UFpTIUt8au9seRGQ= github.com/buildkite/go-pipeline v0.16.0 h1:wEgWUMRAgSg1ZnWOoA3AovtYYdTvN0dLY1zwUWmPP+4= github.com/buildkite/go-pipeline v0.16.0/go.mod h1:VE37qY3X5pmAKKUMoDZvPsHOQuyakB9cmXj9Qn6QasA= github.com/buildkite/interpolate v0.1.5 h1:v2Ji3voik69UZlbfoqzx+qfcsOKLA61nHdU79VV+tPU= @@ -207,8 +207,8 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ= -github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= @@ -216,12 +216,12 @@ 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/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= -github.com/containerd/stargz-snapshotter/estargz v0.18.1 h1:cy2/lpgBXDA3cDKSyEfNOFMA/c10O1axL69EU7iirO8= -github.com/containerd/stargz-snapshotter/estargz v0.18.1/go.mod h1:ALIEqa7B6oVDsrF37GkGN20SuvG/pIMm7FwP7ZmRb0Q= -github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= -github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= -github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= -github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= +github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= +github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= +github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A= +github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4= +github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= +github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -246,12 +246,12 @@ github.com/digitorus/timestamp v0.0.0-20250524132541-c45532741eea h1:ALRwvjsSP53 github.com/digitorus/timestamp v0.0.0-20250524132541-c45532741eea/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/docker/cli v29.1.3+incompatible h1:+kz9uDWgs+mAaIZojWfFt4d53/jv0ZUOOoSh5ZnH36c= -github.com/docker/cli v29.1.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk= +github.com/docker/cli v29.3.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-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI= -github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= +github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= 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/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= @@ -263,6 +263,7 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -285,18 +286,18 @@ github.com/github/smimesign v0.2.0 h1:Hho4YcX5N1I9XNqhq0fNx0Sts8MhLonHd+HRXVGNjv github.com/github/smimesign v0.2.0/go.mod h1:iZiiwNT4HbtGRVqCQu7uJPEZCuEE5sfSSttcnePkDl4= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= -github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= -github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= +github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= +github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM= -github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E= +github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA= +github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y= -github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-git/go-git/v5 v5.19.0 h1:+WkVUQZSy/F1Gb13udrMKjIM2PrzsNfDKFSfo5tkMtc= +github.com/go-git/go-git/v5 v5.19.0/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -304,56 +305,56 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.24.2 h1:6p7WXEuKy1llDgOH8FooVeO+Uq2za9qoAOq4ZN08B50= -github.com/go-openapi/analysis v0.24.2/go.mod h1:x27OOHKANE0lutg2ml4kzYLoHGMKgRm1Cj2ijVOjJuE= -github.com/go-openapi/errors v0.22.6 h1:eDxcf89O8odEnohIXwEjY1IB4ph5vmbUsBMsFNwXWPo= -github.com/go-openapi/errors v0.22.6/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= -github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= -github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= -github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= -github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= -github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4= -github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY= -github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0= -github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0= -github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= -github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= -github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ= -github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8= -github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= -github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= -github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= -github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= -github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= -github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= -github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= -github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= -github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= -github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= -github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= -github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= -github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= -github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= -github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= -github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= -github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= -github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= -github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= -github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= -github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= -github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= -github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= -github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= -github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= -github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= -github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= -github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw= -github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc= -github.com/go-piv/piv-go/v2 v2.4.0 h1:xamQ/fR4MJiw/Ndbk6yi7MVwhjrwlnDAPuaH9zcGb+I= -github.com/go-piv/piv-go/v2 v2.4.0/go.mod h1:ShZi74nnrWNQEdWzRUd/3cSig3uNOcEZp+EWl0oewnI= +github.com/go-openapi/analysis v0.25.0 h1:EnjAq1yO8wEO9HbPmY8vLPEIkdZuuFhCAKBPvCB7bCs= +github.com/go-openapi/analysis v0.25.0/go.mod h1:5WFTRE43WLkPG9r9OtlMfqkkvUTYLVVCIxLlEpyF8kE= +github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA= +github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w= +github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= +github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= +github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= +github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= +github.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ= +github.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA= +github.com/go-openapi/runtime v0.29.4 h1:k2lDxrGoSAJRdhFG2tONKMpkizY/4X1cciSdtzk4Jjo= +github.com/go-openapi/runtime v0.29.4/go.mod h1:K0k/2raY6oqXJnZAgWJB2i/12QKrhUKpZcH4PfV9P18= +github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ= +github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ= +github.com/go-openapi/strfmt v0.26.2 h1:ysjheCh4i1rmFEo2LanhELDNucNzfWTZhUDKgWWPaFM= +github.com/go-openapi/strfmt v0.26.2/go.mod h1:fXh1e449cyUn2NYuz+wb3wARBUdMl7qPEZwX00nqivY= +github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU= +github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA= +github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c= +github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= +github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= +github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= +github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= +github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= +github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= +github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= +github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= +github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= +github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= +github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= +github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY= +github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU= +github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14= +github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= +github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= +github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= +github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= +github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= +github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE= +github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= +github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0= +github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY= +github.com/go-piv/piv-go/v2 v2.5.0 h1:w4KZ3GytEGZt8zm+S7olcIHZk0giL23xVqCa2HgwuqA= +github.com/go-piv/piv-go/v2 v2.5.0/go.mod h1:ShZi74nnrWNQEdWzRUd/3cSig3uNOcEZp+EWl0oewnI= github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= @@ -365,8 +366,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -399,8 +400,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A= -github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs= +github.com/google/certificate-transparency-go v1.3.3 h1:hq/rSxztSkXN2tx/3jQqF6Xc0O565UQPdHrOWvZwybo= +github.com/google/certificate-transparency-go v1.3.3/go.mod h1:iR17ZgSaXRzSa5qvjFl8TnVD5h8ky2JMVio+dzoKMgA= github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -411,8 +412,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.20.7 h1:24VGNpS0IwrOZ2ms2P1QE3Xa5X9p4phx0aUgzYzHW6I= -github.com/google/go-containerregistry v0.20.7/go.mod h1:Lx5LCZQjLH1QBaMPeGwsME9biPeo1lPx6lbGj/UmzgM= +github.com/google/go-containerregistry v0.21.3 h1:Xr+yt3VvwOOn/5nJzd7UoOhwPGiPkYW0zWDLLUXqAi4= +github.com/google/go-containerregistry v0.21.3/go.mod h1:D5ZrJF1e6dMzvInpBPuMCX0FxURz7GLq2rV3Us9aPkc= github.com/google/go-github/v73 v73.0.0 h1:aR+Utnh+Y4mMkS+2qLQwcQ/cF9mOTpdwnzlaw//rG24= github.com/google/go-github/v73 v73.0.0/go.mod h1:fa6w8+/V+edSU0muqdhCVY7Beh1M8F1IlQPZIANKIYw= github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= @@ -430,16 +431,18 @@ github.com/google/trillian v1.7.2/go.mod h1:mfQJW4qRH6/ilABtPYNBerVJAJ/upxHLX81z github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.9 h1:TOpi/QG8iDcZlkQlGlFUti/ZtyLkliXvHDcyUIMuFrU= -github.com/googleapis/enterprise-certificate-proxy v0.3.9/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= -github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE= +github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/graph-gophers/graphql-go v1.9.0 h1:yu0ucKHLc5qGpRwLYKIWtr9bOoxovkWasuBrPQwlHls= +github.com/graph-gophers/graphql-go v1.9.0/go.mod h1:23olKZ7duEvHlF/2ELEoSZaY1aNPfShjP782SOoNTyM= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 h1:kEISI/Gx67NzH3nJxAmY/dGac80kKZgZt134u7Y/k1s= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl9d7cf7mRhtDmm++sOxlnfxI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -469,8 +472,8 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E= -github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM= +github.com/in-toto/attestation v1.2.0 h1:aPRUZ3azbqD7yEBD5fP3TD8Dszf+YHo284SOcpahjQk= +github.com/in-toto/attestation v1.2.0/go.mod h1:r79G45gOmzPismgObLSL+rZTFxUgZLOQJI6LofTZgXk= github.com/in-toto/in-toto-golang v0.10.0 h1:+s2eZQSK3WmWfYV85qXVSBfqgawi/5L02MaqA4o/tpM= github.com/in-toto/in-toto-golang v0.10.0/go.mod h1:wjT4RiyFlLWCmLUJjwB8oZcjaq7HA390aMJcD3xXgmg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -479,8 +482,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -501,14 +504,14 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= -github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= +github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY= +github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= -github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= +github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -521,19 +524,19 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/letsencrypt/boulder v0.20251216.0 h1:zyokmJO6Car3Ksd5bCUIORRis2E6Vi0mEJ3O+naTvaM= -github.com/letsencrypt/boulder v0.20251216.0/go.mod h1:FWHD4EclPHIQ1y2AKEXyySrM3eKiwEyGzcwcupVEFyE= +github.com/letsencrypt/boulder v0.20260223.0 h1:xdS2OnJNUasR6TgVIOpqqcvdkOu47+PQQMBk9ThuWBw= +github.com/letsencrypt/boulder v0.20260223.0/go.mod h1:r3aTSA7UZ7dbDfiGK+HLHJz0bWNbHk6YSPiXgzl23sA= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-tty v0.0.7 h1:KJ486B6qI8+wBO7kQxYgmmEFDaFEE96JMBQ7h400N8Q= -github.com/mattn/go-tty v0.0.7/go.mod h1:f2i5ZOvXBU/tCABmLmOfzLz9azMo5wdAaElRNnJKr+k= +github.com/mattn/go-tty v0.0.8 h1:yxtc0Ye17/1ne/bjy993YUoyP8bJJFa9n5M9XTdwoZQ= +github.com/mattn/go-tty v0.0.8/go.mod h1:f2i5ZOvXBU/tCABmLmOfzLz9azMo5wdAaElRNnJKr+k= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/miekg/pkcs11 v1.1.2 h1:/VxmeAX5qU6Q3EwafypogwWbYryHFmF2RpkJmw3m4MQ= +github.com/miekg/pkcs11 v1.1.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= @@ -561,8 +564,8 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= +github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/oleiade/reflections v1.1.0 h1:D+I/UsXQB4esMathlt0kkZRJZdUDmhv5zGi/HOwYTWo= github.com/oleiade/reflections v1.1.0/go.mod h1:mCxx0QseeVCHs5Um5HhJeCKVC7AwS8kO67tky4rdisA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -586,13 +589,14 @@ github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgr github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pborman/getopt v0.0.0-20180811024354-2b5b3bfb099b/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= -github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= +github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU= +github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -610,8 +614,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= -github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= -github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -624,36 +628,36 @@ github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGq github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k= -github.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14= -github.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk= +github.com/secure-systems-lab/go-securesystemslib v0.11.0 h1:iuCR9kcMFD4QurdKrGvPLoKZLv9YvwPYVr0473BdtFs= +github.com/secure-systems-lab/go-securesystemslib v0.11.0/go.mod h1:+PMOTjUGwHj2vcZ+TFKlb1tXRbrdWE1LYDT5i9JC80Q= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= -github.com/sigstore/cosign/v3 v3.0.4 h1:SuEn9z8V0eyjF0PWxuGgQ7QSPWReNexLJovkZ3wLaf8= -github.com/sigstore/cosign/v3 v3.0.4/go.mod h1:DJY5LPzHiI6bWpG/Q/NQUTfeASjkN8TDAUx1Nnt3I0I= +github.com/sigstore/cosign/v3 v3.0.6 h1:k8XaUd9pmLknHBst/v0rUGHVdB4D9cfaBmWUaMAOocE= +github.com/sigstore/cosign/v3 v3.0.6/go.mod h1:ckLRkVecfUCYxL8isHODY9lwyKmDaRCPn00p6yFxHg0= github.com/sigstore/fulcio v1.8.5 h1:HYTD1/L5wlBp8JxsWxUf8hmfaNBBF/x3r3p5l6tZwbA= github.com/sigstore/fulcio v1.8.5/go.mod h1:tSLYK3JsKvJpDW1BsIsVHZgHj+f8TjXARzqIUWSsSPQ= -github.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY= -github.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= -github.com/sigstore/rekor v1.5.0 h1:rL7SghHd5HLCtsCrxw0yQg+NczGvM75EjSPPWuGjaiQ= -github.com/sigstore/rekor v1.5.0/go.mod h1:D7JoVCUkxwQOpPDNYeu+CE8zeBC18Y5uDo6tF8s2rcQ= -github.com/sigstore/rekor-tiles/v2 v2.0.1 h1:1Wfz15oSRNGF5Dzb0lWn5W8+lfO50ork4PGIfEKjZeo= -github.com/sigstore/rekor-tiles/v2 v2.0.1/go.mod h1:Pjsbhzj5hc3MKY8FfVTYHBUHQEnP0ozC4huatu4x7OU= -github.com/sigstore/sigstore v1.10.4 h1:ytOmxMgLdcUed3w1SbbZOgcxqwMG61lh1TmZLN+WeZE= -github.com/sigstore/sigstore v1.10.4/go.mod h1:tDiyrdOref3q6qJxm2G+JHghqfmvifB7hw+EReAfnbI= +github.com/sigstore/protobuf-specs v0.5.1 h1:/5OPaNuolRJmQfeZLayJGFXMpsRJEdgC6ah1/+7Px7U= +github.com/sigstore/protobuf-specs v0.5.1/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats= +github.com/sigstore/rekor v1.5.1 h1:Ca1egHRWRuDvXV4tZu9aXEXc3Gej9FG+HKeapV9OAMQ= +github.com/sigstore/rekor v1.5.1/go.mod h1:gTLDuZuo3SyQCuZvKqwRPA79Qo/2rw39/WtLP/rZjUQ= +github.com/sigstore/rekor-tiles/v2 v2.2.1 h1:UmV1CBQ3SjxxPGpFmwDoOhoIwiKpM2Qm1pU5tPGmvNk= +github.com/sigstore/rekor-tiles/v2 v2.2.1/go.mod h1:z8n6l6oidpaLjjE6rJERuQqY9X38ulnHZCXyL+DEL7U= +github.com/sigstore/sigstore v1.10.5 h1:KqrOjDhNOVY+uOzQFat2FrGLClPPCb3uz8pK3wuI+ow= +github.com/sigstore/sigstore v1.10.5/go.mod h1:k/mcVVXw3I87dYG/iCVTSW2xTrW7vPzxxGic4KqsqXs= github.com/sigstore/sigstore-go v1.1.4 h1:wTTsgCHOfqiEzVyBYA6mDczGtBkN7cM8mPpjJj5QvMg= github.com/sigstore/sigstore-go v1.1.4/go.mod h1:2U/mQOT9cjjxrtIUeKDVhL+sHBKsnWddn8URlswdBsg= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.3 h1:D/FRl5J9UYAJPGZRAJbP0dH78pfwWnKsyCSBwFBU8CI= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.3/go.mod h1:2GIWuNvTRMvrzd0Nl8RNqxrt9H7X0OBStwOSzGYRjYw= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.3 h1:k5VMLf/ms7hh6MLgVoorM0K+hSMwZLXoywlxh4CXqP8= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.3/go.mod h1:S1Bp3dmP7jYlXcGLAxG81wRbE01NIZING8ZIy0dJlAI= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.3 h1:AVWs0E6rVZMoDTE0Iyezrpo1J6RlI5B4QZhAC4FLE30= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.3/go.mod h1:nxQYF0D6u7mVtiP1azj1YVDIrtz7S0RYCVTqUG8IcCk= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.3 h1:lJSdaC/aOlFHlvqmmV696n1HdXLMLEKGwpNZMV0sKts= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.3/go.mod h1:b2rV9qPbt/jv/Yy75AIOZThP8j+pe1ZdLEjOwmjPdoA= -github.com/sigstore/timestamp-authority/v2 v2.0.4 h1:65IBa4LUeFWDQu9hiTt5lBpi/F5jonJWZtH6VLn4InU= -github.com/sigstore/timestamp-authority/v2 v2.0.4/go.mod h1:EXJLiMDBqRPlzC02hPiFSiYTCqSuUpU68a4vr0DFePM= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.5 h1:aqHRubTITULckG9JAcq2FEhtKkT/RRE8oErfuV3smSI= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.5/go.mod h1:h9eK9QyPqpFskF/ewFkRLtwh4/Q3FLc2/DXbym4IHN8= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.5 h1:+9C6CUkv+J4iT67Lx+H1EGBfAdoAHqXumHadeIj9jA4= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.5/go.mod h1:myZsg7wRiy/vf102g5uUAitYhtXCwepmAGxgHG1VHuE= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.5 h1:BpQx6AhjwIN9LmlO4ypkcMcHiWiepgZQGSw5U69frHU= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.5/go.mod h1:ejMD/17lMJ4HykQRPdj5NNr+OQYIEZto8HjDKghVMOA= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.5 h1:OFwQZgWkB/6J6W5sy3SkXE4pJnhNRnE2cJd8ySXmHpo= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.5/go.mod h1:Ee/enmyxi/RFLVlajbnjgH2wOWQwlJ0wY8qZrk43hEw= +github.com/sigstore/timestamp-authority/v2 v2.0.6 h1:1Vh7/SdmLsVLG6Br6/bisd1SnlicfDm0MJYiA+D7Ppw= +github.com/sigstore/timestamp-authority/v2 v2.0.6/go.mod h1:Nk5ucGBDyH0tXAIMZ0prf6xn8qfTnbJhSq+CDabYcfc= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= @@ -744,28 +748,26 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= -gitlab.com/gitlab-org/api/client-go v1.11.0 h1:L+qzw4kiCf3jKdKHQAwiqYKITvzBrW/tl8ampxNLlv0= -gitlab.com/gitlab-org/api/client-go v1.11.0/go.mod h1:adtVJ4zSTEJ2fP5Pb1zF4Ox1OKFg0MH43yxpb0T0248= -go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= -go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +gitlab.com/gitlab-org/api/client-go v1.46.0 h1:YxBWFZIFYKcGESCb9fpkwzouo+apyB9pr/XTWzNoL24= +gitlab.com/gitlab-org/api/client-go v1.46.0/go.mod h1:FtgyU6g2HS5+fMhw6nLK96GBEEBx5MzntOiJWfIaiN8= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= -go.step.sm/crypto v0.75.0 h1:UAHYD6q6ggYyzLlIKHv1MCUVjZIesXRZpGTlRC/HSHw= -go.step.sm/crypto v0.75.0/go.mod h1:wwQ57+ajmDype9mrI/2hRyrvJd7yja5xVgWYqpUN3PE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.step.sm/crypto v0.77.2 h1:qFjjei+RHc5kP5R7NW9OUWT7SqWIuAOvOkXqg4fNWj8= +go.step.sm/crypto v0.77.2/go.mod h1:W0YJb9onM5l78qgkXIJ2Up6grnwW8EtpCKIza/NCg0o= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -796,11 +798,9 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -812,8 +812,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= -golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -839,11 +839,11 @@ golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -855,8 +855,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -892,8 +892,8 @@ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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= @@ -905,8 +905,8 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -918,10 +918,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= -golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= 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= @@ -938,8 +938,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= -golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= 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= @@ -947,29 +947,29 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.260.0 h1:XbNi5E6bOVEj/uLXQRlt6TKuEzMD7zvW/6tNwltE4P4= -google.golang.org/api v0.260.0/go.mod h1:Shj1j0Phr/9sloYrKomICzdYgsSDImpTxME8rGLaZ/o= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA= +google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20251222181119-0a764e51fe1b h1:kqShdsddZrS6q+DGBCA73CzHsKDu5vW4qw78tFnbVvY= -google.golang.org/genproto v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:gw1DtiPCt5uh/HV9STVEeaO00S5ATsJiJ2LsZV8lcDI= -google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E= -google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE= +google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw= +google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 h1:CogIeEXn4qWYzzQU0QqvYBM8yDF9cFYzDq9ojSpv0Js= +google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 h1:aJmi6DVGGIStN9Mobk/tZOOQUBbj0BPjZjjnOdoZKts= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= 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= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1013,12 +1013,12 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= -k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= -k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8= -k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= -k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE= -k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o= +k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ= +k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4= +k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= +k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg= +k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= @@ -1029,8 +1029,8 @@ sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5E sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/release-utils v0.12.3 h1:iNVJY81QfmMCmXxMg8IvvkkeQNk6ZWlLj+iPKSlKyVQ= -sigs.k8s.io/release-utils v0.12.3/go.mod h1:BvbNmm1BmM3cnEpBmNHWL3wOSziOdGlsYR8vCFq/Q0o= +sigs.k8s.io/release-utils v0.12.4 h1:kuG6WTWGCKx5uUrJwl2uFErOKOw+4Ba8WrPmOQh5J3g= +sigs.k8s.io/release-utils v0.12.4/go.mod h1:Tc3iM9DVM3W9oJu/6rEI+LnREuhy8lZ7wInQhRBtUoo= sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= diff --git a/internal/commands/root/sign.go b/internal/commands/root/sign.go index 24c40f37..4a073854 100644 --- a/internal/commands/root/sign.go +++ b/internal/commands/root/sign.go @@ -50,7 +50,7 @@ func commandSign(o *options, s *gsio.Streams, args ...string) error { // Git is looking for "\n[GNUPG:] SIG_CREATED ", meaning we need to print a // line before SIG_CREATED. BEGIN_SIGNING seems appropriate. GPG emits this, // though GPGSM does not. - gpgout := gpg.NewStatusWriterFromFD(uintptr(o.FlagStatusFD)) + gpgout := gpg.NewStatusWriterFromFD(o.FlagStatusFD) gpgout.Emit(gpg.StatusBeginSigning) var f io.Reader diff --git a/internal/commands/root/verify.go b/internal/commands/root/verify.go index 842e5176..9f9e36c6 100644 --- a/internal/commands/root/verify.go +++ b/internal/commands/root/verify.go @@ -46,7 +46,7 @@ func commandVerify(o *options, s *gsio.Streams, args ...string) error { return errors.New("armor cannot be specified for verification") } - gpgout := gpg.NewStatusWriterFromFD(uintptr(o.FlagStatusFD)) + gpgout := gpg.NewStatusWriterFromFD(o.FlagStatusFD) gpgout.Emit(gpg.StatusNewSig) var ( diff --git a/internal/commands/show/show.go b/internal/commands/show/show.go index 70792a73..d84d3ef7 100644 --- a/internal/commands/show/show.go +++ b/internal/commands/show/show.go @@ -16,10 +16,13 @@ package show import ( "encoding/json" + "fmt" "io" "os" "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + intoto "github.com/in-toto/attestation/go/v1" "github.com/sigstore/gitsign/internal/config" "github.com/sigstore/gitsign/pkg/attest" "github.com/spf13/cobra" @@ -45,7 +48,7 @@ func (o *options) Run(w io.Writer, args []string) error { revision = args[0] } - out, err := attest.CommitStatement(repo, o.FlagRemote, revision) + out, err := statement(repo, o.FlagRemote, revision) if err != nil { return err } @@ -56,6 +59,28 @@ func (o *options) Run(w io.Writer, args []string) error { return enc.Encode(out) } +// statement resolves the revision and returns a tag or commit attestation +// depending on the object type it points to. Lightweight tags are rejected +// since all their data comes from the commit — use the commit ref directly +// instead. +func statement(repo *git.Repository, remote, revision string) (*intoto.Statement, error) { + ref, err := repo.Reference(plumbing.NewTagReferenceName(revision), false) + if err == nil { + obj, err := repo.Object(plumbing.AnyObject, ref.Hash()) + if err != nil { + return nil, err + } + if obj.Type() == plumbing.TagObject { + return attest.TagStatement(repo, remote, revision) + } + // Lightweight tag — reject it. + return nil, fmt.Errorf("%s is not an annotated tag", revision) + } + + // Not a tag ref at all — resolve as a commit. + return attest.CommitStatement(repo, remote, revision) +} + func New(_ *config.Config) *cobra.Command { o := &options{} diff --git a/internal/commands/verify-tag/verify_tag.go b/internal/commands/verify-tag/verify_tag.go index e7674042..33523137 100644 --- a/internal/commands/verify-tag/verify_tag.go +++ b/internal/commands/verify-tag/verify_tag.go @@ -27,6 +27,7 @@ import ( "github.com/sigstore/gitsign/internal/commands/verify" "github.com/sigstore/gitsign/internal/config" "github.com/sigstore/gitsign/internal/gitsign" + "github.com/sigstore/gitsign/pkg/git" "github.com/spf13/cobra" ) @@ -59,32 +60,40 @@ func (o *options) Run(_ io.Writer, args []string) error { return fmt.Errorf("error resolving tag reference: %w", err) } - // Get the tag object - tagObj, err := repo.TagObject(ref.Hash()) + // Read the raw tag object bytes directly from the object store. + // Verifying against the raw bytes (rather than bytes re-encoded through + // go-git) is what git-core does and avoids trust-confusion attacks where + // go-git's loose parser resolves a malformed tag differently than git-core. + obj, err := repo.Storer.EncodedObject(plumbing.TagObject, ref.Hash()) if err != nil { return fmt.Errorf("error reading tag object: %w", err) } - - // Extract the signature - sig := []byte(tagObj.PGPSignature) - p, _ := pem.Decode(sig) - if p == nil || p.Type != "SIGNED MESSAGE" { - return fmt.Errorf("unsupported signature type") - } - - // Get the tag data without the signature - tagData := new(plumbing.MemoryObject) - if err := tagObj.EncodeWithoutSignature(tagData); err != nil { - return err - } - r, err := tagData.Reader() + r, err := obj.Reader() if err != nil { return err } defer r.Close() // nolint:errcheck - data, err := io.ReadAll(r) + + t, err := git.SplitTag(r) if err != nil { - return err + return fmt.Errorf("error extracting tag signature: %w", err) + } + + // Per the SHA-256 transition spec, the in-body PEM block is the + // signature in the tag's current hash algorithm; gpgsig / + // gpgsig-sha256 headers carry signatures over the alternate-algorithm + // form. We verify the local-form signature. + sig := t.InBody + if sig == nil { + return fmt.Errorf("tag has no in-body signature") + } + + p, _ := pem.Decode(sig) + if p == nil { + return fmt.Errorf("%w: not a PEM block", git.ErrUnsupportedSignatureType) + } + if p.Type != "SIGNED MESSAGE" { + return fmt.Errorf("%w: %q", git.ErrUnsupportedSignatureType, p.Type) } // Verify the signature @@ -92,7 +101,7 @@ func (o *options) Run(_ io.Writer, args []string) error { if err != nil { return err } - summary, err := v.Verify(ctx, data, sig, true) + summary, err := v.Verify(ctx, t.Payload, sig, true) if err != nil { return err } diff --git a/internal/commands/verify/verify.go b/internal/commands/verify/verify.go index cbcf93d2..6346e275 100644 --- a/internal/commands/verify/verify.go +++ b/internal/commands/verify/verify.go @@ -60,36 +60,48 @@ func (o *options) Run(_ io.Writer, args []string) error { if err != nil { return fmt.Errorf("error resolving commit object: %w", err) } - c, err := repo.CommitObject(*h) + + obj, err := repo.Storer.EncodedObject(plumbing.CommitObject, *h) if err != nil { return fmt.Errorf("error reading commit object: %w", err) } - - sig := []byte(c.PGPSignature) - p, _ := pem.Decode(sig) - if p == nil || p.Type != "SIGNED MESSAGE" { - return fmt.Errorf("unsupported signature type") - } - - c2 := new(plumbing.MemoryObject) - if err := c.EncodeWithoutSignature(c2); err != nil { - return err - } - r, err := c2.Reader() + r, err := obj.Reader() if err != nil { return err } defer r.Close() // nolint:errcheck - data, err := io.ReadAll(r) + + c, err := git.SplitCommit(r) if err != nil { - return err + return fmt.Errorf("error extracting commit signature: %w", err) + } + + // Per the SHA-256 transition spec a commit can carry gpgsig (SHA-1 + // form), gpgsig-sha256 (SHA-256 form), or both. Prefer gpgsig — every + // repo go-git can read today is SHA-1 form, so its gpgsig matches the + // stripped Payload. gpgsig-sha256 is the fallback for SHA-256-only + // signed commits. + sig := c.Gpgsig + if sig == nil { + sig = c.GpgsigSha256 + } + if sig == nil { + return fmt.Errorf("commit has no gpgsig or gpgsig-sha256 signature") + } + + p, _ := pem.Decode(sig) + if p == nil { + return fmt.Errorf("%w: not a PEM block", git.ErrUnsupportedSignatureType) + } + if p.Type != "SIGNED MESSAGE" { + return fmt.Errorf("%w: %q", git.ErrUnsupportedSignatureType, p.Type) } v, err := gitsign.NewVerifierWithCosignOpts(ctx, o.Config, &o.CertVerifyOptions) if err != nil { return err } - summary, err := v.Verify(ctx, data, sig, true) + summary, err := v.Verify(ctx, c.Payload, sig, true) if err != nil { return err } diff --git a/internal/commands/verify/verify_test.go b/internal/commands/verify/verify_test.go new file mode 100644 index 00000000..1877ea9d --- /dev/null +++ b/internal/commands/verify/verify_test.go @@ -0,0 +1,73 @@ +// Copyright 2026 The Sigstore Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package verify + +import ( + "errors" + "io" + "testing" + + gogit "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/sigstore/gitsign/pkg/git" +) + +// TestRun_RejectsUnsupportedSignatureType confirms the sentinel is wrapped +// so callers can errors.Is on it. End-to-end coverage of the GHSA +// trust-confusion attack lives in +// internal/gitsign/invalid_object_test.go::TestDuplicateTreeTrustConfusion. +func TestRun_RejectsUnsupportedSignatureType(t *testing.T) { + tmpDir := t.TempDir() + repo, err := gogit.PlainInit(tmpDir, false) + if err != nil { + t.Fatalf("PlainInit: %v", err) + } + + // Well-formed commit but with a PGP SIGNATURE (not SIGNED MESSAGE) in gpgsig. + raw := []byte(`tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 +gpgsig -----BEGIN PGP SIGNATURE----- + ZmFrZQ== + -----END PGP SIGNATURE----- + +hi +`) + + obj := repo.Storer.NewEncodedObject() + obj.SetType(plumbing.CommitObject) + w, err := obj.Writer() + if err != nil { + t.Fatalf("obj.Writer: %v", err) + } + if _, err := w.Write(raw); err != nil { + t.Fatalf("write: %v", err) + } + if err := w.Close(); err != nil { + t.Fatalf("close: %v", err) + } + h, err := repo.Storer.SetEncodedObject(obj) + if err != nil { + t.Fatalf("SetEncodedObject: %v", err) + } + + t.Chdir(tmpDir) + + opts := &options{} + err = opts.Run(io.Discard, []string{h.String()}) + if !errors.Is(err, git.ErrUnsupportedSignatureType) { + t.Fatalf("want error wrapping ErrUnsupportedSignatureType, got %v", err) + } +} diff --git a/internal/fork/ietf-cms/verify.go b/internal/fork/ietf-cms/verify.go index fb5fb7e0..d1f1378d 100644 --- a/internal/fork/ietf-cms/verify.go +++ b/internal/fork/ietf-cms/verify.go @@ -5,6 +5,7 @@ import ( "crypto/x509" "encoding/asn1" "errors" + "time" "github.com/github/smimesign/ietf-cms/protocol" "github.com/sigstore/sigstore/pkg/cryptoutils" @@ -180,6 +181,16 @@ func (sd *SignedData) verify(econtent []byte, opts x509.VerifyOptions, tsOpts x5 } } + // If neither the caller nor a timestamp pinned a verification time, + // fall back to this signer cert's NotBefore so the validity-window + // check passes. The caller is expected to verify the actual signing + // time independently (e.g. via Rekor). This is done per-cert so that + // multiple SignerInfos with different validity windows each get a + // time that lies within their own window. + if optsCopy.CurrentTime.IsZero() { + optsCopy.CurrentTime = cert.NotBefore.Add(1 * time.Minute) + } + if chain, err := cert.Verify(optsCopy); err != nil { return nil, err } else { diff --git a/internal/git/git.go b/internal/git/git.go index 2aa9294d..311321f4 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -52,7 +52,11 @@ func LegacySHASign(ctx context.Context, rekor rekor.Writer, ident *fulcio.Identi // using the same key, this is probably okay? e.g. even if you could cause a SHA1 collision, // you would still need the underlying commit to be valid and using the same key which seems hard. - commit, err := git.ObjectHash(data, resp.Signature) + raw, err := git.JoinCommit(&git.CommitSig{Payload: data, Gpgsig: resp.Signature}) + if err != nil { + return nil, fmt.Errorf("error reassembling commit: %w", err) + } + commit, err := git.ObjectHash(raw) if err != nil { return nil, fmt.Errorf("error generating commit hash: %w", err) } diff --git a/internal/gitsign/invalid_object_test.go b/internal/gitsign/invalid_object_test.go new file mode 100644 index 00000000..83403797 --- /dev/null +++ b/internal/gitsign/invalid_object_test.go @@ -0,0 +1,202 @@ +// Copyright 2026 The Sigstore Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gitsign + +import ( + "bytes" + "context" + "crypto/x509" + "crypto/x509/pkix" + "fmt" + "io" + "math/big" + "strings" + "testing" + "time" + + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/go-git/go-git/v5/storage/memory" + "github.com/sigstore/gitsign/internal/signature" + "github.com/sigstore/gitsign/pkg/git" +) + +// TestDuplicateTreeTrustConfusion is an end-to-end regression test for the +// parser trust-confusion class of attack: an attacker replays a legitimate +// signature against a malformed commit with two tree headers. Historically, +// go-git's loose parser was last-wins (keeping the second tree, which +// happened to match the signed canonical form) while git-core was +// first-wins, opening a re-encoding window that let the signature verify +// against attacker-controlled raw bytes. +// +// The defense lives in two layers now: +// 1. Upstream go-git ≥ v5.19.0 switched to first-wins matching git-core, so +// re-encoding through go-git no longer produces canonical bytes. +// 2. gitsign's verify path consumes raw object bytes via SplitCommit and +// skips go-git entirely, so any divergence between signed and stored +// bytes fails cryptographically regardless of parser fashion. +// +// The sub-tests assert all three properties: upstream is fixed, raw-byte +// verification still rejects malformed input, and well-formed input still +// passes (no over-aggressive rejection). +func TestDuplicateTreeTrustConfusion(t *testing.T) { + cert, priv := generateCert(t, &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "alice"}, + NotBefore: time.Now().Add(-time.Minute), + NotAfter: time.Now().Add(time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, + }) + + const ( + legitTree = "b333504b8cf3d9c314fed2cc242c5c38e89534a5" + attackerTree = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + author = "author Alice 1700000000 +0000" + committer = "committer Alice 1700000000 +0000" + message = "legit commit\n" + ) + + // Canonical commit body — this is what Alice intended to sign. A single + // tree header, message "legit commit". + canonical := []byte(fmt.Sprintf("tree %s\n%s\n%s\n\n%s", + legitTree, author, committer, message)) + + id := &identity{cert: cert, priv: priv} + resp, err := signature.Sign(context.Background(), id, canonical, signature.SignOptions{ + Detached: true, + Armor: true, + IncludeCerts: 0, + }) + if err != nil { + t.Fatalf("Sign: %v", err) + } + + roots := x509.NewCertPool() + roots.AddCert(cert) + gv, err := git.NewCertVerifier(git.WithRootPool(roots)) + if err != nil { + t.Fatalf("NewCertVerifier: %v", err) + } + + malformedRaw := []byte(fmt.Sprintf( + "tree %s\ntree %s\n%s\n%s\n%s\n%s", + attackerTree, legitTree, + author, committer, + indent("gpgsig ", string(resp.Signature)), + message, + )) + + t.Run("upstream go-git is no longer last-wins", func(t *testing.T) { + // go-git ≥ v5.19.0 rewrote its commit decoder as a state machine + // that takes the first tree (matching git-core). Re-encoding the + // malformed bytes therefore carries attackerTree, not legitTree, + // so the signature — made over a single-tree commit at legitTree — + // no longer verifies against the re-encoded form. If this ever + // starts passing, go-git has regressed to last-wins parsing and + // the re-encoding attack window is open at the upstream layer + // again (gitsign's own SplitCommit-based path still blocks it, + // per the next sub-test). + reencoded := reencodeViaGoGit(t, malformedRaw) + + if _, err := gv.Verify(context.Background(), reencoded, resp.Signature, true); err == nil { + t.Fatalf("upstream regression: go-git re-encoded malformed bytes verified against the canonical signature") + } + }) + + t.Run("fix: signature fails to verify against the raw malformed bytes", func(t *testing.T) { + // SplitCommit hands the verifier the raw object bytes (with both + // tree headers intact) instead of the go-git-normalized form. The + // signature was made over the canonical (single-tree) bytes, so the + // cryptographic check rejects this. + c, err := git.SplitCommit(bytes.NewReader(malformedRaw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if _, err := gv.Verify(context.Background(), c.Payload, c.Gpgsig, true); err == nil { + t.Fatalf("expected signature verification to fail against malformed raw bytes, got nil error") + } + }) + + t.Run("fix accepts the legitimate commit and signature verifies", func(t *testing.T) { + // Positive control: a well-formed commit with the same signature + // passes SplitCommit and the signature verifies. Guards against an + // over-aggressive validator that also rejects legitimate commits. + wellFormed := []byte(fmt.Sprintf( + "tree %s\n%s\n%s\n%s\n%s", + legitTree, + author, committer, + indent("gpgsig ", string(resp.Signature)), + message, + )) + + c, err := git.SplitCommit(bytes.NewReader(wellFormed)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if _, err := gv.Verify(context.Background(), c.Payload, c.Gpgsig, true); err != nil { + t.Errorf("signature verify over well-formed bytes: %v", err) + } + }) +} + +// indent formats the first line with the given prefix and subsequent lines +// with a single leading space — the git gpgsig header wire format. The output +// ends with a newline suitable for placement as a commit header followed by +// the blank-line message separator. +func indent(prefix, body string) string { + body = strings.TrimSuffix(body, "\n") + lines := strings.Split(body, "\n") + return prefix + strings.Join(lines, "\n ") + "\n" +} + +// reencodeViaGoGit mirrors exactly what the pre-fix gitsign verify path did: +// parse the raw commit through go-git and re-encode it without the signature +// header. This exercises the last-wins normalization that made the GHSA PoC +// work. +func reencodeViaGoGit(t *testing.T, raw []byte) []byte { + t.Helper() + obj := memory.NewStorage().NewEncodedObject() + obj.SetType(plumbing.CommitObject) + w, err := obj.Writer() + if err != nil { + t.Fatal(err) + } + if _, err := w.Write(raw); err != nil { + t.Fatal(err) + } + if err := w.Close(); err != nil { + t.Fatal(err) + } + + var c object.Commit + if err := c.Decode(obj); err != nil { + t.Fatal(err) + } + out := memory.NewStorage().NewEncodedObject() + if err := c.EncodeWithoutSignature(out); err != nil { + t.Fatal(err) + } + r, err := out.Reader() + if err != nil { + t.Fatal(err) + } + defer r.Close() // nolint:errcheck + data, err := io.ReadAll(r) + if err != nil { + t.Fatal(err) + } + return data +} diff --git a/internal/gpg/status.go b/internal/gpg/status.go index 5e2dabbd..51b926db 100644 --- a/internal/gpg/status.go +++ b/internal/gpg/status.go @@ -142,12 +142,16 @@ func NewStatusWriter(w io.Writer) *StatusWriter { } } -func NewStatusWriterFromFD(fd uintptr) *StatusWriter { +func NewStatusWriterFromFD(fd int) *StatusWriter { const ( unixStdout = 1 unixStderr = 2 ) + if fd < 0 { + return NewStatusWriter(io.Discard) + } + var statusFile io.Writer // Even though Windows uses different numbers, we always equate 1/2 with // stdout/stderr because Git always passes `--status-fd=1`. @@ -158,7 +162,7 @@ func NewStatusWriterFromFD(fd uintptr) *StatusWriter { statusFile = os.Stderr default: // TODO: debugging output if this fails - statusFile = os.NewFile(fd, "status") + statusFile = os.NewFile(uintptr(fd), "status") } return NewStatusWriter(statusFile) diff --git a/internal/io/streams.go b/internal/io/streams.go index 34ecb747..ada04ac2 100644 --- a/internal/io/streams.go +++ b/internal/io/streams.go @@ -68,11 +68,12 @@ func New(logPath string) *Streams { return s } -func (s *Streams) Wrap(fn func() error) error { +func (s *Streams) Wrap(fn func() error) (retErr error) { // Log any panics to ttyout, since otherwise they will be lost to os.Stderr. defer func() { if r := recover(); r != nil { fmt.Fprintln(s.TTYOut, r, string(debug.Stack())) // nolint:errcheck + retErr = fmt.Errorf("panic: %v", r) } }() diff --git a/internal/io/streams_test.go b/internal/io/streams_test.go new file mode 100644 index 00000000..69219f91 --- /dev/null +++ b/internal/io/streams_test.go @@ -0,0 +1,79 @@ +// +// Copyright 2026 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io // nolint:revive + +import ( + "bytes" + "errors" + "strings" + "testing" +) + +func TestWrap(t *testing.T) { + wantErr := errors.New("boom") + + for _, tc := range []struct { + name string + fn func() error + wantErr error + wantErrSub string + wantTTYSub string + }{ + { + name: "no error", + fn: func() error { return nil }, + wantErr: nil, + }, + { + name: "returns error", + fn: func() error { return wantErr }, + wantErr: wantErr, + wantTTYSub: "boom", + }, + { + name: "panic returns error", + fn: func() error { panic("kaboom") }, + wantErrSub: "panic: kaboom", + wantTTYSub: "kaboom", + }, + } { + t.Run(tc.name, func(t *testing.T) { + tty := &bytes.Buffer{} + s := &Streams{TTYOut: tty} + + err := s.Wrap(tc.fn) + + switch { + case tc.wantErr != nil: + if !errors.Is(err, tc.wantErr) { + t.Errorf("Wrap() error = %v, want %v", err, tc.wantErr) + } + case tc.wantErrSub != "": + if err == nil || !strings.Contains(err.Error(), tc.wantErrSub) { + t.Errorf("Wrap() error = %v, want error containing %q", err, tc.wantErrSub) + } + default: + if err != nil { + t.Errorf("Wrap() error = %v, want nil", err) + } + } + + if tc.wantTTYSub != "" && !strings.Contains(tty.String(), tc.wantTTYSub) { + t.Errorf("TTYOut = %q, want it to contain %q", tty.String(), tc.wantTTYSub) + } + }) + } +} diff --git a/internal/rekor/oid/oid.go b/internal/rekor/oid/oid.go index 98959eb9..3eb7e4fc 100644 --- a/internal/rekor/oid/oid.go +++ b/internal/rekor/oid/oid.go @@ -113,7 +113,7 @@ func ToAttributes(tlog *models.LogEntryAnon) (protocol.Attributes, error) { return nil, err } - attrs := protocol.Attributes{} + attrs := make(protocol.Attributes, 0, 1) attr, err := protocol.NewAttribute(OIDRekorTransparencyLogEntry, out) if err != nil { return nil, err diff --git a/pkg/attest/statement.go b/pkg/attest/statement.go index 636d955b..6e54c451 100644 --- a/pkg/attest/statement.go +++ b/pkg/attest/statement.go @@ -18,11 +18,14 @@ import ( "encoding/base64" "encoding/pem" "errors" + "fmt" "github.com/github/smimesign/ietf-cms/protocol" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" intoto "github.com/in-toto/attestation/go/v1" + gitraw "github.com/sigstore/gitsign/pkg/git" "github.com/sigstore/gitsign/pkg/predicate" "github.com/sigstore/sigstore/pkg/cryptoutils" "google.golang.org/protobuf/encoding/protojson" @@ -40,7 +43,14 @@ func CommitStatement(repo *git.Repository, remote, revision string) (*intoto.Sta if err != nil { return nil, err } - commit, err := repo.CommitObject(*hash) + obj, err := repo.Storer.EncodedObject(plumbing.CommitObject, *hash) + if err != nil { + return nil, err + } + if err := gitraw.ValidateCommit(obj); err != nil { + return nil, err + } + commit, err := object.DecodeCommit(repo.Storer, obj) if err != nil { return nil, err } @@ -124,6 +134,98 @@ func CommitStatement(repo *git.Repository, remote, revision string) (*intoto.Sta }, nil } +// TagStatement creates an intoto statement representing a git tag signature. +// +// The statement subject is the tag object itself (not the underlying commit). +// For lightweight (non-annotated) tags or unsigned annotated tags the statement +// will still be produced but without signature or signer information, mirroring +// how CommitStatement handles unsigned commits. +func TagStatement(repo *git.Repository, remote, tagName string) (*intoto.Statement, error) { + // Resolve the tag reference. + ref, err := repo.Tag(tagName) + if err != nil { + return nil, err + } + + // Load the tag object. If the storer doesn't have one (lightweight + // tag), surface the existing "not an annotated tag" error. + obj, err := repo.Storer.EncodedObject(plumbing.TagObject, ref.Hash()) + if err != nil { + return nil, fmt.Errorf("tag %q is not an annotated tag", tagName) + } + if err := gitraw.ValidateTag(obj); err != nil { + return nil, err + } + tagObj, err := object.DecodeTag(repo.Storer, obj) + if err != nil { + return nil, err + } + + // We've got the annotated tag. Create the full predicate + pred := &predicate.GitTag{ + Source: &predicate.Tag{ + Object: tagObj.Target.String(), + ObjectType: tagObj.TargetType.String(), + Tag: tagObj.Name, + Tagger: &predicate.Author{ + Name: tagObj.Tagger.Name, + Email: tagObj.Tagger.Email, + Date: timestamppb.New(tagObj.Tagger.When), + }, + Message: tagObj.Message, + }, + Signature: tagObj.PGPSignature, + } + + // Extract signer info from the signature. + if pemBlock, _ := pem.Decode([]byte(tagObj.PGPSignature)); pemBlock != nil { + sigs, err := parseSignature(pemBlock.Bytes) + if err != nil { + return nil, err + } + pred.SignerInfo = sigs + } + + return buildTagStatement(repo, remote, ref.Hash(), pred, predicate.TagTypeV01) +} + +func buildTagStatement(repo *git.Repository, remote string, tagHash plumbing.Hash, pred *predicate.GitTag, predicateType string) (*intoto.Statement, error) { + // Try to resolve the remote name for the subject. + resolvedRemote, err := repo.Remote(remote) + if err != nil && !errors.Is(err, git.ErrRemoteNotFound) { + return nil, err + } + remoteName := "" + if resolvedRemote != nil && resolvedRemote.Config() != nil && len(resolvedRemote.Config().URLs) > 0 { + remoteName = resolvedRemote.Config().URLs[0] + } + + // Convert predicate to structpb.Struct for in-toto Statement. + jsonBytes, err := protojson.Marshal(pred) + if err != nil { + return nil, err + } + predicateStruct := &structpb.Struct{} + if err := protojson.Unmarshal(jsonBytes, predicateStruct); err != nil { + return nil, err + } + + return &intoto.Statement{ + Type: intoto.StatementTypeUri, + Subject: []*intoto.ResourceDescriptor{ + { + Digest: map[string]string{ + "sha1": tagHash.String(), + "gitTag": tagHash.String(), + }, + Name: remoteName, + }, + }, + Predicate: predicateStruct, + PredicateType: predicateType, + }, nil +} + func parseSignature(raw []byte) ([]*predicate.SignerInfo, error) { ci, err := protocol.ParseContentInfo(raw) if err != nil { diff --git a/pkg/attest/statement_test.go b/pkg/attest/statement_test.go index 96b8b6bc..a3870f77 100644 --- a/pkg/attest/statement_test.go +++ b/pkg/attest/statement_test.go @@ -15,6 +15,7 @@ package attest import ( + "errors" "fmt" "os" "testing" @@ -25,11 +26,14 @@ import ( "github.com/go-git/go-git/v5/storage/memory" "github.com/google/go-cmp/cmp" intoto "github.com/in-toto/attestation/go/v1" + gitraw "github.com/sigstore/gitsign/pkg/git" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/testing/protocmp" ) -func TestCommitStatement(t *testing.T) { +// newTestRepo creates an in-memory repo with the given remote configuration. +func newTestRepo(t *testing.T) (*git.Repository, *memory.Storage) { + t.Helper() storage := memory.NewStorage() repo := &git.Repository{ Storer: storage, @@ -38,12 +42,36 @@ func TestCommitStatement(t *testing.T) { Remotes: map[string]*config.RemoteConfig{ "origin": { Name: "origin", - URLs: []string{"git@github.com:wlynch/gitsign.git"}, + URLs: []string{"https://github.com/sigstore/gitsign.git"}, }, }, }); err != nil { t.Fatalf("error setting git config: %v", err) } + return repo, storage +} + +// storeObject writes raw object bytes into the storage and returns the hash. +func storeObject(t *testing.T, storage *memory.Storage, objType plumbing.ObjectType, raw []byte) plumbing.Hash { + t.Helper() + obj := storage.NewEncodedObject() + obj.SetType(objType) + w, err := obj.Writer() + if err != nil { + t.Fatalf("error getting git object writer: %v", err) + } + if _, err = w.Write(raw); err != nil { + t.Fatalf("error writing git object: %v", err) + } + h, err := storage.SetEncodedObject(obj) + if err != nil { + t.Fatalf("error storing git object: %v", err) + } + return h +} + +func TestCommitStatement(t *testing.T) { + repo, storage := newTestRepo(t) // Expect files in testdata directory: // foo.in.txt -> foo.out.json @@ -58,24 +86,53 @@ func TestCommitStatement(t *testing.T) { if err != nil { t.Fatalf("error reading input: %v", err) } - obj := storage.NewEncodedObject() - obj.SetType(plumbing.CommitObject) - w, err := obj.Writer() + h := storeObject(t, storage, plumbing.CommitObject, raw) + + got, err := CommitStatement(repo, "origin", h.String()) if err != nil { - t.Fatalf("error getting git object writer: %v", err) + t.Fatalf("statement(): %v", err) } - _, err = w.Write(raw) + + wantRaw, err := os.ReadFile(fmt.Sprintf("testdata/%s.out.json", tc)) if err != nil { - t.Fatalf("error writing git commit: %v", err) + t.Fatalf("error reading want json: %v", err) } - h, err := storage.SetEncodedObject(obj) + + want := &intoto.Statement{} + if err := protojson.Unmarshal(wantRaw, want); err != nil { + t.Fatalf("error decoding want json: %v", err) + } + + if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { + t.Error(diff) + } + }) + } +} + +func TestTagStatement(t *testing.T) { + repo, storage := newTestRepo(t) + + // IMPORTANT: When generating new test files, use `git cat-file tag > foo.in.txt`. + for _, tc := range []string{ + "fulcio-tag", + } { + t.Run(tc, func(t *testing.T) { + raw, err := os.ReadFile(fmt.Sprintf("testdata/%s.in.txt", tc)) if err != nil { - t.Fatalf("error storing git commit: %v", err) + t.Fatalf("error reading input: %v", err) } + h := storeObject(t, storage, plumbing.TagObject, raw) - got, err := CommitStatement(repo, "origin", h.String()) + // Create a tag reference pointing to the stored tag object. + tagRef := plumbing.NewHashReference(plumbing.NewTagReferenceName(tc), h) + if err := storage.SetReference(tagRef); err != nil { + t.Fatalf("error setting tag reference: %v", err) + } + + got, err := TagStatement(repo, "origin", tc) if err != nil { - t.Fatalf("statement(): %v", err) + t.Fatalf("TagStatement(): %v", err) } wantRaw, err := os.ReadFile(fmt.Sprintf("testdata/%s.out.json", tc)) @@ -94,3 +151,71 @@ func TestCommitStatement(t *testing.T) { }) } } + +// TestCommitStatement_DuplicateAuthor confirms the attest path refuses +// to produce a predicate for a commit with duplicate singleton headers. +// `git hash-object --literally` (and an adversary's direct object write) +// can produce these; go-git ≥ v5.19.0 would silently take the first +// header, but the attestation use case prefers an outright refusal over +// a predicate that obscures the ambiguity. +func TestCommitStatement_DuplicateAuthor(t *testing.T) { + repo, storage := newTestRepo(t) + + raw := []byte("tree 4cf9f177c4c015836fca6a31f9c3917e89ae29ec\n" + + "author Alice 1700000000 +0000\n" + + "author Mallory 1700000001 +0000\n" + + "committer Alice 1700000000 +0000\n" + + "\n" + + "hello\n") + h := storeObject(t, storage, plumbing.CommitObject, raw) + + _, err := CommitStatement(repo, "origin", h.String()) + if !errors.Is(err, gitraw.ErrMalformedObject) { + t.Fatalf("CommitStatement: got err=%v, want ErrMalformedObject", err) + } +} + +func TestTagStatement_DuplicateTagger(t *testing.T) { + repo, storage := newTestRepo(t) + + raw := []byte("object 2d9cff2bad7132c586e128bcc23322dbb5297e8e\n" + + "type commit\n" + + "tag v1\n" + + "tagger Alice 1700000000 +0000\n" + + "tagger Mallory 1700000001 +0000\n" + + "\n" + + "release\n") + h := storeObject(t, storage, plumbing.TagObject, raw) + tagRef := plumbing.NewHashReference(plumbing.NewTagReferenceName("v1"), h) + if err := storage.SetReference(tagRef); err != nil { + t.Fatalf("error setting tag reference: %v", err) + } + + _, err := TagStatement(repo, "origin", "v1") + if !errors.Is(err, gitraw.ErrMalformedObject) { + t.Fatalf("TagStatement: got err=%v, want ErrMalformedObject", err) + } +} + +func TestTagStatementLightweight(t *testing.T) { + repo, storage := newTestRepo(t) + + // Store a commit object so the lightweight tag has something to point to. + raw, err := os.ReadFile("testdata/fulcio-cert.in.txt") + if err != nil { + t.Fatalf("error reading input: %v", err) + } + commitHash := storeObject(t, storage, plumbing.CommitObject, raw) + + // Create a lightweight tag (ref pointing directly at the commit). + tagRef := plumbing.NewHashReference(plumbing.NewTagReferenceName("lightweight"), commitHash) + if err := storage.SetReference(tagRef); err != nil { + t.Fatalf("error setting tag reference: %v", err) + } + + // Lightweight tags are not annotated, so TagStatement should return an error. + _, err = TagStatement(repo, "origin", "lightweight") + if err == nil { + t.Fatal("expected error for lightweight tag, got nil") + } +} diff --git a/pkg/attest/testdata/fulcio-cert.out.json b/pkg/attest/testdata/fulcio-cert.out.json index 387c76f8..3fe38310 100644 --- a/pkg/attest/testdata/fulcio-cert.out.json +++ b/pkg/attest/testdata/fulcio-cert.out.json @@ -3,7 +3,7 @@ "predicateType": "https://gitsign.sigstore.dev/predicate/git/v0.1", "subject": [ { - "name": "git@github.com:wlynch/gitsign.git", + "name": "https://github.com/sigstore/gitsign.git", "digest": { "sha1": "10a3086104c5331623be85a5e30d709f457b536b" } diff --git a/pkg/attest/testdata/fulcio-tag.in.txt b/pkg/attest/testdata/fulcio-tag.in.txt new file mode 100644 index 00000000..d5f0e589 --- /dev/null +++ b/pkg/attest/testdata/fulcio-tag.in.txt @@ -0,0 +1,31 @@ +object dd51a25b4f9fa103ca6f5cdca4183fa15cb3e627 +type commit +tag v0.14.0 +tagger Billy Lynch 1769622626 -0500 + +v0.14.0 +-----BEGIN SIGNED MESSAGE----- +MIIELwYJKoZIhvcNAQcCoIIEIDCCBBwCAQExDTALBglghkgBZQMEAgEwCwYJKoZI +hvcNAQcBoIIC0TCCAs0wggJToAMCAQICFHX4oNpDnR17SVpco2KLU+Lk4cGZMAoG +CCqGSM49BAMDMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2ln +c3RvcmUtaW50ZXJtZWRpYXRlMB4XDTI2MDEyODE3NTAyN1oXDTI2MDEyODE4MDAy +N1owADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDEEyshqZPYaNaswQ7HfxREu +3CKrEAAvf7bwQgWu93i4uN44VzJsW6dxV+7LBmUYP1wqyW03EthlUerMenbRtOOj +ggFyMIIBbjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYD +VR0OBBYEFIX1lG52urQX/ffvXImAVZvAjznAMB8GA1UdIwQYMBaAFN/T6c9WJBGW ++ajY6ShVosYuGGQ/MCIGA1UdEQEB/wQYMBaBFGJpbGx5QGNoYWluZ3VhcmQuZGV2 +MCkGCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgor +BgEEAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYB +BAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6O +AAABnAW6ZYYAAAQDAEcwRQIhAKyeO8OIbBRLAq0PBuJpDlL9m8bbUO97Scd5PFQ6 +bi1ZAiBNHC+SXcQ9tLzlPzNvKRniiKZ1UJuzX+LMt9eUYTd/1DAKBggqhkjOPQQD +AwNoADBlAjEAquQWpoWTaWwNYU8aXRUxJTzoUsxM7Tdjgzz7mEcDsz5KD55nsFnJ +xos0TZXioYoPAjBsmOdaJIh0zIzOj/N6B4VQr+txDqEovFwzcnDFmz2A8Y8CVUR1 +ZOb6gAGmo7RDzYAxggEkMIIBIAIBATBPMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRl +djEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlAhR1+KDaQ50de0laXKNi +i1Pi5OHBmTALBglghkgBZQMEAgGgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB +MBwGCSqGSIb3DQEJBTEPFw0yNjAxMjgxNzUwMjdaMC8GCSqGSIb3DQEJBDEiBCC5 +j6i3ZTOxHPKTcA0Ub1oUtIxfJJ7xfbneBgnUJwakqTAKBggqhkjOPQQDAgRGMEQC +IB8DIWqlTucRQEeSNpuEqUeVXhn4/DlYshDfwn5upV09AiB4gZOaUDX70vYSBup4 +luA5kuL+Nx6AafURfB2WxO8Krw== +-----END SIGNED MESSAGE----- diff --git a/pkg/attest/testdata/fulcio-tag.out.json b/pkg/attest/testdata/fulcio-tag.out.json new file mode 100644 index 00000000..186bf916 --- /dev/null +++ b/pkg/attest/testdata/fulcio-tag.out.json @@ -0,0 +1,33 @@ +{ + "type": "https://in-toto.io/Statement/v1", + "predicateType": "https://gitsign.sigstore.dev/predicate/tag/v0.1", + "subject": [ + { + "name": "https://github.com/sigstore/gitsign.git", + "digest": { + "sha1": "59b806d0c7932446ca9375b36ea3d421cc673224", + "gitTag": "59b806d0c7932446ca9375b36ea3d421cc673224" + } + } + ], + "predicate": { + "source": { + "object": "dd51a25b4f9fa103ca6f5cdca4183fa15cb3e627", + "object_type": "commit", + "tag": "v0.14.0", + "tagger": { + "name": "Billy Lynch", + "email": "billy@chainguard.dev", + "date": "2026-01-28T17:50:26Z" + }, + "message": "v0.14.0\n" + }, + "signature": "-----BEGIN SIGNED MESSAGE-----\nMIIELwYJKoZIhvcNAQcCoIIEIDCCBBwCAQExDTALBglghkgBZQMEAgEwCwYJKoZI\nhvcNAQcBoIIC0TCCAs0wggJToAMCAQICFHX4oNpDnR17SVpco2KLU+Lk4cGZMAoG\nCCqGSM49BAMDMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2ln\nc3RvcmUtaW50ZXJtZWRpYXRlMB4XDTI2MDEyODE3NTAyN1oXDTI2MDEyODE4MDAy\nN1owADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDEEyshqZPYaNaswQ7HfxREu\n3CKrEAAvf7bwQgWu93i4uN44VzJsW6dxV+7LBmUYP1wqyW03EthlUerMenbRtOOj\nggFyMIIBbjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYD\nVR0OBBYEFIX1lG52urQX/ffvXImAVZvAjznAMB8GA1UdIwQYMBaAFN/T6c9WJBGW\n+ajY6ShVosYuGGQ/MCIGA1UdEQEB/wQYMBaBFGJpbGx5QGNoYWluZ3VhcmQuZGV2\nMCkGCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgor\nBgEEAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYB\nBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6O\nAAABnAW6ZYYAAAQDAEcwRQIhAKyeO8OIbBRLAq0PBuJpDlL9m8bbUO97Scd5PFQ6\nbi1ZAiBNHC+SXcQ9tLzlPzNvKRniiKZ1UJuzX+LMt9eUYTd/1DAKBggqhkjOPQQD\nAwNoADBlAjEAquQWpoWTaWwNYU8aXRUxJTzoUsxM7Tdjgzz7mEcDsz5KD55nsFnJ\nxos0TZXioYoPAjBsmOdaJIh0zIzOj/N6B4VQr+txDqEovFwzcnDFmz2A8Y8CVUR1\nZOb6gAGmo7RDzYAxggEkMIIBIAIBATBPMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRl\ndjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlAhR1+KDaQ50de0laXKNi\ni1Pi5OHBmTALBglghkgBZQMEAgGgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB\nMBwGCSqGSIb3DQEJBTEPFw0yNjAxMjgxNzUwMjdaMC8GCSqGSIb3DQEJBDEiBCC5\nj6i3ZTOxHPKTcA0Ub1oUtIxfJJ7xfbneBgnUJwakqTAKBggqhkjOPQQDAgRGMEQC\nIB8DIWqlTucRQEeSNpuEqUeVXhn4/DlYshDfwn5upV09AiB4gZOaUDX70vYSBup4\nluA5kuL+Nx6AafURfB2WxO8Krw==\n-----END SIGNED MESSAGE-----\n", + "signer_info": [ + { + "attributes": "MWkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjYwMTI4MTc1MDI3WjAvBgkqhkiG9w0BCQQxIgQguY+ot2UzsRzyk3ANFG9aFLSMXySe8X253gYJ1CcGpKk=", + "certificate": "-----BEGIN CERTIFICATE-----\nMIICzTCCAlOgAwIBAgIUdfig2kOdHXtJWlyjYotT4uThwZkwCgYIKoZIzj0EAwMw\nNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl\ncm1lZGlhdGUwHhcNMjYwMTI4MTc1MDI3WhcNMjYwMTI4MTgwMDI3WjAAMFkwEwYH\nKoZIzj0CAQYIKoZIzj0DAQcDQgAEMQTKyGpk9ho1qzBDsd/FES7cIqsQAC9/tvBC\nBa73eLi43jhXMmxbp3FX7ssGZRg/XCrJbTcS2GVR6sx6dtG046OCAXIwggFuMA4G\nA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUhfWU\nbna6tBf99+9ciYBVm8CPOcAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4Y\nZD8wIgYDVR0RAQH/BBgwFoEUYmlsbHlAY2hhaW5ndWFyZC5kZXYwKQYKKwYBBAGD\nvzABAQQbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMCsGCisGAQQBg78wAQgE\nHQwbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMIGKBgorBgEEAdZ5AgQCBHwE\negB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGcBbplhgAA\nBAMARzBFAiEArJ47w4hsFEsCrQ8G4mkOUv2bxttQ73tJx3k8VDpuLVkCIE0cL5Jd\nxD20vOU/M28pGeKIpnVQm7Nf4sy315RhN3/UMAoGCCqGSM49BAMDA2gAMGUCMQCq\n5BamhZNpbA1hTxpdFTElPOhSzEztN2ODPPuYRwOzPkoPnmewWcnGizRNleKhig8C\nMGyY51okiHTMjM6P83oHhVCv63EOoSi8XDNycMWbPYDxjwJVRHVk5vqAAaajtEPN\ngA==\n-----END CERTIFICATE-----\n" + } + ] + } +} diff --git a/pkg/attest/testdata/gpg.out.json b/pkg/attest/testdata/gpg.out.json index 8d84c7a3..033f7bc5 100644 --- a/pkg/attest/testdata/gpg.out.json +++ b/pkg/attest/testdata/gpg.out.json @@ -3,7 +3,7 @@ "predicateType": "https://gitsign.sigstore.dev/predicate/git/v0.1", "subject": [ { - "name": "git@github.com:wlynch/gitsign.git", + "name": "https://github.com/sigstore/gitsign.git", "digest": { "sha1": "262c05491554c57ee641461f315bf4023d0e93c7" } diff --git a/pkg/git/git_test.go b/pkg/git/git_test.go index f5b38183..1564925c 100644 --- a/pkg/git/git_test.go +++ b/pkg/git/git_test.go @@ -14,104 +14,44 @@ package git -import "testing" - -const ( - // These are real commit values generated in a test repo that were manually verified. - - // Rekor index: 2802961 - tagBody = `object 040b9af339e69d18848b7bbe05cb27ee42bb0161 -type commit -tag signed-tag2 -tagger Billy Lynch 1656531453 -0400 - -asdf -` - tagSig = `-----BEGIN SIGNED MESSAGE----- -MIIEBQYJKoZIhvcNAQcCoIID9jCCA/ICAQExDTALBglghkgBZQMEAgEwCwYJKoZI -hvcNAQcBoIICpjCCAqIwggIooAMCAQICFGc8V7+B2VlJeFLpglonkbyb2kVeMAoG -CCqGSM49BAMDMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2ln -c3RvcmUtaW50ZXJtZWRpYXRlMB4XDTIyMDYyOTE5MzczOVoXDTIyMDYyOTE5NDcz -OVowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP8JBFjhGqQsQCBmZqyuSHcG -KZpDDRdpq7cl8Bhwuvu9A2bDz0gcuA/Nv18fKtikguBw6YBmEPi8S/YMYgMctVyj -ggFHMIIBQzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYD -VR0OBBYEFMhi60DZPBYkwhDEuiltjyvxYYTDMB8GA1UdIwQYMBaAFN/T6c9WJBGW -+ajY6ShVosYuGGQ/MCIGA1UdEQEB/wQYMBaBFGJpbGx5QGNoYWluZ3VhcmQuZGV2 -MCwGCisGAQQBg78wAQEEHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDCB -iQYKKwYBBAHWeQIEAgR7BHkAdwB1AAhgkvAoUv9oRdHRayeEnEVnGKwWPcM40m3m -vCIGNm9yAAABgbD4HlAAAAQDAEYwRAIgON4g6BzdFgOIcCFk+8EXKpEw1XD0/DZ2 -7gcb9Q/Jeg0CIGozxLGJS71uA2OU3JD6pGWCdnpYVsiG44/Em5w34SHmMAoGCCqG -SM49BAMDA2gAMGUCMQDjLNl6Zaj5HbfLqqUvWNgz/R1VoQ3QG88kzu3GY0PodO8K -QDcgt8bcGXzEdKkSFg4CMHIkGGLrG3bOYsjyIqZxiO6ess1jJxsFnM+GzvjwNRJk -eWF9g96u/pNN8KA5VhveljGCASUwggEhAgEBME8wNzEVMBMGA1UEChMMc2lnc3Rv -cmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUCFGc8V7+B2VlJ -eFLpglonkbyb2kVeMAsGCWCGSAFlAwQCAaBpMBgGCSqGSIb3DQEJAzELBgkqhkiG -9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIyMDYyOTE5MzczOVowLwYJKoZIhvcNAQkE -MSIEINZzCK5apWIVIKK26tVflr6zNoFkJm8SXQC5T65qwF1BMAoGCCqGSM49BAMC -BEcwRQIgfAl7Elc0DB8UEMOXo3ZxKmN7zTrMO/tvhu1Himgc9IYCIQCxf06wWHVw -YKHxU2tY8MNGomLVk0LyA/QaHQnoo34t8A== ------END SIGNED MESSAGE----- -` - tagSHA = "ed092bb8688d6e37185bcdb58900940703c1a292" +import ( + "os" + "testing" +) - // Rekor index: 2801760 - commitBody = `tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 -parent 2dc0ab59d7f0a7a62423bd181d9e2ab3adb7b56d -author Billy Lynch 1656524971 -0400 -committer Billy Lynch 1656524971 -0400 +// loadObject reads a raw object from testdata. The bytes are exactly what +// git's object store holds (i.e. the output of `git cat-file -p `), +// without the " \0" prefix that git prepends before hashing. +func loadObject(t *testing.T, name string) []byte { + t.Helper() + raw, err := os.ReadFile("testdata/" + name) + if err != nil { + t.Fatalf("read testdata/%s: %v", name, err) + } + return raw +} -foo -` - commitSig = `-----BEGIN SIGNED MESSAGE----- -MIIEBwYJKoZIhvcNAQcCoIID+DCCA/QCAQExDTALBglghkgBZQMEAgEwCwYJKoZI -hvcNAQcBoIICqDCCAqQwggIqoAMCAQICFHtMvZZL50P5bLkgDxwMf2MN4jdAMAoG -CCqGSM49BAMDMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2ln -c3RvcmUtaW50ZXJtZWRpYXRlMB4XDTIyMDYyOTE3NDkzNFoXDTIyMDYyOTE3NTkz -NFowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNf9io+JonCZhwe/dSkSoJ/Y -eRun8C7xhPVF3FhoPnPVWdywaAEIkniA2WSHXLHt5aQN/08bV65haMZA/Luhmhaj -ggFJMIIBRTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYD -VR0OBBYEFGzhjCzFUI0caspJJfD4bToYxfDhMB8GA1UdIwQYMBaAFN/T6c9WJBGW -+ajY6ShVosYuGGQ/MCIGA1UdEQEB/wQYMBaBFGJpbGx5QGNoYWluZ3VhcmQuZGV2 -MCwGCisGAQQBg78wAQEEHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDCB -iwYKKwYBBAHWeQIEAgR9BHsAeQB3AAhgkvAoUv9oRdHRayeEnEVnGKwWPcM40m3m -vCIGNm9yAAABgbCVKBkAAAQDAEgwRgIhAJHJalxdErw5icNqfgWtyrv75XGXxAZz -F/J4b7B8ikQAAiEAj8g8ZiSIGmePmES19Y/yFeGj6Fz0NGE2Rk5uJdKyAGEwCgYI -KoZIzj0EAwMDaAAwZQIxAKpQFL9D5s1YVEmNWBoEQ1oo6gBESGhd5L1Kcdq52Ltt -KWXKKB7tpVRwC0lfof2ILgIwU1LTaKeKWb0vToMY9InoS2+hAVljbEh3oxKm/JoX -hiRx2GiDe2OyLCs76/kbH6C/MYIBJTCCASECAQEwTzA3MRUwEwYDVQQKEwxzaWdz -dG9yZS5kZXYxHjAcBgNVBAMTFXNpZ3N0b3JlLWludGVybWVkaWF0ZQIUe0y9lkvn -Q/lsuSAPHAx/Yw3iN0AwCwYJYIZIAWUDBAIBoGkwGAYJKoZIhvcNAQkDMQsGCSqG -SIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjIwNjI5MTc0OTM0WjAvBgkqhkiG9w0B -CQQxIgQgSbThfvXoc6INDxPzRtlUu0TTBjFLm4XmwuxXAzfsZmkwCgYIKoZIzj0E -AwIERzBFAiBeNZewVOFI5aa7bPUXa05HDgz5yevQ9aPclDX6U+koTAIhAMbyysil -7I/UWLzhwM+9iusn3JXy71akUTcrqi2MNPaO ------END SIGNED MESSAGE----- -` - commitSHA = "040b9af339e69d18848b7bbe05cb27ee42bb0161" +// SHAs of the objects in testdata, verified against git's stored hash: +// +// printf 'commit %d\0' $(wc -c < testdata/commit.txt) | cat - testdata/commit.txt | shasum +// printf 'tag %d\0' $(wc -c < testdata/tag.txt) | cat - testdata/tag.txt | shasum +const ( + commitSHA = "4954440f9953588782896a1a473d8968765db82b" + tagSHA = "a7c0f87e7d8f475cfe66d9c848b77cf3b85d860b" ) func TestObjectHash(t *testing.T) { for _, tc := range []struct { name string - body string - sig string + file string sha string }{ - { - name: "tag", - body: tagBody, - sig: tagSig, - sha: tagSHA, - }, - { - name: "commit", - body: commitBody, - sig: commitSig, - sha: commitSHA, - }, + {name: "commit", file: "commit.txt", sha: commitSHA}, + {name: "tag", file: "tag.txt", sha: tagSHA}, } { t.Run(tc.name, func(t *testing.T) { - got, err := ObjectHash([]byte(tc.body), []byte(tc.sig)) + raw := loadObject(t, tc.file) + got, err := ObjectHash(raw) if err != nil { t.Fatal(err) } diff --git a/pkg/git/rawobj.go b/pkg/git/rawobj.go new file mode 100644 index 00000000..fa34c163 --- /dev/null +++ b/pkg/git/rawobj.go @@ -0,0 +1,497 @@ +// +// Copyright 2026 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package git + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "unicode" + + "github.com/go-git/go-git/v5/plumbing" +) + +var ( + // ErrMalformedObject is returned for structural ambiguities the + // downstream signature check can't catch on its own — currently a + // duplicate gpgsig or gpgsig-sha256 header (where it's unclear which + // signature to extract) or, on the join side, a payload with no + // header/body separator. + ErrMalformedObject = errors.New("malformed git object") + // ErrUnsupportedSignatureType is returned when the embedded signature is + // not a format gitsign can verify. gitsign only accepts PEM blocks of type + // "SIGNED MESSAGE" (CMS/PKCS7). + ErrUnsupportedSignatureType = errors.New("unsupported signature type") +) + +// gpgsigPrefix is the SHA-1 signature header. The trailing space distinguishes +// it from gpgsig-sha256 and any future "gpgsig-*" extension headers, which +// are independently valid header fields. +const gpgsigPrefix = "gpgsig " + +// gpgsigSha256Prefix is the SHA-256 transition compat header introduced by +// the hash-function-transition spec. When compatObjectFormat is set, git +// emits this alongside gpgsig — see +// https://git-scm.com/docs/hash-function-transition#_signed_commits. +const gpgsigSha256Prefix = "gpgsig-sha256 " + +// CommitSig holds signature material extracted from a commit's raw bytes. +// +// Per the SHA-256 transition spec, the signed payload for either gpgsig or +// gpgsig-sha256 is the commit content with BOTH header fields removed (in +// the commit's own hash algorithm). The two signatures sign different bytes +// — the SHA-1 form vs the SHA-256 form of the same logical commit — but +// they share the same removal rule. Payload here is whatever form the +// caller's input bytes are in, with both header fields stripped, so it +// matches whichever of Gpgsig / GpgsigSha256 corresponds to that form. +type CommitSig struct { + // Payload is the commit content with gpgsig and gpgsig-sha256 fields + // (and their continuation lines) removed. + Payload []byte + // Gpgsig is the PEM signature from the gpgsig header. It signs the + // SHA-1 form of the commit. Nil if the header is absent. + Gpgsig []byte + // GpgsigSha256 is the PEM signature from the gpgsig-sha256 header. It + // signs the SHA-256 form of the commit. Nil if the header is absent. + GpgsigSha256 []byte +} + +// TagSig holds signature material extracted from a tag's raw bytes. +// +// Per the SHA-256 transition spec, the in-body PEM block is the signature +// over the tag in its current hash algorithm; the gpgsig and gpgsig-sha256 +// headers are signatures over the alternate-algorithm form. All three sign +// the same shape: tag content with both header fields and the in-body PEM +// block removed. +type TagSig struct { + // Payload is the tag content with both header signature fields and the + // in-body PEM block removed. + Payload []byte + // InBody is the PEM signature appended after the tag message body. It + // signs the tag in its own (current) hash algorithm. Nil if absent. + InBody []byte + // Gpgsig is the PEM signature from the gpgsig header — the + // alternate-algorithm signature when the tag's stored form is SHA-256, + // or unused for a SHA-1 tag. Nil if absent. + Gpgsig []byte + // GpgsigSha256 is the PEM signature from the gpgsig-sha256 header — + // the alternate-algorithm signature when the tag's stored form is + // SHA-1. Nil if absent. + GpgsigSha256 []byte +} + +// SplitCommit parses the raw bytes of a commit object (object-database form, +// without the "commit \0" prefix) into payload + signatures. +// +// It operates purely on the raw bytes, mirroring what git-core feeds to its +// signature verifier, and does not go through go-git's object parser. +// +// The trust-confusion class of attack (GHSA-7rmh-48mx-2vwc) relied on gitsign +// re-encoding through go-git before verification — which normalized away +// duplicate headers and let a signature over the canonical form verify +// against attacker-controlled raw bytes. Verifying directly over the raw +// bytes blocks that: any structural divergence between what was signed and +// what's stored makes the signature fail to verify cryptographically. +// Consequently, SplitCommit doesn't reject merely "weird but git-valid" +// objects (e.g. duplicate tree headers); the signature check below is what +// catches them. The structural things we *do* reject are duplicate gpgsig +// or gpgsig-sha256 headers, because either is ambiguous about which +// signature to extract. +func SplitCommit(r io.Reader) (*CommitSig, error) { + scanner := bufio.NewScanner(r) + + var ( + payloadBuf bytes.Buffer + gpgsigBuf bytes.Buffer + sha256Buf bytes.Buffer + // activeSig points at whichever signature buffer is currently + // accepting continuation lines, or nil if we're in regular-header + // territory. + activeSig *bytes.Buffer + inBody bool + ) + + for scanner.Scan() { + line := scanner.Bytes() + + if inBody { + payloadBuf.Write(line) + payloadBuf.WriteByte('\n') + continue + } + + if len(line) == 0 { + // Blank line terminates the header section. + payloadBuf.WriteByte('\n') + inBody = true + activeSig = nil + continue + } + + if activeSig != nil { + // git-core requires exactly one leading space on a signature + // continuation (see git/commit.c parse_buffer_signed_by_header). + // We accept any leading whitespace and strip it: the signature + // is cryptographically verified downstream, so leniency here + // can't cause trust confusion, and being permissive avoids + // rejecting signatures produced by tooling that wraps with + // slightly different indentation. + if trimmed := bytes.TrimLeftFunc(line, unicode.IsSpace); len(trimmed) < len(line) { + activeSig.Write(trimmed) + activeSig.WriteByte('\n') + continue + } + // Non-continuation line -> signature block ended; fall through + // and process this line as a fresh header. + activeSig = nil + } + + // Note: check the longer prefix first so "gpgsig-sha256 " doesn't + // get misclassified by the "gpgsig " branch. (In practice the two + // don't overlap because the 7th byte differs, but ordering this way + // is robust against accidental future widenings of gpgsigPrefix.) + switch { + case bytes.HasPrefix(line, []byte(gpgsigSha256Prefix)): + if sha256Buf.Len() > 0 { + return nil, fmt.Errorf("%w: duplicate gpgsig-sha256 header", ErrMalformedObject) + } + activeSig = &sha256Buf + activeSig.Write(line[len(gpgsigSha256Prefix):]) + activeSig.WriteByte('\n') + case bytes.HasPrefix(line, []byte(gpgsigPrefix)): + if gpgsigBuf.Len() > 0 { + return nil, fmt.Errorf("%w: duplicate gpgsig header", ErrMalformedObject) + } + activeSig = &gpgsigBuf + activeSig.Write(line[len(gpgsigPrefix):]) + activeSig.WriteByte('\n') + default: + payloadBuf.Write(line) + payloadBuf.WriteByte('\n') + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + + return &CommitSig{ + Payload: payloadBuf.Bytes(), + Gpgsig: sigOrNil(&gpgsigBuf), + GpgsigSha256: sigOrNil(&sha256Buf), + }, nil +} + +// JoinCommit is the inverse of SplitCommit. It re-inserts the gpgsig and +// gpgsig-sha256 headers (those that are non-nil) into c.Payload immediately +// before the blank line separating headers from the message body. +// Continuation lines are indented with a single space, matching git-core's +// wire format. +// +// When both headers are present they are emitted in a fixed order: gpgsig +// first, then gpgsig-sha256. This matches git-core's emission order, so +// objects produced by git-core round-trip byte-for-byte through Split/Join. +func JoinCommit(c *CommitSig) ([]byte, error) { + if len(c.Gpgsig) == 0 && len(c.GpgsigSha256) == 0 { + // No signatures to insert; passthrough. + return c.Payload, nil + } + + hdrEnd := bytes.Index(c.Payload, []byte("\n\n")) + if hdrEnd < 0 { + return nil, fmt.Errorf("%w: payload has no header terminator", ErrMalformedObject) + } + + var hdr bytes.Buffer + if len(c.Gpgsig) > 0 { + writeSigHeader(&hdr, gpgsigPrefix, c.Gpgsig) + } + if len(c.GpgsigSha256) > 0 { + writeSigHeader(&hdr, gpgsigSha256Prefix, c.GpgsigSha256) + } + + out := make([]byte, 0, len(c.Payload)+hdr.Len()) + out = append(out, c.Payload[:hdrEnd+1]...) // include the \n terminating the last header + out = append(out, hdr.Bytes()...) + out = append(out, c.Payload[hdrEnd+1:]...) // the remaining \n + message body + return out, nil +} + +// SplitTag parses the raw bytes of a tag object into payload + signatures. +// Like SplitCommit, it works on raw bytes and does not invoke go-git's +// parser, so any divergence between what was signed and what's stored is +// caught by the cryptographic check downstream rather than by structural +// validation here. +// +// The tag's in-body PEM block (current-algorithm signature) is taken to +// start at the *last* line-anchored "-----BEGIN " marker, matching +// git-core's tag verification path. Anything before it stays in the +// payload, including any earlier PEM-looking lines an attacker might have +// embedded in the message body. Header-style alternate-algorithm +// signatures (gpgsig, gpgsig-sha256) are stripped from the header section +// the same way SplitCommit does. +func SplitTag(r io.Reader) (*TagSig, error) { + scanner := bufio.NewScanner(r) + + var ( + payloadBuf bytes.Buffer + inBodySigBuf bytes.Buffer + gpgsigBuf bytes.Buffer + sha256Buf bytes.Buffer + activeSig *bytes.Buffer + inBody bool + ) + + for scanner.Scan() { + line := scanner.Bytes() + + if inBody { + switch { + case bytes.HasPrefix(line, []byte("-----BEGIN ")): + // New PEM block opens — flush any earlier candidate to + // payload (it wasn't the trailing block) and start fresh. + payloadBuf.Write(inBodySigBuf.Bytes()) + inBodySigBuf.Reset() + inBodySigBuf.Write(line) + inBodySigBuf.WriteByte('\n') + case inBodySigBuf.Len() > 0: + inBodySigBuf.Write(line) + inBodySigBuf.WriteByte('\n') + default: + payloadBuf.Write(line) + payloadBuf.WriteByte('\n') + } + continue + } + + if len(line) == 0 { + payloadBuf.WriteByte('\n') + inBody = true + activeSig = nil + continue + } + + if activeSig != nil { + if trimmed := bytes.TrimLeftFunc(line, unicode.IsSpace); len(trimmed) < len(line) { + activeSig.Write(trimmed) + activeSig.WriteByte('\n') + continue + } + activeSig = nil + } + + switch { + case bytes.HasPrefix(line, []byte(gpgsigSha256Prefix)): + if sha256Buf.Len() > 0 { + return nil, fmt.Errorf("%w: duplicate gpgsig-sha256 header", ErrMalformedObject) + } + activeSig = &sha256Buf + activeSig.Write(line[len(gpgsigSha256Prefix):]) + activeSig.WriteByte('\n') + case bytes.HasPrefix(line, []byte(gpgsigPrefix)): + if gpgsigBuf.Len() > 0 { + return nil, fmt.Errorf("%w: duplicate gpgsig header", ErrMalformedObject) + } + activeSig = &gpgsigBuf + activeSig.Write(line[len(gpgsigPrefix):]) + activeSig.WriteByte('\n') + default: + payloadBuf.Write(line) + payloadBuf.WriteByte('\n') + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + + return &TagSig{ + Payload: payloadBuf.Bytes(), + InBody: sigOrNil(&inBodySigBuf), + Gpgsig: sigOrNil(&gpgsigBuf), + GpgsigSha256: sigOrNil(&sha256Buf), + }, nil +} + +// JoinTag is the inverse of SplitTag. It re-inserts gpgsig / gpgsig-sha256 +// headers into the header section (in fixed order: gpgsig, then +// gpgsig-sha256) and appends the in-body PEM block after the message body. +func JoinTag(t *TagSig) ([]byte, error) { + payload := t.Payload + + if len(t.Gpgsig) > 0 || len(t.GpgsigSha256) > 0 { + hdrEnd := bytes.Index(payload, []byte("\n\n")) + if hdrEnd < 0 { + return nil, fmt.Errorf("%w: payload has no header terminator", ErrMalformedObject) + } + + var hdr bytes.Buffer + if len(t.Gpgsig) > 0 { + writeSigHeader(&hdr, gpgsigPrefix, t.Gpgsig) + } + if len(t.GpgsigSha256) > 0 { + writeSigHeader(&hdr, gpgsigSha256Prefix, t.GpgsigSha256) + } + + out := make([]byte, 0, len(payload)+hdr.Len()+len(t.InBody)) + out = append(out, payload[:hdrEnd+1]...) + out = append(out, hdr.Bytes()...) + out = append(out, payload[hdrEnd+1:]...) + out = append(out, t.InBody...) + return out, nil + } + + out := make([]byte, 0, len(payload)+len(t.InBody)) + out = append(out, payload...) + out = append(out, t.InBody...) + return out, nil +} + +// writeSigHeader emits a single header field of the form +// +// prefix line1\n line2\n line3\n... +// +// matching git-core's wire format. sig is a PEM-encoded signature with +// lines separated by "\n"; a trailing newline is ignored. +func writeSigHeader(w *bytes.Buffer, prefix string, sig []byte) { + s := sig + if len(s) > 0 && s[len(s)-1] == '\n' { + s = s[:len(s)-1] + } + lines := bytes.Split(s, []byte{'\n'}) + + w.WriteString(prefix) + w.Write(lines[0]) + w.WriteByte('\n') + for _, l := range lines[1:] { + w.WriteByte(' ') + w.Write(l) + w.WriteByte('\n') + } +} + +// sigOrNil returns nil for an empty buffer (so absent signatures surface as +// nil rather than zero-length slices). +func sigOrNil(b *bytes.Buffer) []byte { + if b.Len() == 0 { + return nil + } + return b.Bytes() +} + +// commitSingletons / tagSingletons name the headers a well-formed object +// carries at most once. parent is intentionally absent from the commit set +// (merge commits have several); mergetag and encoding are intentionally +// absent because git accepts repetition of mergetag and we don't audit +// encoding for uniqueness. gpgsig / gpgsig-sha256 are included so the +// attest path matches SplitCommit's duplicate-signature rejection on the +// verify path. +var commitSingletons = map[string]bool{ + "tree": true, + "author": true, + "committer": true, + "gpgsig": true, + "gpgsig-sha256": true, +} + +var tagSingletons = map[string]bool{ + "object": true, + "type": true, + "tag": true, + "tagger": true, + "gpgsig": true, + "gpgsig-sha256": true, +} + +// ValidateCommit returns ErrMalformedObject if the commit's header +// section carries a duplicate singleton header (tree, author, committer, +// gpgsig, gpgsig-sha256). Other ambiguities — missing required headers, +// unparseable signatures, bad encodings — are out of scope and surface +// later via go-git's decoder or the cryptographic check. +// +// go-git ≥ v5.19.0 silently drops duplicates and takes the first (matching +// git-core's standard_header_field filter). That's safe but ambiguous — +// `git hash-object --literally` and adversarial pushes are the only ways +// such objects appear, and the attest path would rather refuse than emit +// a predicate that obscures the underlying weirdness. +func ValidateCommit(obj plumbing.EncodedObject) error { + return checkUniqueHeaders(obj, commitSingletons) +} + +// ValidateTag is the tag-object counterpart to ValidateCommit, rejecting +// duplicate object / type / tag / tagger / gpgsig / gpgsig-sha256 headers. +func ValidateTag(obj plumbing.EncodedObject) error { + return checkUniqueHeaders(obj, tagSingletons) +} + +// checkUniqueHeaders walks the header section (up to the first blank +// line) and returns ErrMalformedObject on the second occurrence of any +// key in singletons. Lines beginning with whitespace are treated as +// continuations of the previous header and skipped. Lines without a +// space are skipped (git-core ignores them; downstream parsers surface +// the issue if it matters). +func checkUniqueHeaders(obj plumbing.EncodedObject, singletons map[string]bool) error { + r, err := obj.Reader() + if err != nil { + // Storage-layer failure: surface as-is so callers can see the + // underlying go-git error rather than a generic malformed-object. + return err + } + defer r.Close() // nolint:errcheck + + seen := make(map[string]bool, len(singletons)) + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Bytes() + if len(line) == 0 { + // Blank line terminates the header section; anything after + // is the message body, which is opaque text that may + // legitimately contain "tree foo" etc. Stop here so body + // content doesn't produce false positives. + return nil + } + if line[0] == ' ' || line[0] == '\t' { + // Continuation of the previous header (git wraps long values + // like gpgsig PEM blocks across multiple lines, each prefixed + // with a leading space). Treat as part of the prior header, + // not a new one. + continue + } + key, _, ok := bytes.Cut(line, []byte{' '}) + if !ok { + // Header line with no space separator — git-core's parser + // skips these silently. We do the same; if it actually + // matters, go-git's decoder will fail on it downstream. + continue + } + k := string(key) + if !singletons[k] { + // Header isn't one we track (parent, mergetag, encoding, + // arbitrary extra headers). Duplicates of these are allowed + // or out of scope for this validator. + continue + } + if seen[k] { + // Second occurrence of a singleton — this is the + // trust-confusion-adjacent ambiguity we're rejecting. + return fmt.Errorf("%w: duplicate %s header", ErrMalformedObject, k) + } + seen[k] = true + } + // Reached EOF without seeing a blank line. No duplicates found, so + // validation passes; return any scanner I/O error (nil on success). + return scanner.Err() +} diff --git a/pkg/git/rawobj_test.go b/pkg/git/rawobj_test.go new file mode 100644 index 00000000..cb44787e --- /dev/null +++ b/pkg/git/rawobj_test.go @@ -0,0 +1,725 @@ +// +// Copyright 2026 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package git + +import ( + "bytes" + "errors" + "fmt" + "strings" + "testing" + + "github.com/go-git/go-git/v5/plumbing" + "github.com/google/go-cmp/cmp" +) + +// makeObject wraps raw bytes in a plumbing.MemoryObject of the given +// type, for tests that need to feed Validate{Commit,Tag} something that +// implements plumbing.EncodedObject without spinning up a full storer. +func makeObject(t *testing.T, ot plumbing.ObjectType, raw []byte) plumbing.EncodedObject { + t.Helper() + o := &plumbing.MemoryObject{} + o.SetType(ot) + if _, err := o.Write(raw); err != nil { + t.Fatalf("MemoryObject.Write: %v", err) + } + return o +} + +// fakeSig is a syntactically-valid PEM block used for tests that exercise +// the parser/joiner shape but don't need a real cryptographic signature. +const fakeSig = `-----BEGIN SIGNED MESSAGE----- +ZmFrZXBheWxvYWQ= +-----END SIGNED MESSAGE----- +` + +const fakeSig2 = `-----BEGIN SIGNED MESSAGE----- +b3RoZXJwYXlsb2Fk +-----END SIGNED MESSAGE----- +` + +// sigHeader formats a PEM signature as a wire-format header block: the first +// line preceded by `prefix` (e.g. "gpgsig "), subsequent lines indented by a +// single space. The result ends with a trailing newline so it can be +// concatenated directly into a header section. +func sigHeader(prefix, sig string) string { + sig = strings.TrimSuffix(sig, "\n") + lines := strings.Split(sig, "\n") + return prefix + strings.Join(lines, "\n ") + "\n" +} + +// TestSplitCommit_WellFormed runs against a real signed commit +// (testdata/commit.txt = `git cat-file -p HEAD` from a gitsign-signed commit +// in this repo) and confirms split + join round-trips byte-for-byte. +func TestSplitCommit_WellFormed(t *testing.T) { + raw := loadObject(t, "commit.txt") + + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if !bytes.HasPrefix(c.Payload, []byte("tree ")) { + t.Errorf("payload should start with 'tree ', got %q", firstLine(c.Payload)) + } + if bytes.Contains(c.Payload, []byte("\ngpgsig ")) { + t.Errorf("payload should not contain gpgsig header") + } + if c.GpgsigSha256 != nil { + t.Errorf("expected no gpgsig-sha256 on a SHA-1 only commit, got %q", c.GpgsigSha256) + } + if !bytes.HasPrefix(c.Gpgsig, []byte("-----BEGIN ")) { + t.Errorf("Gpgsig should start with PEM marker, got %q", firstLine(c.Gpgsig)) + } + + rejoined, err := JoinCommit(c) + if err != nil { + t.Fatalf("JoinCommit: %v", err) + } + if diff := cmp.Diff(raw, rejoined); diff != "" { + t.Errorf("round-trip mismatch (-want +got):\n%s", diff) + } +} + +func TestSplitCommit_DuplicateGpgsig(t *testing.T) { + raw := fmt.Appendf(nil, `tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 +%s%s +foo +`, sigHeader(gpgsigPrefix, fakeSig), sigHeader(gpgsigPrefix, fakeSig)) + + _, err := SplitCommit(bytes.NewReader(raw)) + if !errors.Is(err, ErrMalformedObject) { + t.Fatalf("want ErrMalformedObject, got %v", err) + } +} + +func TestSplitCommit_DuplicateGpgsigSha256(t *testing.T) { + raw := fmt.Appendf(nil, `tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 +%s%s +foo +`, sigHeader(gpgsigSha256Prefix, fakeSig), sigHeader(gpgsigSha256Prefix, fakeSig)) + + _, err := SplitCommit(bytes.NewReader(raw)) + if !errors.Is(err, ErrMalformedObject) { + t.Fatalf("want ErrMalformedObject, got %v", err) + } +} + +// TestSplitCommit_DualSigned exercises the SHA-256 transition compat shape +// — a commit carrying both gpgsig and gpgsig-sha256 +// (https://git-scm.com/docs/hash-function-transition#_signed_commits) — and +// confirms each signature is extracted into its own field with both header +// fields stripped from Payload (the spec says either signature's signed +// payload is the commit content with *both* fields removed). +// +// JoinCommit uses a fixed gpgsig-then-gpgsig-sha256 emission order, so the +// "gpgsig-first" subcase round-trips byte-for-byte while the +// "gpgsig-sha256-first" subcase only checks parse correctness. +func TestSplitCommit_DualSigned(t *testing.T) { + gpgsig := sigHeader(gpgsigPrefix, fakeSig) + sha256Sig := sigHeader(gpgsigSha256Prefix, fakeSig2) + + for _, tc := range []struct { + name string + first string + second string + roundTrip bool + }{ + {"gpgsig-first", gpgsig, sha256Sig, true}, + {"gpgsig-sha256-first", sha256Sig, gpgsig, false}, + } { + t.Run(tc.name, func(t *testing.T) { + raw := fmt.Appendf(nil, `tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 +%s%s +dual-signed +`, tc.first, tc.second) + + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if diff := cmp.Diff([]byte(fakeSig), c.Gpgsig); diff != "" { + t.Errorf("Gpgsig mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff([]byte(fakeSig2), c.GpgsigSha256); diff != "" { + t.Errorf("GpgsigSha256 mismatch (-want +got):\n%s", diff) + } + if bytes.Contains(c.Payload, []byte("\ngpgsig ")) { + t.Errorf("payload should not contain gpgsig header") + } + if bytes.Contains(c.Payload, []byte("\ngpgsig-sha256 ")) { + t.Errorf("payload should not contain gpgsig-sha256 header") + } + + if tc.roundTrip { + rejoined, err := JoinCommit(c) + if err != nil { + t.Fatalf("JoinCommit: %v", err) + } + if diff := cmp.Diff(raw, rejoined); diff != "" { + t.Errorf("round-trip mismatch (-want +got):\n%s", diff) + } + } + }) + } +} + +// TestSplitCommit_Sha256Only covers mode 3 of the transition spec: a commit +// signed only with gpgsig-sha256 (no gpgsig). gitsign should extract the +// signature and report Gpgsig as nil. +func TestSplitCommit_Sha256Only(t *testing.T) { + raw := fmt.Appendf(nil, `tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 +%s +sha256 only +`, sigHeader(gpgsigSha256Prefix, fakeSig)) + + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if c.Gpgsig != nil { + t.Errorf("expected Gpgsig nil, got %q", c.Gpgsig) + } + if diff := cmp.Diff([]byte(fakeSig), c.GpgsigSha256); diff != "" { + t.Errorf("GpgsigSha256 mismatch (-want +got):\n%s", diff) + } + + rejoined, err := JoinCommit(c) + if err != nil { + t.Fatalf("JoinCommit: %v", err) + } + if diff := cmp.Diff(raw, rejoined); diff != "" { + t.Errorf("round-trip mismatch (-want +got):\n%s", diff) + } +} + +// TestSplitCommit_GpgsigInBody confirms that gpgsig-looking lines in the +// commit message body are treated as plain text, not as a second gpgsig +// header. Once the blank line ends the header section, prefix detection and +// duplicate-header rejection are off — body content is opaque to the +// parser. +func TestSplitCommit_GpgsigInBody(t *testing.T) { + raw := fmt.Appendf(nil, `tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 +%s +This commit message has lines that look like gpgsig headers: +gpgsig pretend-sig-here +gpgsig-sha256 pretend-other-sig + indented-continuation-shaped-line +gpgsig second-fake +`, sigHeader(gpgsigPrefix, fakeSig)) + + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if diff := cmp.Diff([]byte(fakeSig), c.Gpgsig); diff != "" { + t.Errorf("Gpgsig mismatch (-want +got):\n%s", diff) + } + if c.GpgsigSha256 != nil { + t.Errorf("expected GpgsigSha256 nil, got %q", c.GpgsigSha256) + } + for _, want := range []string{ + "gpgsig pretend-sig-here", + "gpgsig-sha256 pretend-other-sig", + "gpgsig second-fake", + } { + if !bytes.Contains(c.Payload, []byte(want)) { + t.Errorf("body line %q should remain in payload", want) + } + } +} + +// TestSplitTag_GpgsigInBody confirms that gpgsig-looking lines in the tag +// message body are treated as plain text, not as duplicate header +// signatures. +func TestSplitTag_GpgsigInBody(t *testing.T) { + raw := fmt.Appendf(nil, `object 040b9af339e69d18848b7bbe05cb27ee42bb0161 +type commit +tag body-noise +tagger Alice 1700000000 +0000 +%s +Tag message with gpgsig-looking text: +gpgsig pretend-sig-here +gpgsig-sha256 pretend-other-sig + +%s`, sigHeader(gpgsigPrefix, fakeSig), fakeSig) + + tag, err := SplitTag(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitTag: %v", err) + } + if diff := cmp.Diff([]byte(fakeSig), tag.Gpgsig); diff != "" { + t.Errorf("Gpgsig mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff([]byte(fakeSig), tag.InBody); diff != "" { + t.Errorf("InBody mismatch (-want +got):\n%s", diff) + } + for _, want := range []string{ + "gpgsig pretend-sig-here", + "gpgsig-sha256 pretend-other-sig", + } { + if !bytes.Contains(tag.Payload, []byte(want)) { + t.Errorf("body line %q should remain in payload", want) + } + } +} + +// TestSplitCommit_GpgsigPrefixedHeaders confirms unrelated headers that share +// the "gpgsig" prefix (e.g. a hypothetical "gpgsig-key-id" extension) don't +// trip the duplicate-gpgsig check or get extracted as a signature: the +// trailing space in gpgsigPrefix / gpgsigSha256Prefix distinguishes them +// from "gpgsig-*". +func TestSplitCommit_GpgsigPrefixedHeaders(t *testing.T) { + raw := fmt.Appendf(nil, `tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 +%sgpgsig-key-id ABCDEF0123456789 +gpgsig-fingerprint 0011223344556677 + +foo +`, sigHeader(gpgsigPrefix, fakeSig)) + + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if diff := cmp.Diff([]byte(fakeSig), c.Gpgsig); diff != "" { + t.Errorf("Gpgsig mismatch (-want +got):\n%s", diff) + } + if c.GpgsigSha256 != nil { + t.Errorf("expected GpgsigSha256 nil, got %q", c.GpgsigSha256) + } + for _, want := range []string{"gpgsig-key-id ABCDEF0123456789", "gpgsig-fingerprint 0011223344556677"} { + if !bytes.Contains(c.Payload, []byte(want)) { + t.Errorf("payload should contain %q", want) + } + } +} + +// TestSplitCommit_MergeCommit confirms we don't over-reject: commits with +// multiple parent headers (merge commits) are valid and must parse cleanly. +func TestSplitCommit_MergeCommit(t *testing.T) { + raw := fmt.Appendf(nil, `tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +parent 2dc0ab59d7f0a7a62423bd181d9e2ab3adb7b56d +parent 1111111111111111111111111111111111111111 +parent 2222222222222222222222222222222222222222 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 +%s +merge commit +`, sigHeader(gpgsigPrefix, fakeSig)) + + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + for _, p := range []string{ + "parent 2dc0ab59d7f0a7a62423bd181d9e2ab3adb7b56d\n", + "parent 1111111111111111111111111111111111111111\n", + "parent 2222222222222222222222222222222222222222\n", + } { + if !bytes.Contains(c.Payload, []byte(p)) { + t.Errorf("payload missing expected parent line %q", p) + } + } + if diff := cmp.Diff([]byte(fakeSig), c.Gpgsig); diff != "" { + t.Errorf("Gpgsig mismatch (-want +got):\n%s", diff) + } +} + +// TestSplitCommit_NoSignature documents that an unsigned commit splits without +// error: the entire input becomes payload and both signature fields are nil. +// The downstream PEM decode + cryptographic check is what surfaces the +// missing signature to the user. +func TestSplitCommit_NoSignature(t *testing.T) { + raw := []byte(`tree b333504b8cf3d9c314fed2cc242c5c38e89534a5 +author Alice 1700000000 +0000 +committer Alice 1700000000 +0000 + +unsigned commit +`) + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if c.Gpgsig != nil || c.GpgsigSha256 != nil { + t.Errorf("expected both signature fields nil, got Gpgsig=%q GpgsigSha256=%q", c.Gpgsig, c.GpgsigSha256) + } + if diff := cmp.Diff(raw, c.Payload); diff != "" { + t.Errorf("payload should equal input (-want +got):\n%s", diff) + } +} + +// TestSplitCommit_NoHeaderTerminator documents that a malformed input with no +// blank line terminating the headers is not rejected — the bytes flow through +// to the verifier, which will reject them as not matching anything that was +// signed. +func TestSplitCommit_NoHeaderTerminator(t *testing.T) { + raw := []byte("tree b333504b8cf3d9c314fed2cc242c5c38e89534a5\n") + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + if c.Gpgsig != nil || c.GpgsigSha256 != nil { + t.Errorf("expected no signatures, got Gpgsig=%q GpgsigSha256=%q", c.Gpgsig, c.GpgsigSha256) + } + if diff := cmp.Diff(raw, c.Payload); diff != "" { + t.Errorf("payload should equal input (-want +got):\n%s", diff) + } +} + +// TestSplitCommit_LenientContinuation confirms gpgsig continuation lines with +// extra leading whitespace (or tabs) are accepted, with all leading whitespace +// stripped. git-core requires exactly one space, but the recovered signature +// is cryptographically verified downstream so leniency here is safe. +func TestSplitCommit_LenientContinuation(t *testing.T) { + raw := []byte("tree b333504b8cf3d9c314fed2cc242c5c38e89534a5\n" + + "author Alice 1700000000 +0000\n" + + "committer Alice 1700000000 +0000\n" + + "gpgsig -----BEGIN SIGNED MESSAGE-----\n" + + " two-space-continuation\n" + + "\textra-tab\n" + + " -----END SIGNED MESSAGE-----\n" + + "\n" + + "foo\n") + + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + want := "-----BEGIN SIGNED MESSAGE-----\ntwo-space-continuation\nextra-tab\n-----END SIGNED MESSAGE-----\n" + if diff := cmp.Diff([]byte(want), c.Gpgsig); diff != "" { + t.Errorf("Gpgsig mismatch (-want +got):\n%s", diff) + } +} + +// TestJoinCommit_RoundTrip uses the real signed HEAD commit: split it, join +// it back, and confirm the bytes are identical. +func TestJoinCommit_RoundTrip(t *testing.T) { + raw := loadObject(t, "commit.txt") + c, err := SplitCommit(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitCommit: %v", err) + } + rejoined, err := JoinCommit(c) + if err != nil { + t.Fatalf("JoinCommit: %v", err) + } + if diff := cmp.Diff(raw, rejoined); diff != "" { + t.Errorf("round-trip mismatch (-want +got):\n%s", diff) + } +} + +func TestJoinCommit_NoHeaderTerminator(t *testing.T) { + _, err := JoinCommit(&CommitSig{Payload: []byte("not-a-commit"), Gpgsig: []byte("sig")}) + if !errors.Is(err, ErrMalformedObject) { + t.Fatalf("want ErrMalformedObject, got %v", err) + } +} + +// TestJoinCommit_PassthroughNoSig documents that JoinCommit on a CommitSig +// with no signatures returns the payload unchanged — useful for callers that +// want a single code path regardless of whether the input was signed. +func TestJoinCommit_PassthroughNoSig(t *testing.T) { + in := []byte("not even a real commit, no terminator") + out, err := JoinCommit(&CommitSig{Payload: in}) + if err != nil { + t.Fatalf("JoinCommit: %v", err) + } + if diff := cmp.Diff(in, out); diff != "" { + t.Errorf("passthrough mismatch (-want +got):\n%s", diff) + } +} + +// TestSplitTag_WellFormed runs against a real signed tag (testdata/tag.txt = +// `git cat-file -p v0.1.0`) and confirms split + join round-trips +// byte-for-byte. +func TestSplitTag_WellFormed(t *testing.T) { + raw := loadObject(t, "tag.txt") + + tag, err := SplitTag(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitTag: %v", err) + } + if !bytes.HasPrefix(tag.Payload, []byte("object ")) { + t.Errorf("payload should start with 'object ', got %q", firstLine(tag.Payload)) + } + if !bytes.HasPrefix(tag.InBody, []byte("-----BEGIN ")) { + t.Errorf("InBody should start with PEM marker, got %q", firstLine(tag.InBody)) + } + if tag.Gpgsig != nil || tag.GpgsigSha256 != nil { + t.Errorf("expected no header sigs, got Gpgsig=%q GpgsigSha256=%q", tag.Gpgsig, tag.GpgsigSha256) + } + + rejoined, err := JoinTag(tag) + if err != nil { + t.Fatalf("JoinTag: %v", err) + } + if diff := cmp.Diff(raw, rejoined); diff != "" { + t.Errorf("round-trip mismatch (-want +got):\n%s", diff) + } +} + +// TestSplitTag_DualSigned exercises a tag with all three signature forms: +// the in-body PEM block (current-algorithm signature) plus gpgsig and +// gpgsig-sha256 headers (alternate-algorithm signatures, per the SHA-256 +// transition spec). All three must be extracted into their own fields with +// the payload stripped of all three. +func TestSplitTag_DualSigned(t *testing.T) { + raw := fmt.Appendf(nil, `object 040b9af339e69d18848b7bbe05cb27ee42bb0161 +type commit +tag dual-signed +tagger Alice 1700000000 +0000 +%s%s +dual signed tag + +%s`, + sigHeader(gpgsigPrefix, fakeSig), + sigHeader(gpgsigSha256Prefix, fakeSig2), + fakeSig) + + tag, err := SplitTag(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitTag: %v", err) + } + if diff := cmp.Diff([]byte(fakeSig), tag.InBody); diff != "" { + t.Errorf("InBody mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff([]byte(fakeSig), tag.Gpgsig); diff != "" { + t.Errorf("Gpgsig mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff([]byte(fakeSig2), tag.GpgsigSha256); diff != "" { + t.Errorf("GpgsigSha256 mismatch (-want +got):\n%s", diff) + } + if bytes.Contains(tag.Payload, []byte("\ngpgsig ")) { + t.Errorf("payload should not contain gpgsig header") + } + if bytes.Contains(tag.Payload, []byte("\ngpgsig-sha256 ")) { + t.Errorf("payload should not contain gpgsig-sha256 header") + } + + rejoined, err := JoinTag(tag) + if err != nil { + t.Fatalf("JoinTag: %v", err) + } + if diff := cmp.Diff(raw, rejoined); diff != "" { + t.Errorf("round-trip mismatch (-want +got):\n%s", diff) + } +} + +// TestSplitTag_MultipleSignatureBlocks confirms that when a tag body contains +// more than one "-----BEGIN " block — e.g. an attacker concatenates a +// fake/canonical signature into the message and appends their own signature +// at the end — only the *last* block is treated as the signature, matching +// git-core's tag verification path. Anything before the last block (including +// any earlier PEM-looking content) is part of the signed payload, so a +// signature over a different earlier block doesn't get treated as "the" +// signature. +func TestSplitTag_MultipleSignatureBlocks(t *testing.T) { + raw := []byte(`object 040b9af339e69d18848b7bbe05cb27ee42bb0161 +type commit +tag multi-pem +tagger Alice 1700000000 +0000 + +embedded earlier block in the message body: +-----BEGIN SIGNED MESSAGE----- +ZmFrZQ== +-----END SIGNED MESSAGE----- +-----BEGIN SIGNED MESSAGE----- +cmVhbA== +-----END SIGNED MESSAGE----- +`) + + tag, err := SplitTag(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitTag: %v", err) + } + + wantSig := []byte(`-----BEGIN SIGNED MESSAGE----- +cmVhbA== +-----END SIGNED MESSAGE----- +`) + if diff := cmp.Diff(wantSig, tag.InBody); diff != "" { + t.Errorf("InBody mismatch (-want +got):\n%s", diff) + } + + // The earlier PEM block must remain in the payload — it's part of what + // was signed, not the signature itself. + if !bytes.Contains(tag.Payload, []byte("ZmFrZQ==")) { + t.Errorf("payload should contain the earlier embedded PEM block") + } + if bytes.Contains(tag.Payload, []byte("cmVhbA==")) { + t.Errorf("payload should not contain the trailing signature payload") + } + + rejoined, err := JoinTag(tag) + if err != nil { + t.Fatalf("JoinTag: %v", err) + } + if diff := cmp.Diff(raw, rejoined); diff != "" { + t.Errorf("round-trip mismatch (-want +got):\n%s", diff) + } +} + +// TestSplitTag_NoSignature documents that an unsigned tag splits without +// error: payload holds the input and all signature fields are nil. +func TestSplitTag_NoSignature(t *testing.T) { + raw := []byte(`object 040b9af339e69d18848b7bbe05cb27ee42bb0161 +type commit +tag unsigned +tagger Alice 1700000000 +0000 + +unsigned tag +`) + tag, err := SplitTag(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitTag: %v", err) + } + if tag.InBody != nil || tag.Gpgsig != nil || tag.GpgsigSha256 != nil { + t.Errorf("expected all signatures nil, got InBody=%q Gpgsig=%q GpgsigSha256=%q", + tag.InBody, tag.Gpgsig, tag.GpgsigSha256) + } + if diff := cmp.Diff(raw, tag.Payload); diff != "" { + t.Errorf("payload should equal input (-want +got):\n%s", diff) + } +} + +// TestSplitTag_NoHeaderTerminator documents that a tag with no blank line is +// not rejected — the verifier downstream catches the mismatch. +func TestSplitTag_NoHeaderTerminator(t *testing.T) { + raw := []byte("object 040b9af339e69d18848b7bbe05cb27ee42bb0161\n") + tag, err := SplitTag(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitTag: %v", err) + } + if tag.InBody != nil || tag.Gpgsig != nil || tag.GpgsigSha256 != nil { + t.Errorf("expected all signatures nil, got InBody=%q Gpgsig=%q GpgsigSha256=%q", + tag.InBody, tag.Gpgsig, tag.GpgsigSha256) + } + if diff := cmp.Diff(raw, tag.Payload); diff != "" { + t.Errorf("payload should equal input (-want +got):\n%s", diff) + } +} + +func TestJoinTag_RoundTrip(t *testing.T) { + raw := loadObject(t, "tag.txt") + tag, err := SplitTag(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("SplitTag: %v", err) + } + rejoined, err := JoinTag(tag) + if err != nil { + t.Fatalf("JoinTag: %v", err) + } + if diff := cmp.Diff(raw, rejoined); diff != "" { + t.Errorf("round-trip mismatch (-want +got):\n%s", diff) + } +} + +// firstLine returns the first line of b for use in error messages. +func firstLine(b []byte) []byte { + first, _, _ := bytes.Cut(b, []byte{'\n'}) + return first +} + +func TestValidateCommit(t *testing.T) { + authorLine := "author Alice 1700000000 +0000\n" + commLine := "committer Alice 1700000000 +0000\n" + base := "tree b333504b8cf3d9c314fed2cc242c5c38e89534a5\n" + authorLine + commLine + + tcs := []struct { + name string + raw string + wantErr bool + }{ + {"well-formed", base + "\nmsg\n", false}, + {"merge commit with multiple parents", "tree aaaa\nparent bbbb\nparent cccc\n" + authorLine + commLine + "\nmsg\n", false}, + {"duplicate tree", "tree aaaa\ntree bbbb\n" + authorLine + commLine + "\nmsg\n", true}, + {"duplicate author", "tree aaaa\n" + authorLine + authorLine + commLine + "\nmsg\n", true}, + {"duplicate committer", "tree aaaa\n" + authorLine + commLine + commLine + "\nmsg\n", true}, + {"duplicate gpgsig", base + "gpgsig sig1\ngpgsig sig2\n\nmsg\n", true}, + {"duplicate gpgsig-sha256", base + "gpgsig-sha256 sig1\ngpgsig-sha256 sig2\n\nmsg\n", true}, + {"gpgsig with continuation lines does not count as duplicate", base + "gpgsig line1\n line2\n line3\n\nmsg\n", false}, + {"duplicate headers in message body are ignored", base + "\ntree fake\ntree alsofake\n", false}, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + err := ValidateCommit(makeObject(t, plumbing.CommitObject, []byte(tc.raw))) + gotErr := err != nil + if gotErr != tc.wantErr { + t.Fatalf("err: got %v, want error=%v", err, tc.wantErr) + } + if tc.wantErr && !errors.Is(err, ErrMalformedObject) { + t.Errorf("err: got %v, want ErrMalformedObject", err) + } + }) + } +} + +func TestValidateTag(t *testing.T) { + taggerLine := "tagger Alice 1700000000 +0000\n" + base := "object 2d9cff2bad7132c586e128bcc23322dbb5297e8e\ntype commit\ntag v1\n" + taggerLine + + tcs := []struct { + name string + raw string + wantErr bool + }{ + {"well-formed", base + "\nmsg\n", false}, + {"duplicate object", "object aaaa\nobject bbbb\ntype commit\ntag v1\n" + taggerLine + "\nmsg\n", true}, + {"duplicate type", "object aaaa\ntype commit\ntype tag\ntag v1\n" + taggerLine + "\nmsg\n", true}, + {"duplicate tag", "object aaaa\ntype commit\ntag v1\ntag v2\n" + taggerLine + "\nmsg\n", true}, + {"duplicate tagger", "object aaaa\ntype commit\ntag v1\n" + taggerLine + taggerLine + "\nmsg\n", true}, + {"duplicate headers in message body are ignored", base + "\nobject fake\nobject alsofake\n", false}, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + err := ValidateTag(makeObject(t, plumbing.TagObject, []byte(tc.raw))) + gotErr := err != nil + if gotErr != tc.wantErr { + t.Fatalf("err: got %v, want error=%v", err, tc.wantErr) + } + if tc.wantErr && !errors.Is(err, ErrMalformedObject) { + t.Errorf("err: got %v, want ErrMalformedObject", err) + } + }) + } +} + +// TestValidateCommit_RealCommit confirms the validator accepts a real signed +// commit with gpgsig continuation lines. +func TestValidateCommit_RealCommit(t *testing.T) { + raw := loadObject(t, "commit.txt") + if err := ValidateCommit(makeObject(t, plumbing.CommitObject, raw)); err != nil { + t.Errorf("ValidateCommit on real commit: %v", err) + } +} + +func TestValidateTag_RealTag(t *testing.T) { + raw := loadObject(t, "tag.txt") + if err := ValidateTag(makeObject(t, plumbing.TagObject, raw)); err != nil { + t.Errorf("ValidateTag on real tag: %v", err) + } +} diff --git a/pkg/git/signature_test.go b/pkg/git/signature_test.go index 0dd37a11..f9b4c53b 100644 --- a/pkg/git/signature_test.go +++ b/pkg/git/signature_test.go @@ -22,9 +22,12 @@ import ( "fmt" "os" "path/filepath" + "strings" "testing" + "time" "github.com/github/smimesign/fakeca" + cms "github.com/sigstore/gitsign/internal/fork/ietf-cms" "github.com/sigstore/gitsign/internal/signature" "github.com/sigstore/sigstore/pkg/cryptoutils" ) @@ -101,3 +104,169 @@ func TestSignVerify(t *testing.T) { }) } } + +// TestVerifyReturnsSignerCert ensures that Verify returns the certificate that +// actually authenticated the signature (resolved via SignerInfo) rather than +// whatever certificate happens to be at position 0 in the PKCS7 cert bag. An +// attacker who controls the SignedData can put a decoy cert at position 0 +// while signing with a different one; the caller must not be handed back the +// decoy. See CVE-2026-39984 for the equivalent issue in timestamp-authority. +func TestVerifyReturnsSignerCert(t *testing.T) { + ctx := context.Background() + ca := fakeca.New(fakeca.IsCA) + // Issue the decoy first so it gets the lower serial — the ASN.1 SET + // encoding of the PKCS7 cert bag sorts by canonical DER, and within this + // fake CA the lower-serial cert lands at position 0 after a roundtrip. + decoy := ca.Issue() + signer := ca.Issue() + roots := x509.NewCertPool() + roots.AddCert(ca.Certificate) + data := []byte("tacocat") + + for _, detached := range []bool{true, false} { + t.Run(fmt.Sprintf("detached(%t)", detached), func(t *testing.T) { + sd, err := cms.NewSignedData(data) + if err != nil { + t.Fatalf("NewSignedData() = %v", err) + } + if err := sd.Sign([]*x509.Certificate{signer.Certificate}, signer.PrivateKey); err != nil { + t.Fatalf("Sign() = %v", err) + } + // Replace the cert bag with [decoy, signer] — the decoy is what an + // attacker would inject as certs[0] to confuse callers. + if err := sd.SetCertificates([]*x509.Certificate{decoy.Certificate, signer.Certificate}); err != nil { + t.Fatalf("SetCertificates() = %v", err) + } + if detached { + sd.Detached() + } + der, err := sd.ToDER() + if err != nil { + t.Fatalf("ToDER() = %v", err) + } + + // Sanity check: confirm the attack setup landed the decoy at certs[0]. + // If this ever stops being true (e.g. ASN.1 SET sorting changes), the + // test is no longer exercising the vulnerability and needs updating. + parsed, err := cms.ParseSignedData(der) + if err != nil { + t.Fatalf("ParseSignedData() = %v", err) + } + certs, err := parsed.GetCertificates() + if err != nil { + t.Fatalf("GetCertificates() = %v", err) + } + if !certs[0].Equal(decoy.Certificate) { + t.Fatalf("attack setup failed: certs[0] is not the decoy") + } + + cv, err := NewCertVerifier(WithRootPool(roots)) + if err != nil { + t.Fatal(err) + } + got, err := cv.Verify(ctx, data, der, detached) + if err != nil { + t.Fatalf("Verify() = %v", err) + } + if got.Equal(decoy.Certificate) { + t.Errorf("Verify() returned the decoy certificate; signer cert authentication was bypassed") + } + if !got.Equal(signer.Certificate) { + t.Errorf("Verify() returned cert with serial %v, want signer serial %v", + got.SerialNumber, signer.Certificate.SerialNumber) + } + }) + } +} + +// TestVerifyMultiSignerDifferentWindows ensures Verify succeeds for a PKCS7 +// containing multiple SignerInfos whose certs have non-overlapping validity +// windows. With a single shared CurrentTime, one of the chain verifications +// would always fail; the internal verifier must derive a per-cert time so +// that each SignerInfo is checked against a time within its own window. +func TestVerifyMultiSignerDifferentWindows(t *testing.T) { + ctx := context.Background() + ca := fakeca.New(fakeca.IsCA) + + // Issue two signers with deliberately non-overlapping validity windows. + now := time.Now() + oldSigner := ca.Issue( + fakeca.NotBefore(now.Add(-48*time.Hour)), + fakeca.NotAfter(now.Add(-24*time.Hour)), + ) + newSigner := ca.Issue( + fakeca.NotBefore(now.Add(24*time.Hour)), + fakeca.NotAfter(now.Add(48*time.Hour)), + ) + roots := x509.NewCertPool() + roots.AddCert(ca.Certificate) + data := []byte("tacocat") + + sd, err := cms.NewSignedData(data) + if err != nil { + t.Fatalf("NewSignedData() = %v", err) + } + if err := sd.Sign([]*x509.Certificate{oldSigner.Certificate}, oldSigner.PrivateKey); err != nil { + t.Fatalf("Sign() old = %v", err) + } + if err := sd.Sign([]*x509.Certificate{newSigner.Certificate}, newSigner.PrivateKey); err != nil { + t.Fatalf("Sign() new = %v", err) + } + der, err := sd.ToDER() + if err != nil { + t.Fatalf("ToDER() = %v", err) + } + + cv, err := NewCertVerifier(WithRootPool(roots)) + if err != nil { + t.Fatal(err) + } + if _, err := cv.Verify(ctx, data, der, false); err != nil { + t.Fatalf("Verify() = %v; want success despite non-overlapping cert windows", err) + } +} + +// TestVerifyNoCerts ensures that Verify returns an error (rather than panicking +// on an out-of-bounds index) when the parsed signature contains no +// certificates. +func TestVerifyNoCerts(t *testing.T) { + ctx := context.Background() + data := []byte("tacocat") + + ca := fakeca.New() + sd, err := cms.NewSignedData(data) + if err != nil { + t.Fatalf("NewSignedData() = %v", err) + } + if err := sd.Sign([]*x509.Certificate{ca.Certificate}, ca.PrivateKey); err != nil { + t.Fatalf("Sign() = %v", err) + } + // Strip the certificates so GetCertificates returns an empty slice. + if err := sd.SetCertificates(nil); err != nil { + t.Fatalf("SetCertificates() = %v", err) + } + der, err := sd.ToDER() + if err != nil { + t.Fatalf("ToDER() = %v", err) + } + + cv, err := NewCertVerifier(WithRootPool(x509.NewCertPool())) + if err != nil { + t.Fatal(err) + } + + for _, detached := range []bool{true, false} { + t.Run(fmt.Sprintf("detached(%t)", detached), func(t *testing.T) { + cert, err := cv.Verify(ctx, data, der, detached) + if err == nil { + t.Fatalf("Verify() expected error, got cert %v", cert) + } + if !strings.Contains(err.Error(), "no certificates") { + t.Errorf("Verify() error = %v, want error containing %q", err, "no certificates") + } + if cert != nil { + t.Errorf("Verify() cert = %v, want nil", cert) + } + }) + } +} diff --git a/pkg/git/testdata/commit.txt b/pkg/git/testdata/commit.txt new file mode 100644 index 00000000..568d3297 --- /dev/null +++ b/pkg/git/testdata/commit.txt @@ -0,0 +1,45 @@ +tree 3cced1b431769cf16b530b25162e23bb7545d79c +parent 4638d83b00e1119b9aab39ff747802a8ff63f530 +author Billy Lynch 1777409421 -0400 +committer Billy Lynch 1777414757 -0400 +gpgsig -----BEGIN SIGNED MESSAGE----- + MIIEMQYJKoZIhvcNAQcCoIIEIjCCBB4CAQExDTALBglghkgBZQMEAgEwCwYJKoZI + hvcNAQcBoIIC0TCCAs0wggJToAMCAQICFFXZxLeN2TVVA+zJekvjaHqjkE8sMAoG + CCqGSM49BAMDMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2ln + c3RvcmUtaW50ZXJtZWRpYXRlMB4XDTI2MDQyODIyMTkxOVoXDTI2MDQyODIyMjkx + OVowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLSqaKRhP2YdGaAny/Q06etl + lnRenHZ3V/dd/Qrs+dFBql61ykcJxMkBIGVdlwNFBsq/bVdnT4SmLh4phchc5MOj + ggFyMIIBbjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYD + VR0OBBYEFH8O61w2Zecmc+V/maJiTyqSSK0wMB8GA1UdIwQYMBaAFN/T6c9WJBGW + +ajY6ShVosYuGGQ/MCIGA1UdEQEB/wQYMBaBFGJpbGx5QGNoYWluZ3VhcmQuZGV2 + MCkGCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgor + BgEEAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYB + BAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6O + AAABndYs5ecAAAQDAEcwRQIgVr9D9jTvN6V/OHTju7uZ3ozQYzh42nvp1snRT6k9 + hRACIQD3cTr0I32kPAadwtikj8uYZpkqzOPvEAd4/HEy7HLe6zAKBggqhkjOPQQD + AwNoADBlAjAE+izbGwhBz67CE0Z+li7g/CMLfj+vvQHov+r759DHMxFAAZ0Fyz8d + VUhx3uGDwYYCMQDKRy/yLx4F5+TrvEXISSCXOEGi36eR7zwGeyX8iEh3HhuipnOt + 934wxIYdjzc+qxExggEmMIIBIgIBATBPMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRl + djEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlAhRV2cS3jdk1VQPsyXpL + 42h6o5BPLDALBglghkgBZQMEAgGgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB + MBwGCSqGSIb3DQEJBTEPFw0yNjA0MjgyMjE5MTlaMC8GCSqGSIb3DQEJBDEiBCCF + Ehl2z0/PP3PtI7l3rt+50IjOaduv6YSaac3A1bNbmDAKBggqhkjOPQQDAgRIMEYC + IQDvpk8hV5WbBr/MbdDlSV58HrlV28ByANAboL82/KkK+AIhAJZ6CCS13PQS500u + AndjllmzU6kBCqWygwascQ73CZ9f + -----END SIGNED MESSAGE----- + +Verify against raw git object bytes to prevent parser trust-confusion + +go-git's loose object parser uses last-wins semantics for duplicate +singleton headers (tree, author, committer, etc.), while git-core uses +first-wins. An attacker can craft a commit or tag whose raw bytes hash +to one set of contents under git-core but re-encode through go-git to a +different signed payload, letting a legitimate signature verify against +attacker-controlled bytes. + +Replace the go-git decode + EncodeWithoutSignature path with SplitCommit +and SplitTag, which operate directly on the object-database bytes (the +same bytes git-core feeds its verifier) and reject objects with +structural ambiguities — duplicate singleton headers, duplicate gpgsig, +malformed gpgsig continuations. ObjectHash now reassembles via +JoinCommit/JoinTag so the recorded hash matches git-core. diff --git a/pkg/git/testdata/tag.txt b/pkg/git/testdata/tag.txt new file mode 100644 index 00000000..e13f8d9d --- /dev/null +++ b/pkg/git/testdata/tag.txt @@ -0,0 +1,30 @@ +object 2d9cff2bad7132c586e128bcc23322dbb5297e8e +type commit +tag v0.1.0 +tagger Billy Lynch 1654190833 -0400 + +v0.1.0 +-----BEGIN SIGNED MESSAGE----- +MIIEAQYJKoZIhvcNAQcCoIID8jCCA+4CAQExDTALBglghkgBZQMEAgEwCwYJKoZI +hvcNAQcBoIICozCCAp8wggIloAMCAQICFFjVwi84I8kTNqgZRmYKleVVm8NBMAoG +CCqGSM49BAMDMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2ln +c3RvcmUtaW50ZXJtZWRpYXRlMB4XDTIyMDYwMjE3MjcxN1oXDTIyMDYwMjE3Mzcx +N1owADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIK6JRkNRb0z1H3EmQa9zcgN +KdPblSoo1jw5+khs9nV3FPuxvnyJHlAvM4evIkRSRpw7I4LLYIOPlR7TBpQx7vWj +ggFEMIIBQDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYD +VR0OBBYEFKmLjG22l4wfrfLlDLg2G2sZW5C0MB8GA1UdIwQYMBaAFN/T6c9WJBGW ++ajY6ShVosYuGGQ/MCIGA1UdEQEB/wQYMBaBFGJpbGx5QGNoYWluZ3VhcmQuZGV2 +MCgGCisGAQQBg78wAQEEGmdpdGh1Yi1zaWdzdG9yZS1wcm9kdWN0aW9uMIGKBgor +BgEEAdZ5AgQCBHwEegB4AHYACGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2 +b3IAAAGBJXUQkwAABAMARzBFAiEApoKNdV/X2WpMufae6aoxViXF9h/FQfUPCPUo +15qiMlwCIE+98oDMJZjIJMEp9MtyYbIIhctm/FF7WJ6oV4AseuzDMAoGCCqGSM49 +BAMDA2gAMGUCMQDwONklh8R09WQJy0iJA16Qin6lXjixqNvkKW+1X8i5K2Aft2wZ ++eYZKyF1oMCFy0gCMH6/G9E77FgXiNWJEE559FF8eSjX4UpDCKNeHuiBmcDYSONw +ctsPJ48/40lmNJgWjzGCASQwggEgAgEBME8wNzEVMBMGA1UEChMMc2lnc3RvcmUu +ZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUCFFjVwi84I8kTNqgZ +RmYKleVVm8NBMAsGCWCGSAFlAwQCAaBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0B +BwEwHAYJKoZIhvcNAQkFMQ8XDTIyMDYwMjE3MjcxOFowLwYJKoZIhvcNAQkEMSIE +IJS1yaKNQbN+VIRZ8gHWI9+cLWx39QlpBM4T3ILbSsNBMAoGCCqGSM49BAMCBEYw +RAIgOFDlbTSxHflbq/IS73TWvGgUr7ieO1dlljIALkNbJ5UCIEG9TS7F+N1UdBin +jFqU99hj1obCogXt2+rwd/+8IyYt +-----END SIGNED MESSAGE----- diff --git a/pkg/git/verifier.go b/pkg/git/verifier.go index 1eab8885..d76672be 100644 --- a/pkg/git/verifier.go +++ b/pkg/git/verifier.go @@ -20,7 +20,6 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "time" cms "github.com/sigstore/gitsign/internal/fork/ietf-cms" "github.com/sigstore/gitsign/internal/fulcio/fulcioroots" @@ -106,20 +105,24 @@ func (v *CertVerifier) Verify(_ context.Context, data, sig []byte, detached bool return nil, fmt.Errorf("failed to parse signature: %w", err) } - // Generate verification options. - certs, err := sd.GetCertificates() - if err != nil { + // Fail fast with a clear error if the cert bag is empty — otherwise the + // internal verifier's per-SignerInfo FindCertificate error is what the + // caller sees, and the message is less obvious. + if certs, err := sd.GetCertificates(); err != nil { return nil, fmt.Errorf("error getting signature certs: %w", err) + } else if len(certs) == 0 { + return nil, fmt.Errorf("no certificates found in signature") } - cert := certs[0] opts := x509.VerifyOptions{ Roots: v.roots, Intermediates: v.intermediates, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, - // cosign hack: ignore the current time for now - we'll use the tlog to - // verify whether the commit was signed at a valid time. - CurrentTime: cert.NotBefore.Add(1 * time.Minute), + // Leave CurrentTime zero. The internal CMS verifier picks a per-cert + // time inside its SignerInfos loop (timestamp if present, else the + // cert's own NotBefore + 1min), so each SignerInfo is checked against + // a time that lies within its own validity window. Actual signing + // time is verified independently via Rekor. } tsaOpts := x509.VerifyOptions{ @@ -129,17 +132,26 @@ func (v *CertVerifier) Verify(_ context.Context, data, sig []byte, detached bool tsaOpts.Roots = v.tsa } + var chains [][][]*x509.Certificate if detached { - if _, err := sd.VerifyDetached(data, opts, tsaOpts); err != nil { + chains, err = sd.VerifyDetached(data, opts, tsaOpts) + if err != nil { return nil, fmt.Errorf("failed to verify detached signature: %w", err) } } else { - if _, err := sd.Verify(opts, tsaOpts); err != nil { + chains, err = sd.Verify(opts, tsaOpts) + if err != nil { return nil, fmt.Errorf("failed to verify attached signature: %w", err) } } - return cert, nil + // Return the leaf of the first verified chain — this is the certificate + // the internal CMS verifier actually authenticated the signature against, + // not whatever happens to sit at certs[0]. + if len(chains) == 0 || len(chains[0]) == 0 || len(chains[0][0]) == 0 { + return nil, fmt.Errorf("no verified certificate chains returned") + } + return chains[0][0][0], nil } // NewDefaultVerifier returns a new CertVerifier with the default Fulcio roots loaded from the local TUF client. diff --git a/pkg/git/verify.go b/pkg/git/verify.go index ce627da9..00618832 100644 --- a/pkg/git/verify.go +++ b/pkg/git/verify.go @@ -23,7 +23,6 @@ import ( "fmt" "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/object" "github.com/sigstore/gitsign/pkg/rekor" "github.com/sigstore/rekor/pkg/generated/models" ) @@ -82,8 +81,25 @@ func Verify(ctx context.Context, git Verifier, rekor rekor.Verifier, data, sig [ }, nil } - // Legacy commit based lookup. - commit, err := ObjectHash(data, sig) + // Legacy rekor lookup: reconstruct the raw object bytes so their hash + // matches what git-core (and the legacy rekor entries) recorded. We + // assume sig is the SHA-1-form signature (gpgsig for commits, in-body + // PEM for tags) — which matches how legacy rekor entries were + // generated. For new gpgsig-sha256 signatures there are no legacy + // entries to look up, so this reconstruction is best-effort. + var raw []byte + switch { + case bytes.HasPrefix(data, []byte("tree ")): + raw, err = JoinCommit(&CommitSig{Payload: data, Gpgsig: sig}) + case bytes.HasPrefix(data, []byte("object ")): + raw, err = JoinTag(&TagSig{Payload: data, InBody: sig}) + default: + return nil, errors.New("could not determine Git object type") + } + if err != nil { + return nil, err + } + commit, err := ObjectHash(raw) if err != nil { return nil, err } @@ -118,76 +134,25 @@ func VerifySignature(data, sig []byte, detached bool, rootCerts, intermediates * return v.Verify(context.Background(), data, sig, detached) } -type encoder interface { - Encode(o plumbing.EncodedObject) error -} - -// ObjectHash is a string representation of an encoded Git object -func ObjectHash(data, sig []byte) (string, error) { - // Precompute commit hash to store in tlog - obj := &plumbing.MemoryObject{} - if _, err := obj.Write(data); err != nil { - return "", err - } - - var ( - encoder encoder - err error - ) - // We're making big assumptions here about the ordering of fields - // in Git objects. Unfortunately go-git does loose parsing of objects, - // so it will happily decode objects that don't match the unmarshal type. - // We should see if there's a better way to detect object types. +// ObjectHash returns the git-core hash of an object given its raw bytes +// (the same bytes git stores in the object database, without the +// " \0" prefix). The object type is inferred from the first +// header line: "tree " for commits, "object " for tags. +func ObjectHash(raw []byte) (string, error) { + var objType plumbing.ObjectType switch { - case bytes.HasPrefix(data, []byte("tree ")): - encoder, err = commit(obj, sig) - case bytes.HasPrefix(data, []byte("object ")): - encoder, err = tag(obj, sig) + case bytes.HasPrefix(raw, []byte("tree ")): + objType = plumbing.CommitObject + case bytes.HasPrefix(raw, []byte("object ")): + objType = plumbing.TagObject default: return "", errors.New("could not determine Git object type") } - if err != nil { - return "", err - } - - // go-git will compute a hash on decode and preserve even if we alter the - // object data. To work around this, re-encode the object into a new object - // to force a new hash to be computed. - out := &plumbing.MemoryObject{} - err = encoder.Encode(out) - return out.Hash().String(), err -} - -func commit(obj *plumbing.MemoryObject, sig []byte) (*object.Commit, error) { - obj.SetType(plumbing.CommitObject) - - base := object.Commit{} - if err := base.Decode(obj); err != nil { - return nil, err - } - return &object.Commit{ - Author: base.Author, - Committer: base.Committer, - PGPSignature: string(sig), - Message: base.Message, - TreeHash: base.TreeHash, - ParentHashes: base.ParentHashes, - }, nil -} - -func tag(obj *plumbing.MemoryObject, sig []byte) (*object.Tag, error) { - obj.SetType(plumbing.TagObject) - base := object.Tag{} - if err := base.Decode(obj); err != nil { - return nil, err + obj := &plumbing.MemoryObject{} + obj.SetType(objType) + if _, err := obj.Write(raw); err != nil { + return "", err } - return &object.Tag{ - Tagger: base.Tagger, - Name: base.Name, - TargetType: base.TargetType, - Target: base.Target, - Message: base.Message, - PGPSignature: string(sig), - }, nil + return obj.Hash().String(), nil } diff --git a/pkg/predicate/predicate.go b/pkg/predicate/predicate.go index 3ddf340c..b0f1beef 100644 --- a/pkg/predicate/predicate.go +++ b/pkg/predicate/predicate.go @@ -15,5 +15,6 @@ package predicate const ( - TypeV01 = "https://gitsign.sigstore.dev/predicate/git/v0.1" + TypeV01 = "https://gitsign.sigstore.dev/predicate/git/v0.1" + TagTypeV01 = "https://gitsign.sigstore.dev/predicate/tag/v0.1" ) diff --git a/pkg/predicate/predicate.pb.go b/pkg/predicate/predicate.pb.go index e12dd5b7..d84ec419 100644 --- a/pkg/predicate/predicate.pb.go +++ b/pkg/predicate/predicate.pb.go @@ -96,6 +96,142 @@ func (x *GitCommit) GetSignerInfo() []*SignerInfo { return nil } +type GitTag struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *Tag `protobuf:"bytes,4,opt,name=source,proto3" json:"source,omitempty"` + Signature string `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + SignerInfo []*SignerInfo `protobuf:"bytes,3,rep,name=signer_info,proto3" json:"signer_info,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GitTag) Reset() { + *x = GitTag{} + mi := &file_predicate_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GitTag) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GitTag) ProtoMessage() {} + +func (x *GitTag) ProtoReflect() protoreflect.Message { + mi := &file_predicate_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GitTag.ProtoReflect.Descriptor instead. +func (*GitTag) Descriptor() ([]byte, []int) { + return file_predicate_proto_rawDescGZIP(), []int{1} +} + +func (x *GitTag) GetSource() *Tag { + if x != nil { + return x.Source + } + return nil +} + +func (x *GitTag) GetSignature() string { + if x != nil { + return x.Signature + } + return "" +} + +func (x *GitTag) GetSignerInfo() []*SignerInfo { + if x != nil { + return x.SignerInfo + } + return nil +} + +type Tag struct { + state protoimpl.MessageState `protogen:"open.v1"` + Object string `protobuf:"bytes,1,opt,name=object,proto3" json:"object,omitempty"` + ObjectType string `protobuf:"bytes,2,opt,name=object_type,proto3" json:"object_type,omitempty"` // object type (eg 'commit') + Tag string `protobuf:"bytes,3,opt,name=tag,proto3" json:"tag,omitempty"` + Tagger *Author `protobuf:"bytes,4,opt,name=tagger,proto3" json:"tagger,omitempty"` + Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Tag) Reset() { + *x = Tag{} + mi := &file_predicate_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Tag) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Tag) ProtoMessage() {} + +func (x *Tag) ProtoReflect() protoreflect.Message { + mi := &file_predicate_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Tag.ProtoReflect.Descriptor instead. +func (*Tag) Descriptor() ([]byte, []int) { + return file_predicate_proto_rawDescGZIP(), []int{2} +} + +func (x *Tag) GetObject() string { + if x != nil { + return x.Object + } + return "" +} + +func (x *Tag) GetObjectType() string { + if x != nil { + return x.ObjectType + } + return "" +} + +func (x *Tag) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +func (x *Tag) GetTagger() *Author { + if x != nil { + return x.Tagger + } + return nil +} + +func (x *Tag) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + type Commit struct { state protoimpl.MessageState `protogen:"open.v1"` Tree string `protobuf:"bytes,1,opt,name=tree,proto3" json:"tree,omitempty"` @@ -109,7 +245,7 @@ type Commit struct { func (x *Commit) Reset() { *x = Commit{} - mi := &file_predicate_proto_msgTypes[1] + mi := &file_predicate_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -121,7 +257,7 @@ func (x *Commit) String() string { func (*Commit) ProtoMessage() {} func (x *Commit) ProtoReflect() protoreflect.Message { - mi := &file_predicate_proto_msgTypes[1] + mi := &file_predicate_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -134,7 +270,7 @@ func (x *Commit) ProtoReflect() protoreflect.Message { // Deprecated: Use Commit.ProtoReflect.Descriptor instead. func (*Commit) Descriptor() ([]byte, []int) { - return file_predicate_proto_rawDescGZIP(), []int{1} + return file_predicate_proto_rawDescGZIP(), []int{3} } func (x *Commit) GetTree() string { @@ -183,7 +319,7 @@ type Author struct { func (x *Author) Reset() { *x = Author{} - mi := &file_predicate_proto_msgTypes[2] + mi := &file_predicate_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -195,7 +331,7 @@ func (x *Author) String() string { func (*Author) ProtoMessage() {} func (x *Author) ProtoReflect() protoreflect.Message { - mi := &file_predicate_proto_msgTypes[2] + mi := &file_predicate_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -208,7 +344,7 @@ func (x *Author) ProtoReflect() protoreflect.Message { // Deprecated: Use Author.ProtoReflect.Descriptor instead. func (*Author) Descriptor() ([]byte, []int) { - return file_predicate_proto_rawDescGZIP(), []int{2} + return file_predicate_proto_rawDescGZIP(), []int{4} } func (x *Author) GetName() string { @@ -242,7 +378,7 @@ type SignerInfo struct { func (x *SignerInfo) Reset() { *x = SignerInfo{} - mi := &file_predicate_proto_msgTypes[3] + mi := &file_predicate_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -254,7 +390,7 @@ func (x *SignerInfo) String() string { func (*SignerInfo) ProtoMessage() {} func (x *SignerInfo) ProtoReflect() protoreflect.Message { - mi := &file_predicate_proto_msgTypes[3] + mi := &file_predicate_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -267,7 +403,7 @@ func (x *SignerInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SignerInfo.ProtoReflect.Descriptor instead. func (*SignerInfo) Descriptor() ([]byte, []int) { - return file_predicate_proto_rawDescGZIP(), []int{3} + return file_predicate_proto_rawDescGZIP(), []int{5} } func (x *SignerInfo) GetAttributes() string { @@ -292,7 +428,17 @@ const file_predicate_proto_rawDesc = "" + "\tGitCommit\x12>\n" + "\x06source\x18\x01 \x01(\v2&.sigstore.gitsign.predicate.v01.CommitR\x06source\x12\x1c\n" + "\tsignature\x18\x02 \x01(\tR\tsignature\x12L\n" + - "\vsigner_info\x18\x03 \x03(\v2*.sigstore.gitsign.predicate.v01.SignerInfoR\vsigner_info\"\xd6\x01\n" + + "\vsigner_info\x18\x03 \x03(\v2*.sigstore.gitsign.predicate.v01.SignerInfoR\vsigner_info\"\xb1\x01\n" + + "\x06GitTag\x12;\n" + + "\x06source\x18\x04 \x01(\v2#.sigstore.gitsign.predicate.v01.TagR\x06source\x12\x1c\n" + + "\tsignature\x18\x02 \x01(\tR\tsignature\x12L\n" + + "\vsigner_info\x18\x03 \x03(\v2*.sigstore.gitsign.predicate.v01.SignerInfoR\vsigner_info\"\xab\x01\n" + + "\x03Tag\x12\x16\n" + + "\x06object\x18\x01 \x01(\tR\x06object\x12 \n" + + "\vobject_type\x18\x02 \x01(\tR\vobject_type\x12\x10\n" + + "\x03tag\x18\x03 \x01(\tR\x03tag\x12>\n" + + "\x06tagger\x18\x04 \x01(\v2&.sigstore.gitsign.predicate.v01.AuthorR\x06tagger\x12\x18\n" + + "\amessage\x18\x05 \x01(\tR\amessage\"\xd6\x01\n" + "\x06Commit\x12\x12\n" + "\x04tree\x18\x01 \x01(\tR\x04tree\x12\x18\n" + "\aparents\x18\x02 \x03(\tR\aparents\x12>\n" + @@ -322,25 +468,30 @@ func file_predicate_proto_rawDescGZIP() []byte { return file_predicate_proto_rawDescData } -var file_predicate_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_predicate_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_predicate_proto_goTypes = []any{ (*GitCommit)(nil), // 0: sigstore.gitsign.predicate.v01.GitCommit - (*Commit)(nil), // 1: sigstore.gitsign.predicate.v01.Commit - (*Author)(nil), // 2: sigstore.gitsign.predicate.v01.Author - (*SignerInfo)(nil), // 3: sigstore.gitsign.predicate.v01.SignerInfo - (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*GitTag)(nil), // 1: sigstore.gitsign.predicate.v01.GitTag + (*Tag)(nil), // 2: sigstore.gitsign.predicate.v01.Tag + (*Commit)(nil), // 3: sigstore.gitsign.predicate.v01.Commit + (*Author)(nil), // 4: sigstore.gitsign.predicate.v01.Author + (*SignerInfo)(nil), // 5: sigstore.gitsign.predicate.v01.SignerInfo + (*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp } var file_predicate_proto_depIdxs = []int32{ - 1, // 0: sigstore.gitsign.predicate.v01.GitCommit.source:type_name -> sigstore.gitsign.predicate.v01.Commit - 3, // 1: sigstore.gitsign.predicate.v01.GitCommit.signer_info:type_name -> sigstore.gitsign.predicate.v01.SignerInfo - 2, // 2: sigstore.gitsign.predicate.v01.Commit.author:type_name -> sigstore.gitsign.predicate.v01.Author - 2, // 3: sigstore.gitsign.predicate.v01.Commit.committer:type_name -> sigstore.gitsign.predicate.v01.Author - 4, // 4: sigstore.gitsign.predicate.v01.Author.date:type_name -> google.protobuf.Timestamp - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 3, // 0: sigstore.gitsign.predicate.v01.GitCommit.source:type_name -> sigstore.gitsign.predicate.v01.Commit + 5, // 1: sigstore.gitsign.predicate.v01.GitCommit.signer_info:type_name -> sigstore.gitsign.predicate.v01.SignerInfo + 2, // 2: sigstore.gitsign.predicate.v01.GitTag.source:type_name -> sigstore.gitsign.predicate.v01.Tag + 5, // 3: sigstore.gitsign.predicate.v01.GitTag.signer_info:type_name -> sigstore.gitsign.predicate.v01.SignerInfo + 4, // 4: sigstore.gitsign.predicate.v01.Tag.tagger:type_name -> sigstore.gitsign.predicate.v01.Author + 4, // 5: sigstore.gitsign.predicate.v01.Commit.author:type_name -> sigstore.gitsign.predicate.v01.Author + 4, // 6: sigstore.gitsign.predicate.v01.Commit.committer:type_name -> sigstore.gitsign.predicate.v01.Author + 6, // 7: sigstore.gitsign.predicate.v01.Author.date:type_name -> google.protobuf.Timestamp + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_predicate_proto_init() } @@ -354,7 +505,7 @@ func file_predicate_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_predicate_proto_rawDesc), len(file_predicate_proto_rawDesc)), NumEnums: 0, - NumMessages: 4, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/v01/predicate.proto b/proto/v01/predicate.proto index eb99656c..fe694b56 100644 --- a/proto/v01/predicate.proto +++ b/proto/v01/predicate.proto @@ -26,6 +26,20 @@ message GitCommit { repeated SignerInfo signer_info = 3 [json_name = "signer_info"]; } +message GitTag { + Tag source = 4 [json_name = "source"]; + string signature = 2 [json_name = "signature"]; + repeated SignerInfo signer_info = 3 [json_name = "signer_info"]; +} + +message Tag { + string object = 1 [json_name = "object"]; + string object_type = 2 [json_name = "object_type"]; // object type (eg 'commit') + string tag = 3 [json_name = "tag"]; + Author tagger = 4 [json_name = "tagger"]; + string message = 5 [json_name = "message"]; +} + message Commit { string tree = 1 [json_name = "tree"]; repeated string parents = 2 [json_name = "parents"];