* Add cosign-specific user-agent to all HTTP requests
This change addresses issue #4692 by ensuring that all HTTP requests
made by cosign identify with a proper user-agent that includes both
the cosign version and sigstore-go version.
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
* Scope sigstore-go user-agent to sigstore-go code paths
Keep the global user-agent as cosign/<version> (os; arch) and only
send cosign/<version> sigstore-go/<version> (os; arch) on requests
made through sigstore-go. Fulcio and TSA clients built in SignData
now use a transport that overrides sigstore-go's default user-agent.
Rekor requests made via sigstore-go still use sigstore-go's
user-agent until sigstore-go exposes a way to set it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
* Set cosign user-agent on sigstore-go Fulcio, TSA and Rekor clients
Bump sigstore-go to main to pick up sigstore/sigstore-go#686, which
adds a UserAgent option and always appends sigstore-go/<version>.
Pass cosign's user-agent through it and drop the transport wrapper
and the sigstore-go version lookup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
---------
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
For #5138
To help people transition old container signatures and attestations to
the protobuf bundle format that supports offline verification, rotating
verification material via trusted roots, etc.
Also do not try to convert container signatures, since the signature
over the container won't match the signature over the new DSSE that
containers use with the protobuf bundle format.
---------
Signed-off-by: Zach Steindler <steiza@github.com>
* fix: strip trailing whitespace from attached signature files/stdin
Motivation:
The linked report below describes a failure after detaching a
signature with `cosign download signature ... | jq -r
'.Base64Signature' > signature.sig` and later re-attaching it on
another registry with `cosign attach signature --signature
signature.sig ...`. The reporter's own --verbose debug output shows
the OCI manifest annotation dev.cosignproject.cosign/signature on the
re-attached image literally contains the base64 signature value with
a trailing newline embedded in it, unlike the original signing
machine's copy. That newline comes from shell redirection after
`jq -r`; cosign's attach signature command stores whatever bytes it
reads from the --signature file or stdin verbatim, with no trimming.
Storing extraneous whitespace inside a signature annotation is a
real, demonstrated defect regardless of whether it fully explains the
"invalid signature when validating ASN.1 encoded signature" error the
reporter hit: Go's own base64 decoder tolerates embedded \r/\n and
decodes to the same bytes, so this alone would not necessarily
reproduce that exact failure, and the report's repro also mixed up
`cosign download signature`'s JSON envelope for --payload, a separate
issue. This change fixes the annotation-pollution defect on its own
merits; it is not a claim that it resolves the full reported scenario
end-to-end.
Approach:
Trim leading and trailing whitespace from bytes read from a signature
file or stdin in signatureBytes() (cmd/cosign/cli/attach/sig.go)
before they are stored as the signature annotation. Base64 has no
whitespace in its alphabet, so trimming cannot remove meaningful
signature bytes. The literal-argument path is untouched.
Validation:
- go test ./cmd/cosign/cli/attach/... — added
TestSignatureBytesTrimsTrailingWhitespace, which fails before this
change (returned bytes include the trailing "\n") and passes after;
verified this directly by stashing the fix and re-running the test.
Also added TestSignatureBytesFileWithoutTrailingWhitespaceUnaffected
confirming clean input is unaffected.
- go build ./... and go vet ./cmd/cosign/cli/attach/... pass.
- go test ./cmd/cosign/... passes (all subpackages).
- golangci-lint run ./cmd/cosign/cli/attach/... (v2.12.2, matching
this repo's Dockerfile.golangci-lint) reports 0 issues.
Report: https://github.com/sigstore/cosign/issues/4207
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
* fix: update copyright year in sig_test.go to 2026
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
---------
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Motivation:
When signing with `cosign sign --upload=false -a key=value
--output-signature=sig.file`, cosign builds the signed payload from
payload.Cosign{Image, ClaimedIdentity, Annotations} including the
annotations (cmd/cosign/cli/sign/sign.go). When later verifying
offline with `cosign verify -a key=value --signature sig.file` and no
explicit `--payload`, cosign reconstructs an "implied" payload via
ObsoletePayload to compare against the signature, but that
reconstruction dropped annotations entirely. The reconstructed payload
therefore never matched the payload that was actually signed, so
verification always failed with "crypto/rsa: verification error" (or
equivalent) any time annotations were used with offline signing.
Approach:
Thread the caller-supplied annotations through ObsoletePayload so the
implied payload it builds matches what was used at signing time. The
verify call site (pkg/cosign/verify.go) now passes co.Annotations,
which is populated from the `-a` flag. The attach-signature call site
(cmd/cosign/cli/attach/sig.go) has no annotations flag, so it passes
nil, preserving its existing behavior exactly.
This is a pure payload-construction/serialization fix with no
dependency on registry, network, or transparency-log state: the
default (no-annotations) case is byte-for-byte unchanged.
Validation:
- go build ./...
- go test ./pkg/cosign/... ./cmd/cosign/cli/attach/... ./cmd/cosign/cli/verify/...
- go test $(go list ./... | grep -v third_party/)
All pass. Added TestObsoletePayloadWithAnnotations in
pkg/cosign/obsolete_test.go, which fails to compile against the
pre-fix ObsoletePayload signature and asserts the annotation now
appears in the marshaled payload's "optional" field once the fix is
applied; confirmed by temporarily reverting the source changes and
observing the test target fail to build.
Report: https://github.com/sigstore/cosign/issues/4024
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
* chore(deps): bump the all group across 1 directory with 2 updates
Bumps the all group with 2 updates in the / directory: golang and golangci/golangci-lint.
Updates `golang` from 1.26.5 to 1.27.0
Updates `golangci/golangci-lint` from v2.12.2 to v2.13.1
---
updated-dependencies:
- dependency-name: golang
dependency-version: 1.27.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: all
- dependency-name: golangci/golangci-lint
dependency-version: v2.13.1
dependency-type: direct:production
dependency-group: all
...
Signed-off-by: dependabot[bot] <support@github.com>
* address lints
Signed-off-by: Bob Callaway <bcallaway@google.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Bob Callaway <bcallaway@google.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bob Callaway <bcallaway@google.com>
* fix: verify-blob should not force SHA256 for non-default key hash algorithms
Motivation:
cosign verify-blob fails to verify legacy-bundle blob signatures made
with keys whose conventional digest algorithm is not SHA256 (e.g. an
ECDSA P-521 key, which pairs with SHA-512), unless the user explicitly
passes the deprecated --signature-digest-algorithm flag. Without it,
verification fails with "invalid signature when validating ASN.1
encoded signature".
Approach:
The --signature-digest-algorithm flag had a hardcoded default value of
"sha256", so its value was indistinguishable from an explicit
--signature-digest-algorithm sha256. SignatureDigestOptions.HashAlgorithm()
compounded this by also returning SHA256 for an empty/unset value.
VerifyBlobCmd.Exec then forced any zero HashAlgorithm to SHA256 before
loading the verifier, discarding the zero-value sentinel that
LoadPublicKeyRaw (pkg/signature/keys.go) relies on to fall back to
signature.LoadDefaultVerifier, which selects the correct digest
algorithm for the given key type (SHA256 for P-256/RSA, SHA384 for
P-384, SHA512 for P-521).
This change makes the flag default to an empty string, has
HashAlgorithm() return 0 (unset) when the flag isn't provided, and
removes the forced-SHA256 default in VerifyBlobCmd.Exec, letting the
key-loading path pick the digest algorithm that matches the key. The
same forced-default pattern still exists, unchanged, in the image
verify, verify-attestation, and verify-blob-attestation commands,
which are out of scope for this fix.
Validation:
Added TestVerifyBlobWithUnsetHashAlgorithmMatchesKeyDefault
(cmd/cosign/cli/verify/verify_blob_test.go), which signs a blob with a
generated P-521 key using SHA-512 and verifies it via VerifyBlobCmd.Exec
with HashAlgorithm left unset. Confirmed this test fails with the
exact reported error before this change (verified by reverting only
the implementation files) and passes after. Also added
TestSignatureDigestOptionsHashAlgorithm
(cmd/cosign/cli/options/signature_digest_test.go) covering the
unset/explicit/invalid cases for HashAlgorithm().
Ran:
go build ./...
go vet ./cmd/... ./pkg/...
go test ./cmd/... ./pkg/...
golangci-lint run ./cmd/cosign/cli/options/... ./cmd/cosign/cli/verify/...
All passed with no regressions.
Report: https://github.com/sigstore/cosign/issues/5063
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
* fix: verify, verify-attestation, verify-blob-attestation should not force SHA256 for non-default key hash algorithms
Applies the same fix as verify-blob to the sibling verify commands, per
review feedback on #5064: stop forcing HashAlgorithm to SHA256 when
unset, and let LoadVerifierFromKeyOrCert pick the digest algorithm
that matches the provided key (e.g. SHA-512 for P-521 ECDSA keys).
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
---------
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
* fix: only show public-instance privacy statement for the default Rekor URL
Motivation:
When uploading to a transparency log, cosign always printed the
"hosted by sigstore a Series of LF Projects, LLC" privacy statement
and confirmation prompt, even when the user pointed --rekor-url at
their own local or self-hosted Rekor instance. The upload itself
correctly went to the custom instance, but the displayed text
described the public sigstore.dev data-retention/Terms of Use policy,
which is misleading and confusing for anyone running their own Rekor
server (e.g. via the rekor docker-compose setup).
This is a UX/cosmetic issue only: no upload destination or signing
behavior changes, only the confirmation text shown to the user.
Approach:
In ShouldUploadToTlog (cmd/cosign/cli/signcommon/common.go), only show
the public-instance privacy statement when the configured Rekor URL is
empty (unset) or equal to options.DefaultRekorURL
(https://rekor.sigstore.dev). A custom --rekor-url no longer triggers
the public-instance notice.
Validation:
- go build ./...
- go test $(go list ./... | grep -v third_party/) (equivalent to `make test`) — all packages pass
- make lint — 0 issues
- Added TestShouldUploadToTlog_CustomRekorURLSkipsPublicInstanceStatement
in cmd/cosign/cli/signcommon/common_test.go, which calls
ShouldUploadToTlog with a custom RekorURL and asserts the public-instance
statement is not printed. Manually reverted the guard and reran the test
to confirm it fails without the fix and passes with it restored.
Report: https://github.com/sigstore/cosign/issues/4400
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
* fix: match public-good Rekor URLs by *.sigstore.dev/*.sigstage.dev suffix
Per review feedback, an exact-string match against the default Rekor
URL doesn't account for region- and year-specific public-good
hostnames. Only suppress/show the privacy statement based on whether
the configured Rekor URL's hostname is sigstore.dev/sigstage.dev or a
subdomain of either.
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
* test: consolidate ShouldUploadToTlog tests into a table-driven test
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
* fix: treat empty Rekor URL as not public-good in isPublicGoodRekorURL
An empty ko.RekorURL means NewSigningConfigFromKeyOpts configures no
Rekor service at all, so nothing is uploaded to the public-good
instance in that case; the privacy statement should not be shown.
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
---------
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Remove the fallback that loaded a raw public key from a legacy bundle's
cert field when X.509 certificate parsing failed. Previously, an attacker
could supply a bare public key in b.Cert to populate co.SigVerifier,
skipping X.509 certificate chain validation and CheckCertificatePolicy
enforcement during keyless verification. This would use the key without
any additional checks. The key-as-certificate in the bundle would take
priority over a key being provided via --key as well.
This updates the sign-blob and attest-blob code paths to never set a key
in the certificate field in the legacy bundle. Before a refactor a few
months ago, attest-blob did this. After the refactor, sign-blob did as
well. Now, neither code path will persist a key in the bundle.
For verification, verify-blob and verify-blob-attestation have been
updated to not fallback to parsing a certificate as a key. If a bundle
is supplied that contains a key, it fails with a parsing error.
Note that no change is needed for sign/attest and
verify/verify-attestation, as the container signing path never stored a
public key in the certificate field and the container verification path
would only load a certificate as a certificate.
Signed-off-by: Hayden <8418760+Hayden-IO@users.noreply.github.com>
* Adding BYOCA Certificate Chain Option for Signing and Verification
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
* Temporary Reference to Uncommited sigstore-go Updates
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
* Update sigstore-go v1.2.2
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
* Update Copyright Date
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
* Certificate Chain Support for Verify Blob and E2E Tests
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
* Update Unit Tests for sigstore-go v1.2.2 Changes
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
* Update localCertChainProvider to Skip Self-Signed Certificate
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
* Fix Unit Test for Legacy Signing to Include Self-Signed Root
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
---------
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
* Allow attestation download to handle both bundle types.
As suggested on https://github.com/sigstore/cosign/issues/4573
Signed-off-by: Zach Steindler <steiza@github.com>
* Do not error out if no old attestations are found to download
Signed-off-by: Zach Steindler <steiza@github.com>
* Update e2e test to actually check download attestation output
Signed-off-by: Zach Steindler <steiza@github.com>
---------
Signed-off-by: Zach Steindler <steiza@github.com>
* Return an error instead of panicking on malformed FROM lines in dockerfile verify
getImageFromLine panicked with an index-out-of-range on two malformed but
parseable FROM lines: an unresolvable build arg ("FROM ${BASE_IMAGE}" with
BASE_IMAGE unset) expands to an empty string and indexes fields[-1], and a
trailing "AS" ("FROM image AS") reads fields[i+1] past the slice. Return an
actionable error in both cases and propagate it from the caller. Adds a
regression test.
Signed-off-by: Nikhil Jathar <22786232+mailnike@users.noreply.github.com>
---------
Signed-off-by: Nikhil Jathar <22786232+mailnike@users.noreply.github.com>
Signed-off-by: Zach Steindler <steiza@github.com>
`cosign dockerfile verify` extracted the target of a `FROM` instruction even
when it referenced a previously declared build stage (e.g. `FROM base_image`),
then tried to pull that stage name as an image and failed with an
authentication/not-found error.
Skip `FROM` references that resolve to a known stage name, mirroring the
existing handling for `COPY --from=<stage>`.
Fixes#3880
Signed-off-by: bejaratommy <tommy@bejara.net>
Co-authored-by: bejaratommy <tommy@bejara.net>
AnnotationsMap parsed --annotations with strings.Split(a, "="), so any value
containing '=' (base64 padding, URLs with query strings, chained key=value)
yielded len>2 and was rejected with "unable to parse annotation". Use
strings.SplitN(a, "=", 2), matching the repo's canonical parser in
pkg/signature/annotations.go. The no-'=' case is still rejected.
Signed-off-by: Seonghyun Hong <s3onghyun.hong@gmail.com>
The Short command descriptions were inconsistent: most followed the cobra
convention of a short phrase with no trailing period, but sixteen ended in
a period. Standardize the outliers to match the period-free majority and
regenerate the Markdown docs.
piv-tool and pkcs11-tool Short fields are addressed in separate changes.
Signed-off-by: Ogulcan Aydogan <ogulcanaydogan@hotmail.com>
* Update sigstore-go to v1.2.0
sigstore-go v1.2.0 encodes DSSE envelopes as hashedrekord entries
on Rekor v2. Bump conformance action to v0.0.29 and remove the
message-digest-mismatch xfail, which now passes. Fix e2e and unit
tests for updated transitive dependencies (timestamp-authority
v2.1.2 requires default-policy-oid config; tlog entry body parsing
now requires a valid Rekor v1 or v2 body).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Cody Soyland <cody.soyland@chainguard.dev>
* fix(ci): plug DSSE bundle reader leak and use --allow-http-registry
Two fixes for the Rekor v2 / sigstore-go v1.2.0 attestation path:
- oci/remote.Bundle never closed the reader returned by Uncompressed(),
leaking a slot in go-containerregistry's pull limiter. Repeated calls
(e.g. GetBundles walking multiple attestations) exhaust the limiter and
block forever, hanging TestSignVerifyBundle and the vuln
verify-attestation e2e step.
- go-containerregistry v0.21.6 narrowed the local registry regex from
`.local` to `.localhost`, so `registry.local:5000` is no longer
auto-detected as HTTP. Use the correct `--allow-http-registry` flag.
Signed-off-by: Cody Soyland <cody.soyland@chainguard.dev>
---------
Signed-off-by: Cody Soyland <cody.soyland@chainguard.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New bundle verification cannot fall back to legacy TUF targets when the live trusted root cannot be loaded. Return the wrapped TUF error from SetTrustedMaterial in that mode so callers see the underlying trusted root failure instead of the later nil TrustedMaterial invariant.
Legacy verification still warns and falls back to individual targets, and the new tests cover both paths.
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>