Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package gitsign for openSUSE:Factory checked 
in at 2026-06-10 16:15:00
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/gitsign (Old)
 and      /work/SRC/openSUSE:Factory/.gitsign.new.2375 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "gitsign"

Wed Jun 10 16:15:00 2026 rev:14 rq:1358520 version:0.16.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/gitsign/gitsign.changes  2026-05-07 
15:44:53.438913544 +0200
+++ /work/SRC/openSUSE:Factory/.gitsign.new.2375/gitsign.changes        
2026-06-10 16:19:03.773140144 +0200
@@ -1,0 +2,12 @@
+Wed Jun 10 10:59:38 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.16.1:
+  * Bump sigstore/cosign-installer from 4.1.1 to 4.1.2 in the
+    actions group (#804)
+  * Return the actual signer cert from CertVerifier.Verify (#810)
+  * Updates (#803)
+  * Reject malformed objects in attest predicate generation (#809)
+  * Bump github.com/go-openapi/strfmt in the gomod group across 1
+    directory (#811)
+
+-------------------------------------------------------------------

Old:
----
  gitsign-0.16.0.obscpio

New:
----
  gitsign-0.16.1.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ gitsign.spec ++++++
--- /var/tmp/diff_new_pack.XmlspJ/_old  2026-06-10 16:19:05.793223858 +0200
+++ /var/tmp/diff_new_pack.XmlspJ/_new  2026-06-10 16:19:05.797224024 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           gitsign
-Version:        0.16.0
+Version:        0.16.1
 Release:        0
 Summary:        Keyless Git signing using Sigstore
 License:        Apache-2.0
@@ -26,7 +26,7 @@
 Source1:        vendor.tar.gz
 Source2:        gitsign-credential-cache.service
 Source3:        gitsign-credential-cache.socket
-BuildRequires:  go1.25 >= 1.25.5
+BuildRequires:  go1.26 >= 1.26.4
 
 %description
 Keyless Git signing with Sigstore!

++++++ _service ++++++
--- /var/tmp/diff_new_pack.XmlspJ/_old  2026-06-10 16:19:05.837225682 +0200
+++ /var/tmp/diff_new_pack.XmlspJ/_new  2026-06-10 16:19:05.845226013 +0200
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/sigstore/gitsign</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v0.16.0</param>
+    <param name="revision">v0.16.1</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.XmlspJ/_old  2026-06-10 16:19:05.881227505 +0200
+++ /var/tmp/diff_new_pack.XmlspJ/_new  2026-06-10 16:19:05.885227671 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/sigstore/gitsign</param>
-              <param 
name="changesrevision">3c84d87240644b5c62b3b3d79177ae64448591c8</param></service></servicedata>
+              <param 
name="changesrevision">d5e8a584d0551218fc96b5c89c0db910788b432a</param></service></servicedata>
 (No newline at EOF)
 

++++++ gitsign-0.16.0.obscpio -> gitsign-0.16.1.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/go.mod new/gitsign-0.16.1/go.mod
--- old/gitsign-0.16.0/go.mod   2026-05-06 17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/go.mod   2026-05-15 21:07:37.000000000 +0200
@@ -1,15 +1,15 @@
 module github.com/sigstore/gitsign
 
-go 1.25.7
+go 1.26
 
 require (
        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.8.0
-       github.com/go-git/go-git/v5 v5.18.0
+       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.1
+       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.2.0
@@ -25,7 +25,7 @@
        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.50.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
@@ -60,7 +60,7 @@
        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
@@ -181,7 +181,7 @@
        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/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
@@ -203,7 +203,7 @@
        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/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
@@ -251,12 +251,12 @@
        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/mod v0.34.0 // indirect
-       golang.org/x/net v0.53.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.43.0 // indirect
-       golang.org/x/term v0.42.0 // indirect
-       golang.org/x/text v0.36.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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/go.sum new/gitsign-0.16.1/go.sum
--- old/gitsign-0.16.0/go.sum   2026-05-06 17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/go.sum   2026-05-15 21:07:37.000000000 +0200
@@ -78,8 +78,8 @@
 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=
@@ -290,12 +290,12 @@
 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.8.0 
h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
-github.com/go-git/go-billy/v5 v5.8.0/go.mod 
h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
+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.18.0 
h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM=
-github.com/go-git/go-git/v5 v5.18.0/go.mod 
h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=
+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=
@@ -319,8 +319,8 @@
 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.1 
h1:7zGCHji7zSYDC2tCXIusoxYQz/48jAf2q+sF6wXTG+c=
-github.com/go-openapi/strfmt v0.26.1/go.mod 
h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y=
+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=
@@ -504,8 +504,8 @@
 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=
@@ -595,8 +595,8 @@
 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=
@@ -798,8 +798,8 @@
 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.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
-golang.org/x/crypto v0.50.0/go.mod 
h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
+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/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=
@@ -812,8 +812,8 @@
 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.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
-golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
+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,8 +839,8 @@
 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.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
-golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
+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.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
 golang.org/x/oauth2 v0.36.0/go.mod 
h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
@@ -892,8 +892,8 @@
 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.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
-golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+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.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.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
-golang.org/x/term v0.42.0/go.mod 
h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
+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,8 +918,8 @@
 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.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
-golang.org/x/text v0.36.0/go.mod 
h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
+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=
@@ -938,8 +938,8 @@
 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.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
-golang.org/x/tools v0.43.0/go.mod 
h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
+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=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/internal/fork/ietf-cms/verify.go 
new/gitsign-0.16.1/internal/fork/ietf-cms/verify.go
--- old/gitsign-0.16.0/internal/fork/ietf-cms/verify.go 2026-05-06 
17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/internal/fork/ietf-cms/verify.go 2026-05-15 
21:07:37.000000000 +0200
@@ -5,6 +5,7 @@
        "crypto/x509"
        "encoding/asn1"
        "errors"
+       "time"
 
        "github.com/github/smimesign/ietf-cms/protocol"
        "github.com/sigstore/sigstore/pkg/cryptoutils"
@@ -180,6 +181,16 @@
                        }
                }
 
+               // 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gitsign-0.16.0/internal/gitsign/invalid_object_test.go 
new/gitsign-0.16.1/internal/gitsign/invalid_object_test.go
--- old/gitsign-0.16.0/internal/gitsign/invalid_object_test.go  2026-05-06 
17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/internal/gitsign/invalid_object_test.go  2026-05-15 
21:07:37.000000000 +0200
@@ -33,19 +33,24 @@
        "github.com/sigstore/gitsign/pkg/git"
 )
 
-// TestDuplicateTreeTrustConfusion is an end-to-end reproduction of a parser
-// trust-confusion attack: an attacker replays a legitimate signature against
-// a malformed commit with two tree headers. go-git's loose parser keeps the
-// last tree (so a signature over that canonical form verifies) while git-core
-// and the on-disk hash reflect the first. The test:
+// 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.
 //
-//  1. Confirms the crafted signature really does verify against the re-encoded
-//     (last-wins) bytes — proving the PoC is a working forgery, not a typo.
-//  2. Confirms the fix: when the verifier is fed the raw object bytes (the
-//     SplitCommit output), the signature fails to verify because the bytes
-//     differ from what was signed.
-//  3. Confirms a well-formed commit with the same signature still verifies —
-//     guards against an over-aggressive verifier path.
+// 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),
@@ -94,16 +99,20 @@
                message,
        ))
 
-       t.Run("signature is a genuine forgery against the re-encoded form", 
func(t *testing.T) {
-               // Simulate the pre-fix behavior: load the malformed commit 
through
-               // go-git and re-encode it via EncodeWithoutSignature (which 
drops the
-               // first tree under last-wins), then verify. This MUST succeed,
-               // otherwise the PoC isn't actually demonstrating the attack 
and the
-               // rejection assertion below would pass vacuously.
+       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("pre-fix behavior check: expected signature to 
verify over re-encoded bytes (proves the PoC is genuine), got: %v", err)
+               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")
                }
        })
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/pkg/attest/statement.go 
new/gitsign-0.16.1/pkg/attest/statement.go
--- old/gitsign-0.16.0/pkg/attest/statement.go  2026-05-06 17:06:12.000000000 
+0200
+++ new/gitsign-0.16.1/pkg/attest/statement.go  2026-05-15 21:07:37.000000000 
+0200
@@ -23,7 +23,9 @@
        "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"
@@ -41,7 +43,14 @@
        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
        }
@@ -138,11 +147,19 @@
                return nil, err
        }
 
-       // Try to get the tag object. If this fails, the tag is not annotated.
-       tagObj, err := repo.TagObject(ref.Hash())
+       // 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{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/pkg/attest/statement_test.go 
new/gitsign-0.16.1/pkg/attest/statement_test.go
--- old/gitsign-0.16.0/pkg/attest/statement_test.go     2026-05-06 
17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/pkg/attest/statement_test.go     2026-05-15 
21:07:37.000000000 +0200
@@ -15,6 +15,7 @@
 package attest
 
 import (
+       "errors"
        "fmt"
        "os"
        "testing"
@@ -25,12 +26,13 @@
        "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"
 )
 
 // newTestRepo creates an in-memory repo with the given remote configuration.
-func newTestRepo(t *testing.T, remoteURL string) (*git.Repository, 
*memory.Storage) {
+func newTestRepo(t *testing.T) (*git.Repository, *memory.Storage) {
        t.Helper()
        storage := memory.NewStorage()
        repo := &git.Repository{
@@ -40,7 +42,7 @@
                Remotes: map[string]*config.RemoteConfig{
                        "origin": {
                                Name: "origin",
-                               URLs: []string{remoteURL},
+                               URLs: 
[]string{"https://github.com/sigstore/gitsign.git"},
                        },
                },
        }); err != nil {
@@ -69,7 +71,7 @@
 }
 
 func TestCommitStatement(t *testing.T) {
-       repo, storage := newTestRepo(t, "[email protected]:wlynch/gitsign.git")
+       repo, storage := newTestRepo(t)
 
        // Expect files in testdata directory:
        //  foo.in.txt -> foo.out.json
@@ -109,7 +111,7 @@
 }
 
 func TestTagStatement(t *testing.T) {
-       repo, storage := newTestRepo(t, "[email protected]:wlynch/gitsign.git")
+       repo, storage := newTestRepo(t)
 
        // IMPORTANT: When generating new test files, use `git cat-file tag 
<tagname> > foo.in.txt`.
        for _, tc := range []string{
@@ -150,8 +152,53 @@
        }
 }
 
+// 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 <[email protected]> 1700000000 +0000\n" +
+               "author Mallory <[email protected]> 1700000001 +0000\n" +
+               "committer Alice <[email protected]> 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 <[email protected]> 1700000000 +0000\n" +
+               "tagger Mallory <[email protected]> 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, "[email protected]:wlynch/gitsign.git")
+       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")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gitsign-0.16.0/pkg/attest/testdata/fulcio-cert.out.json 
new/gitsign-0.16.1/pkg/attest/testdata/fulcio-cert.out.json
--- old/gitsign-0.16.0/pkg/attest/testdata/fulcio-cert.out.json 2026-05-06 
17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/pkg/attest/testdata/fulcio-cert.out.json 2026-05-15 
21:07:37.000000000 +0200
@@ -3,7 +3,7 @@
   "predicateType": "https://gitsign.sigstore.dev/predicate/git/v0.1";,
   "subject": [
     {
-      "name": "[email protected]:wlynch/gitsign.git",
+      "name": "https://github.com/sigstore/gitsign.git";,
       "digest": {
         "sha1": "10a3086104c5331623be85a5e30d709f457b536b"
       }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gitsign-0.16.0/pkg/attest/testdata/fulcio-tag.out.json 
new/gitsign-0.16.1/pkg/attest/testdata/fulcio-tag.out.json
--- old/gitsign-0.16.0/pkg/attest/testdata/fulcio-tag.out.json  2026-05-06 
17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/pkg/attest/testdata/fulcio-tag.out.json  2026-05-15 
21:07:37.000000000 +0200
@@ -3,7 +3,7 @@
   "predicateType": "https://gitsign.sigstore.dev/predicate/tag/v0.1";,
   "subject": [
     {
-      "name": "[email protected]:wlynch/gitsign.git",
+      "name": "https://github.com/sigstore/gitsign.git";,
       "digest": {
         "sha1": "59b806d0c7932446ca9375b36ea3d421cc673224",
         "gitTag": "59b806d0c7932446ca9375b36ea3d421cc673224"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/pkg/attest/testdata/gpg.out.json 
new/gitsign-0.16.1/pkg/attest/testdata/gpg.out.json
--- old/gitsign-0.16.0/pkg/attest/testdata/gpg.out.json 2026-05-06 
17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/pkg/attest/testdata/gpg.out.json 2026-05-15 
21:07:37.000000000 +0200
@@ -3,7 +3,7 @@
   "predicateType": "https://gitsign.sigstore.dev/predicate/git/v0.1";,
   "subject": [
     {
-      "name": "[email protected]:wlynch/gitsign.git",
+      "name": "https://github.com/sigstore/gitsign.git";,
       "digest": {
         "sha1": "262c05491554c57ee641461f315bf4023d0e93c7"
       }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/pkg/git/rawobj.go 
new/gitsign-0.16.1/pkg/git/rawobj.go
--- old/gitsign-0.16.0/pkg/git/rawobj.go        2026-05-06 17:06:12.000000000 
+0200
+++ new/gitsign-0.16.1/pkg/git/rawobj.go        2026-05-15 21:07:37.000000000 
+0200
@@ -22,6 +22,8 @@
        "fmt"
        "io"
        "unicode"
+
+       "github.com/go-git/go-git/v5/plumbing"
 )
 
 var (
@@ -389,3 +391,107 @@
        }
        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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/pkg/git/rawobj_test.go 
new/gitsign-0.16.1/pkg/git/rawobj_test.go
--- old/gitsign-0.16.0/pkg/git/rawobj_test.go   2026-05-06 17:06:12.000000000 
+0200
+++ new/gitsign-0.16.1/pkg/git/rawobj_test.go   2026-05-15 21:07:37.000000000 
+0200
@@ -22,9 +22,23 @@
        "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-----
@@ -629,3 +643,83 @@
        first, _, _ := bytes.Cut(b, []byte{'\n'})
        return first
 }
+
+func TestValidateCommit(t *testing.T) {
+       authorLine := "author Alice <[email protected]> 1700000000 +0000\n"
+       commLine := "committer Alice <[email protected]> 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 <[email protected]> 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/pkg/git/signature_test.go 
new/gitsign-0.16.1/pkg/git/signature_test.go
--- old/gitsign-0.16.0/pkg/git/signature_test.go        2026-05-06 
17:06:12.000000000 +0200
+++ new/gitsign-0.16.1/pkg/git/signature_test.go        2026-05-15 
21:07:37.000000000 +0200
@@ -24,6 +24,7 @@
        "path/filepath"
        "strings"
        "testing"
+       "time"
 
        "github.com/github/smimesign/fakeca"
        cms "github.com/sigstore/gitsign/internal/fork/ietf-cms"
@@ -104,6 +105,127 @@
        }
 }
 
+// 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.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitsign-0.16.0/pkg/git/verifier.go 
new/gitsign-0.16.1/pkg/git/verifier.go
--- old/gitsign-0.16.0/pkg/git/verifier.go      2026-05-06 17:06:12.000000000 
+0200
+++ new/gitsign-0.16.1/pkg/git/verifier.go      2026-05-15 21:07:37.000000000 
+0200
@@ -20,7 +20,6 @@
        "crypto/x509"
        "encoding/pem"
        "fmt"
-       "time"
 
        cms "github.com/sigstore/gitsign/internal/fork/ietf-cms"
        "github.com/sigstore/gitsign/internal/fulcio/fulcioroots"
@@ -106,23 +105,24 @@
                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)
-       }
-       if len(certs) == 0 {
+       } 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{
@@ -132,17 +132,26 @@
                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.

++++++ gitsign.obsinfo ++++++
--- /var/tmp/diff_new_pack.XmlspJ/_old  2026-06-10 16:19:06.265243419 +0200
+++ /var/tmp/diff_new_pack.XmlspJ/_new  2026-06-10 16:19:06.269243585 +0200
@@ -1,5 +1,5 @@
 name: gitsign
-version: 0.16.0
-mtime: 1778079972
-commit: 3c84d87240644b5c62b3b3d79177ae64448591c8
+version: 0.16.1
+mtime: 1778872057
+commit: d5e8a584d0551218fc96b5c89c0db910788b432a
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/gitsign/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.gitsign.new.2375/vendor.tar.gz differ: char 134, 
line 1

Reply via email to