Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package trufflehog for openSUSE:Factory 
checked in at 2025-12-27 11:28:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/trufflehog (Old)
 and      /work/SRC/openSUSE:Factory/.trufflehog.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "trufflehog"

Sat Dec 27 11:28:57 2025 rev:107 rq:1324459 version:3.92.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/trufflehog/trufflehog.changes    2025-12-12 
21:42:38.504537928 +0100
+++ /work/SRC/openSUSE:Factory/.trufflehog.new.1928/trufflehog.changes  
2025-12-27 11:29:13.568485963 +0100
@@ -1,0 +2,11 @@
+Fri Dec 26 07:36:55 UTC 2025 - Felix Niederwanger <[email protected]>
+
+- Update to version 3.92.4:
+  * enable line numbers for ghr (#4611)
+  * [INS-207] Add Role-Aware Resumption Support for Legacy S3 Scan (#4600)
+  * Update module golang.org/x/crypto to v0.45.0 [SECURITY] (#4562)
+  * [INS-226] use pinned image for quay registry test (#4602)
+  * Pagination and Rate-Limit Handling In Docker Registry Namespace API Calls 
(#4557)
+  * [INS-170] Unify JDBC URL parsing across detectors and analyzers (#4574)
+
+-------------------------------------------------------------------

Old:
----
  trufflehog-3.92.3.obscpio

New:
----
  trufflehog-3.92.4.obscpio

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

Other differences:
------------------
++++++ trufflehog.spec ++++++
--- /var/tmp/diff_new_pack.4WIDSh/_old  2025-12-27 11:29:19.200716146 +0100
+++ /var/tmp/diff_new_pack.4WIDSh/_new  2025-12-27 11:29:19.200716146 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           trufflehog
-Version:        3.92.3
+Version:        3.92.4
 Release:        0
 Summary:        CLI tool to find exposed secrets in source and archives
 License:        AGPL-3.0-or-later

++++++ _service ++++++
--- /var/tmp/diff_new_pack.4WIDSh/_old  2025-12-27 11:29:19.244717945 +0100
+++ /var/tmp/diff_new_pack.4WIDSh/_new  2025-12-27 11:29:19.248718108 +0100
@@ -2,7 +2,7 @@
   <service name="obs_scm" mode="manual">
     <param name="url">https://github.com/trufflesecurity/trufflehog.git</param>
     <param name="scm">git</param>
-    <param name="revision">v3.92.3</param>
+    <param name="revision">v3.92.4</param>
        <param name="match-tag">v*</param>
        <param name="versionformat">@PARENT_TAG@</param>
        <param name="versionrewrite-pattern">v(.*)</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.4WIDSh/_old  2025-12-27 11:29:19.268718926 +0100
+++ /var/tmp/diff_new_pack.4WIDSh/_new  2025-12-27 11:29:19.272719089 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/trufflesecurity/trufflehog.git</param>
-              <param 
name="changesrevision">05cccb53bc9e13bc6d17997db5a6bcc3df44bf2f</param></service></servicedata>
+              <param 
name="changesrevision">ef6e76c3c4023279497fab4721ffa071a722fd05</param></service></servicedata>
 (No newline at EOF)
 

++++++ trufflehog-3.92.3.obscpio -> trufflehog-3.92.4.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/go.mod new/trufflehog-3.92.4/go.mod
--- old/trufflehog-3.92.3/go.mod        2025-12-11 12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/go.mod        2025-12-19 16:15:11.000000000 +0100
@@ -106,11 +106,11 @@
        go.uber.org/automaxprocs v1.6.0
        go.uber.org/mock v0.5.2
        go.uber.org/zap v1.27.0
-       golang.org/x/crypto v0.43.0
-       golang.org/x/net v0.45.0
+       golang.org/x/crypto v0.45.0
+       golang.org/x/net v0.47.0
        golang.org/x/oauth2 v0.30.0
-       golang.org/x/sync v0.17.0
-       golang.org/x/text v0.30.0
+       golang.org/x/sync v0.18.0
+       golang.org/x/text v0.31.0
        golang.org/x/time v0.12.0
        google.golang.org/api v0.247.0
        google.golang.org/protobuf v1.36.9
@@ -316,9 +316,9 @@
        go.uber.org/multierr v1.11.0 // indirect
        go4.org v0.0.0-20230225012048-214862532bf5 // indirect
        golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
-       golang.org/x/mod v0.28.0 // indirect
-       golang.org/x/sys v0.37.0 // indirect
-       golang.org/x/term v0.36.0 // indirect
+       golang.org/x/mod v0.29.0 // indirect
+       golang.org/x/sys v0.38.0 // indirect
+       golang.org/x/term v0.37.0 // indirect
        google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // 
indirect
        google.golang.org/genproto/googleapis/api 
v0.0.0-20250818200422-3122310a409c // indirect
        google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250818200422-3122310a409c // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/go.sum new/trufflehog-3.92.4/go.sum
--- old/trufflehog-3.92.3/go.sum        2025-12-11 12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/go.sum        2025-12-19 16:15:11.000000000 +0100
@@ -854,6 +854,8 @@
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod 
h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
 golang.org/x/crypto v0.43.0/go.mod 
h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+golang.org/x/crypto v0.45.0/go.mod 
h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod 
h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -885,6 +887,8 @@
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod 
h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
 golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
+golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
+golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
 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=
@@ -908,6 +912,8 @@
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
 golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod 
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -925,6 +931,8 @@
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
 golang.org/x/sync v0.17.0/go.mod 
h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
+golang.org/x/sync v0.18.0/go.mod 
h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
 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=
@@ -968,11 +976,15 @@
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
 golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod 
h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
 golang.org/x/term v0.36.0/go.mod 
h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
+golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
+golang.org/x/term v0.37.0/go.mod 
h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod 
h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod 
h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -984,6 +996,8 @@
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
 golang.org/x/text v0.30.0/go.mod 
h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod 
h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod 
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/detectors/jdbc/jdbc.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/jdbc.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/jdbc.go    2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/jdbc.go    2025-12-19 
16:15:11.000000000 +0100
@@ -207,9 +207,9 @@
 }
 
 var supportedSubprotocols = map[string]func(logContext.Context, string) (jdbc, 
error){
-       "mysql":      parseMySQL,
-       "postgresql": parsePostgres,
-       "sqlserver":  parseSqlServer,
+       "mysql":      ParseMySQL,
+       "postgresql": ParsePostgres,
+       "sqlserver":  ParseSqlServer,
 }
 
 type pingResult struct {
@@ -217,6 +217,15 @@
        determinate bool
 }
 
+// ConnectionInfo holds parsed connection information
+type ConnectionInfo struct {
+       Host     string // includes port if specified, e.g., "host:port"
+       Database string
+       User     string
+       Password string
+       Params   map[string]string
+}
+
 type jdbc interface {
        ping(context.Context) pingResult
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/detectors/jdbc/mysql.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/mysql.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/mysql.go   2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/mysql.go   2025-12-19 
16:15:11.000000000 +0100
@@ -12,25 +12,30 @@
        "github.com/go-sql-driver/mysql"
 )
 
-type mysqlJDBC struct {
-       conn     string
-       userPass string
-       host     string
-       params   string
+type MysqlJDBC struct {
+       ConnectionInfo
 }
 
-func (s *mysqlJDBC) ping(ctx context.Context) pingResult {
+func (s *MysqlJDBC) ping(ctx context.Context) pingResult {
        return ping(ctx, "mysql", isMySQLErrorDeterminate,
-               buildMySQLConnectionString(s.host, "", s.userPass, s.params))
+               BuildMySQLConnectionString(s.Host, "", s.User, s.Password, 
s.Params))
 }
 
-func buildMySQLConnectionString(host, database, userPass, params string) 
string {
+func BuildMySQLConnectionString(host, database, user, password string, params 
map[string]string) string {
        conn := host + "/" + database
+       userPass := user
+       if password != "" {
+               userPass = userPass + ":" + password
+       }
        if userPass != "" {
                conn = userPass + "@" + conn
        }
-       if params != "" {
-               conn = conn + "?" + params
+       if len(params) > 0 {
+               var paramList []string
+               for k, v := range params {
+                       paramList = append(paramList, fmt.Sprintf("%s=%s", k, 
v))
+               }
+               conn = conn + "?" + strings.Join(paramList, "&")
        }
        return conn
 }
@@ -51,7 +56,7 @@
        return false
 }
 
-func parseMySQL(ctx logContext.Context, subname string) (jdbc, error) {
+func ParseMySQL(ctx logContext.Context, subname string) (jdbc, error) {
        // expected form: 
[subprotocol:]//[user:password@]HOST[/DB][?key=val[&key=val]]
        if !strings.HasPrefix(subname, "//") {
                return nil, errors.New("expected host to start with //")
@@ -70,11 +75,14 @@
                        Info("Skipping invalid MySQL URL - no password or host 
found")
                return nil, fmt.Errorf("missing host or password in connection 
string")
        }
-       return &mysqlJDBC{
-               conn:     subname[2:],
-               userPass: cfg.User + ":" + cfg.Passwd,
-               host:     fmt.Sprintf("tcp(%s)", cfg.Addr),
-               params:   "timeout=5s",
+       return &MysqlJDBC{
+               ConnectionInfo: ConnectionInfo{
+                       User:     cfg.User,
+                       Password: cfg.Passwd,
+                       Host:     fmt.Sprintf("tcp(%s)", cfg.Addr),
+                       Params:   map[string]string{"timeout": "5s"},
+                       Database: cfg.DBName,
+               },
        }, nil
 }
 
@@ -107,13 +115,20 @@
                return nil, fmt.Errorf("missing host or password in connection 
string")
        }
 
-       userAndPass := user + ":" + pass
+       // Parse database name
+       dbName := strings.TrimPrefix(u.Path, "/")
+       if dbName == "" {
+               dbName = "mysql" // default DB
+       }
 
-       return &mysqlJDBC{
-               conn:     subname[2:],
-               userPass: userAndPass,
-               host:     fmt.Sprintf("tcp(%s)", u.Host),
-               params:   "timeout=5s",
+       return &MysqlJDBC{
+               ConnectionInfo: ConnectionInfo{
+                       User:     user,
+                       Password: pass,
+                       Host:     fmt.Sprintf("tcp(%s)", u.Host),
+                       Params:   map[string]string{"timeout": "5s"},
+                       Database: dbName,
+               },
        }, nil
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/trufflehog-3.92.3/pkg/detectors/jdbc/mysql_integration_test.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/mysql_integration_test.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/mysql_integration_test.go  
2025-12-11 12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/mysql_integration_test.go  
2025-12-19 16:15:11.000000000 +0100
@@ -12,6 +12,8 @@
        "github.com/google/go-cmp/cmp"
        "github.com/stretchr/testify/assert"
        "github.com/testcontainers/testcontainers-go/modules/mysql"
+
+       logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"
 )
 
 func TestMySQL(t *testing.T) {
@@ -89,7 +91,7 @@
        }
        for _, tt := range tests {
                t.Run(tt.input, func(t *testing.T) {
-                       j, err := parseMySQL(tt.input)
+                       j, err := ParseMySQL(logContext.Background(), tt.input)
 
                        if err != nil {
                                got := result{ParseErr: true}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/detectors/jdbc/mysql_test.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/mysql_test.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/mysql_test.go      2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/mysql_test.go      2025-12-19 
16:15:11.000000000 +0100
@@ -2,7 +2,6 @@
 
 import (
        "context"
-       "strings"
        "testing"
 
        logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"
@@ -59,7 +58,7 @@
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                        ctx := logContext.AddLogger(context.Background())
-                       j, err := parseMySQL(ctx, tt.subname)
+                       j, err := ParseMySQL(ctx, tt.subname)
 
                        if tt.shouldBeNil {
                                if j != nil {
@@ -103,15 +102,15 @@
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                        ctx := logContext.AddLogger(context.Background())
-                       j, err := parseMySQL(ctx, tt.subname)
+                       j, err := ParseMySQL(ctx, tt.subname)
                        if err != nil {
                                t.Fatalf("parseMySQL() error = %v", err)
                        }
 
-                       mysqlConn := j.(*mysqlJDBC)
-                       if !strings.Contains(mysqlConn.userPass, 
tt.wantUsername) {
+                       mysqlConn := j.(*MysqlJDBC)
+                       if mysqlConn.User != tt.wantUsername {
                                t.Errorf("Connection string does not contain 
expected username '%s'\nGot: %s\nExpected: %s",
-                                       tt.wantUsername, mysqlConn.userPass, 
tt.wantUsername)
+                                       tt.wantUsername, mysqlConn.User, 
tt.wantUsername)
                        }
                })
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/detectors/jdbc/postgres.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/postgres.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/postgres.go        2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/postgres.go        2025-12-19 
16:15:11.000000000 +0100
@@ -12,20 +12,19 @@
        "github.com/lib/pq"
 )
 
-type postgresJDBC struct {
-       conn   string
-       params map[string]string
+type PostgresJDBC struct {
+       ConnectionInfo
 }
 
-func (s *postgresJDBC) ping(ctx context.Context) pingResult {
+func (s *PostgresJDBC) ping(ctx context.Context) pingResult {
        // It is crucial that we try to build a connection string ourselves 
before using the one we found. This is because
        // if the found connection string doesn't include a username, the 
driver will attempt to connect using the current
        // user's name, which will fail in a way that looks like a determinate 
failure, thus terminating the waterfall. In
        // contrast, when we build a connection string ourselves, if there's no 
username, we try 'postgres' instead, which
        // actually has a chance of working.
        return ping(ctx, "postgres", isPostgresErrorDeterminate,
-               buildPostgresConnectionString(s.params, true),
-               buildPostgresConnectionString(s.params, false),
+               BuildPostgresConnectionString(s.Host, s.User, s.Password, 
"postgres", s.Params, true),
+               BuildPostgresConnectionString(s.Host, s.User, s.Password, 
"postgres", s.Params, false),
        )
 }
 
@@ -59,7 +58,7 @@
        return strings.Join(data, sep)
 }
 
-func parsePostgres(ctx logContext.Context, subname string) (jdbc, error) {
+func ParsePostgres(ctx logContext.Context, subname string) (jdbc, error) {
        // expected form: 
[subprotocol:]//[user:password@]HOST[/DB][?key=val[&key=val]]
 
        if !strings.HasPrefix(subname, "//") {
@@ -77,16 +76,22 @@
        }
 
        params := map[string]string{
-               "host":            u.Host,
-               "dbname":          dbName,
                "connect_timeout": "5",
        }
 
+       postgresJDBC := &PostgresJDBC{
+               ConnectionInfo: ConnectionInfo{
+                       Host:     u.Host,
+                       Database: dbName,
+                       Params:   params,
+               },
+       }
+
        if u.User != nil {
-               params["user"] = u.User.Username()
+               postgresJDBC.User = u.User.Username()
                pass, set := u.User.Password()
                if set {
-                       params["password"] = pass
+                       postgresJDBC.Password = pass
                }
        }
 
@@ -95,46 +100,51 @@
                // 
https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-PROTECTION
                case "disable", "allow", "prefer",
                        "require", "verify-ca", "verify-full":
-                       params["sslmode"] = v[0]
+                       postgresJDBC.Params["sslmode"] = v[0]
                }
        }
 
        if v := u.Query().Get("user"); v != "" {
-               params["user"] = v
+               postgresJDBC.User = v
        }
 
        if v := u.Query().Get("password"); v != "" {
-               params["password"] = v
+               postgresJDBC.Password = v
        }
 
-       if params["host"] == "" || params["password"] == "" {
+       if postgresJDBC.Host == "" || postgresJDBC.Password == "" {
                ctx.Logger().WithName("jdbc").
                        V(2).
                        Info("Skipping invalid Postgres URL - no password or 
host found")
                return nil, fmt.Errorf("missing host or password in connection 
string")
        }
 
-       return &postgresJDBC{subname[2:], params}, nil
+       return postgresJDBC, nil
 }
 
-func buildPostgresConnectionString(params map[string]string, includeDbName 
bool) string {
+func BuildPostgresConnectionString(host string, user string, password string, 
dbName string, params map[string]string, includeDbName bool) string {
        data := map[string]string{
                // default user
-               "user": "postgres",
+               "user":     "postgres",
+               "password": password,
+               "host":     host,
+       }
+       if user != "" {
+               data["user"] = user
+       }
+       if h, p, ok := strings.Cut(host, ":"); ok {
+               data["host"] = h
+               data["port"] = p
        }
        for key, val := range params {
-               if key == "host" {
-                       if h, p, found := strings.Cut(val, ":"); found {
-                               data["host"] = h
-                               data["port"] = p
-                               continue
-                       }
-               }
                data[key] = val
        }
 
-       if !includeDbName {
+       if includeDbName {
                data["dbname"] = "postgres"
+               if dbName != "" {
+                       data["dbname"] = dbName
+               }
        }
 
        connStr := joinKeyValues(data, " ")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/trufflehog-3.92.3/pkg/detectors/jdbc/postgres_integration_test.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/postgres_integration_test.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/postgres_integration_test.go       
2025-12-11 12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/postgres_integration_test.go       
2025-12-19 16:15:11.000000000 +0100
@@ -15,6 +15,8 @@
        "github.com/testcontainers/testcontainers-go"
        "github.com/testcontainers/testcontainers-go/modules/postgres"
        "github.com/testcontainers/testcontainers-go/wait"
+
+       logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"
 )
 
 func TestPostgres(t *testing.T) {
@@ -119,7 +121,7 @@
 
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       j, err := parsePostgres(tt.input)
+                       j, err := ParsePostgres(logContext.Background(), 
tt.input)
                        if err != nil {
                                got := result{ParseErr: true}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/trufflehog-3.92.3/pkg/detectors/jdbc/postgres_test.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/postgres_test.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/postgres_test.go   2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/postgres_test.go   2025-12-19 
16:15:11.000000000 +0100
@@ -47,7 +47,7 @@
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                        ctx := logContext.AddLogger(context.Background())
-                       j, err := parsePostgres(ctx, tt.subname)
+                       j, err := ParsePostgres(ctx, tt.subname)
 
                        if tt.shouldBeNil {
                                if j != nil {
@@ -86,14 +86,14 @@
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
                        ctx := logContext.AddLogger(context.Background())
-                       j, err := parsePostgres(ctx, tt.subname)
+                       j, err := ParsePostgres(ctx, tt.subname)
                        if err != nil {
-                               t.Fatalf("parsePostgres() error = %v", err)
+                               t.Fatalf("ParsePostgres() error = %v", err)
                        }
 
-                       pgConn := j.(*postgresJDBC)
-                       if pgConn.params["user"] != tt.wantUsername {
-                               t.Errorf("expected username '%s', got '%s'", 
tt.wantUsername, pgConn.params["user"])
+                       pgConn := j.(*PostgresJDBC)
+                       if pgConn.User != tt.wantUsername {
+                               t.Errorf("expected username '%s', got '%s'", 
tt.wantUsername, pgConn.User)
                        }
                })
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/detectors/jdbc/sqlserver.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/sqlserver.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/sqlserver.go       2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/sqlserver.go       2025-12-19 
16:15:11.000000000 +0100
@@ -4,7 +4,6 @@
        "context"
        "errors"
        "fmt"
-       "net/url"
        "strings"
 
        logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"
@@ -12,13 +11,13 @@
        mssql "github.com/microsoft/go-mssqldb"
 )
 
-type sqlServerJDBC struct {
-       connStr string
+type SqlServerJDBC struct {
+       ConnectionInfo
 }
 
-func (s *sqlServerJDBC) ping(ctx context.Context) pingResult {
+func (s *SqlServerJDBC) ping(ctx context.Context) pingResult {
        return ping(ctx, "mssql", isSqlServerErrorDeterminate,
-               s.connStr)
+               BuildSQLServerConnectionString(s.Host, s.User, s.Password, 
"master", map[string]string{"connection+timeout": "5"}))
 }
 
 func isSqlServerErrorDeterminate(err error) bool {
@@ -35,7 +34,7 @@
        return false
 }
 
-func parseSqlServer(ctx logContext.Context, subname string) (jdbc, error) {
+func ParseSqlServer(ctx logContext.Context, subname string) (jdbc, error) {
        if !strings.HasPrefix(subname, "//") {
                return nil, errors.New("expected connection to start with //")
        }
@@ -43,7 +42,9 @@
 
        port := "1433"
        user := "sa"
+       database := "master"
        var password, host string
+       params := make(map[string]string)
 
        for i, param := range strings.Split(conn, ";") {
                key, value, found := strings.Cut(param, "=")
@@ -59,17 +60,18 @@
                }
 
                switch strings.ToLower(key) {
-               case "password":
-                       password = value
-               case "spring.datasource.password":
+               case "password", "spring.datasource.password", "pwd":
                        password = value
                case "server":
                        host = value
                case "port":
                        port = value
-               case "user", "uid", "user id":
+               case "user", "uid", "user id", "userid":
                        user = value
-
+               case "database", "databasename":
+                       database = value
+               default:
+                       params[key] = value
                }
        }
 
@@ -80,14 +82,23 @@
                return nil, fmt.Errorf("missing host or password in connection 
string")
        }
 
-       urlStr := 
fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=master&connection+timeout=5", 
user, password, host, port)
-       jdbcUrl, err := url.Parse(urlStr)
-       if err != nil {
-               ctx.Logger().WithName("jdbc").
-                       V(3).
-                       Info("Skipping invalid SQL Server URL", "url", urlStr, 
"err", err)
-               return nil, err
-       }
+       return &SqlServerJDBC{
+               ConnectionInfo: ConnectionInfo{
+                       Host:     host + ":" + port,
+                       User:     user,
+                       Password: password,
+                       Database: database,
+                       Params:   params,
+               },
+       }, nil
+}
 
-       return &sqlServerJDBC{connStr: jdbcUrl.String()}, nil
+func BuildSQLServerConnectionString(host, user, password, database string, 
params map[string]string) string {
+       conn := fmt.Sprintf("sqlserver://%s:%s@%s?database=%s", user, password, 
host, database)
+       if len(params) > 0 {
+               for k, v := range params {
+                       conn += fmt.Sprintf("&%s=%s", k, v)
+               }
+       }
+       return conn
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/trufflehog-3.92.3/pkg/detectors/jdbc/sqlserver_test.go 
new/trufflehog-3.92.4/pkg/detectors/jdbc/sqlserver_test.go
--- old/trufflehog-3.92.3/pkg/detectors/jdbc/sqlserver_test.go  2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/detectors/jdbc/sqlserver_test.go  2025-12-19 
16:15:11.000000000 +0100
@@ -2,8 +2,6 @@
 
 import (
        "context"
-       "fmt"
-       "strings"
        "testing"
 
        logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"
@@ -56,7 +54,7 @@
                t.Run(tt.name, func(t *testing.T) {
                        ctx := logContext.AddLogger(context.Background())
 
-                       j, err := parseSqlServer(ctx, tt.subname)
+                       j, err := ParseSqlServer(ctx, tt.subname)
 
                        if tt.shouldBeNil {
                                if j != nil {
@@ -103,17 +101,16 @@
                t.Run(tt.name, func(t *testing.T) {
                        ctx := logContext.AddLogger(context.Background())
 
-                       j, err := parseSqlServer(ctx, tt.subname)
+                       j, err := ParseSqlServer(ctx, tt.subname)
                        if err != nil {
                                t.Fatalf("parseSqlServer() error = %v", err)
                        }
 
-                       sqlServerConn := j.(*sqlServerJDBC)
-                       expectedPrefix := fmt.Sprintf("sqlserver://%s:", 
tt.wantUsername)
+                       sqlServerConn := j.(*SqlServerJDBC)
 
-                       if !strings.Contains(sqlServerConn.connStr, 
expectedPrefix) {
+                       if sqlServerConn.User != tt.wantUsername {
                                t.Errorf("Connection string does not contain 
expected username '%s'\nGot: %s\nExpected to contain: %s",
-                                       tt.wantUsername, sqlServerConn.connStr, 
expectedPrefix)
+                                       tt.wantUsername, sqlServerConn.User, 
tt.wantUsername)
                        }
                })
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/engine/engine.go 
new/trufflehog-3.92.4/pkg/engine/engine.go
--- old/trufflehog-3.92.3/pkg/engine/engine.go  2025-12-11 12:40:06.000000000 
+0100
+++ new/trufflehog-3.92.4/pkg/engine/engine.go  2025-12-19 16:15:11.000000000 
+0100
@@ -1266,6 +1266,7 @@
        switch sourceType {
        case sourcespb.SourceType_SOURCE_TYPE_GIT,
                sourcespb.SourceType_SOURCE_TYPE_GITHUB,
+               sourcespb.SourceType_SOURCE_TYPE_GITHUB_REALTIME,
                sourcespb.SourceType_SOURCE_TYPE_GITLAB,
                sourcespb.SourceType_SOURCE_TYPE_BITBUCKET,
                sourcespb.SourceType_SOURCE_TYPE_GERRIT,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/sources/docker/docker_test.go 
new/trufflehog-3.92.4/pkg/sources/docker/docker_test.go
--- old/trufflehog-3.92.3/pkg/sources/docker/docker_test.go     2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/sources/docker/docker_test.go     2025-12-19 
16:15:11.000000000 +0100
@@ -71,7 +71,7 @@
                Credential: &sourcespb.Docker_Unauthenticated{
                        Unauthenticated: &credentialspb.Unauthenticated{},
                },
-               Images: []string{"quay.io/prometheus/busybox"}, // 
https://quay.io/repository/prometheus/busybox
+               Images: 
[]string{"quay.io/prometheus/node-exporter@sha256:337ff1d356b68d39cef853e8c6345de11ce7556bb34cda8bd205bcf2ed30b565"},
        }
 
        conn := &anypb.Any{}
@@ -109,9 +109,9 @@
        close(chunksChan)
        wg.Wait()
 
-       assert.Equal(t, 944, chunkCounter)
-       assert.Equal(t, 941, layerCounter)
-       assert.Equal(t, 3, historyCounter)
+       assert.Equal(t, 1302, chunkCounter)
+       assert.Equal(t, 1291, layerCounter)
+       assert.Equal(t, 11, historyCounter)
 }
 
 func TestGHCRRegistry(t *testing.T) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/sources/docker/registries.go 
new/trufflehog-3.92.4/pkg/sources/docker/registries.go
--- old/trufflehog-3.92.3/pkg/sources/docker/registries.go      2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/sources/docker/registries.go      2025-12-19 
16:15:11.000000000 +0100
@@ -9,11 +9,21 @@
        "net/url"
        "path"
        "strings"
-       "time"
+
+       "github.com/trufflesecurity/trufflehog/v3/pkg/common"
+
+       "golang.org/x/time/rate"
 )
 
 // defaultHTTPClient defines a shared HTTP client with timeout for all 
registry requests.
-var defaultHTTPClient = &http.Client{Timeout: 10 * time.Second}
+var defaultHTTPClient = common.RetryableHTTPClientTimeout(10)
+
+// registryRateLimiter limits how quickly we make registry API calls across 
all registries.
+// We allow roughly 1 event every 1.5s, with a burst of 2 as a simple 
safeguard against overloading upstream APIs.
+var registryRateLimiter = rate.NewLimiter(rate.Limit(2.0/3.0), 2)
+
+// maxRegistryPageSize defines the maximum number of images to request per 
page from a registry API.
+const maxRegistryPageSize = 100
 
 // Image represents a container image or repository entry in a registry API 
response.
 type Image struct {
@@ -22,10 +32,10 @@
 
 // Registry is an interface for any Docker/OCI registry implementation that 
can list all images under a given namespace.
 type Registry interface {
-       Name() string                           // return name of the registry
-       WithRegistryToken(registryToken string) // set token for registry
-       // TODO: Handle pagination and rate limits for list images API Call
+       Name() string                                                       // 
return name of the registry
+       WithRegistryToken(registryToken string)                             // 
set token for registry
        ListImages(ctx context.Context, namespace string) ([]string, error) // 
list all images
+       WithClient(client *http.Client)                                     // 
return the HTTP client to use
 }
 
 // MakeRegistryFromNamespace returns a Registry implementation
@@ -45,11 +55,12 @@
        return registry
 }
 
-// === DockerHub registry ===
+// === Docker Hub Registry ===
 
-// DockerHub implements the Registry interface for Docker Hub.
+// DockerHub implements the Registry interface for hub.docker.com.
 type DockerHub struct {
-       Token string
+       Token  string
+       Client *http.Client
 }
 
 // dockerhubResp models Docker Hub's /v2/namespaces/<ns>/repositories API 
response.
@@ -66,67 +77,101 @@
        d.Token = registryToken
 }
 
+func (d *DockerHub) WithClient(client *http.Client) {
+       d.Client = client
+}
+
 // ListImages lists all images under a Docker Hub namespace using Docker Hub's 
API.
+//
+// We fetch images in fixed-size pages and keep following the "next" link 
until there
+// are no more pages. A shared rate limiter is used so we don't accidentally 
hammer
+// the Docker Hub API.
 func (d *DockerHub) ListImages(ctx context.Context, namespace string) 
([]string, error) {
-       url := &url.URL{
+       baseURL := &url.URL{
                Scheme: "https",
                Host:   "hub.docker.com",
                Path:   path.Join("v2", "namespaces", namespace, 
"repositories"),
        }
 
-       req, err := http.NewRequestWithContext(ctx, http.MethodGet, 
url.String(), http.NoBody)
-       if err != nil {
-               return nil, err
-       }
+       query := baseURL.Query()
+       query.Set("page_size", fmt.Sprint(maxRegistryPageSize))
+       baseURL.RawQuery = query.Encode()
 
-       if d.Token != "" {
-               req.Header.Set("Authorization", "Bearer "+d.Token)
-       }
+       allImages := []string{}
+       nextURL := baseURL.String()
 
-       resp, err := defaultHTTPClient.Do(req)
-       if err != nil {
-               return nil, err
-       }
+       for {
+               if err := registryRateLimiter.Wait(ctx); err != nil {
+                       return nil, err
+               }
 
-       defer func() {
-               _, _ = io.Copy(io.Discard, resp.Body)
-               _ = resp.Body.Close()
-       }()
+               req, err := http.NewRequestWithContext(ctx, http.MethodGet, 
nextURL, http.NoBody)
+               if err != nil {
+                       return nil, err
+               }
 
-       responseBodyByte, err := io.ReadAll(resp.Body)
-       if err != nil {
-               return nil, err
-       }
+               if d.Token != "" {
+                       req.Header.Set("Authorization", "Bearer "+d.Token)
+               }
+
+               client := d.Client
+               if client == nil {
+                       client = defaultHTTPClient
+               }
+               resp, err := client.Do(req)
+               if err != nil {
+                       return nil, err
+               }
+
+               body, err := io.ReadAll(resp.Body)
+               discardBody(resp)
+               if err != nil {
+                       return nil, err
+               }
 
-       switch resp.StatusCode {
-       case http.StatusOK:
-               var allImages = make([]string, 0)
+               if resp.StatusCode != http.StatusOK {
+                       return nil, fmt.Errorf("failed to list dockerhub 
images: unexpected status code: %d", resp.StatusCode)
+               }
 
-               var listImagesResp dockerhubResp
-               if err := json.Unmarshal(responseBodyByte, &listImagesResp); 
err != nil {
+               var page dockerhubResp
+               if err := json.Unmarshal(body, &page); err != nil {
                        return nil, err
                }
 
-               for _, image := range listImagesResp.Results {
+               for _, image := range page.Results {
                        allImages = append(allImages, fmt.Sprintf("%s/%s", 
namespace, image.Name)) // <namespace>/<image_name>
                }
 
-               return allImages, nil
-       default:
-               return nil, fmt.Errorf("failed to list dockerhub images: 
unexpected status code: %d", resp.StatusCode)
+               if page.Next == "" {
+                       break
+               }
+
+               // Docker Hub sometimes returns an absolute "next" URL and 
sometimes a
+               // relative one. ResolveReference cleans that up for us and 
turns whatever
+               // they send into a proper URL we can call.
+               next, err := url.Parse(page.Next)
+               if err != nil {
+                       return nil, err
+               }
+               nextURL = baseURL.ResolveReference(next).String()
        }
+
+       return allImages, nil
 }
 
 // === Red Hat Quay Registry ===
 
 // Quay implements the Registry interface for Quay.io.
 type Quay struct {
-       Token string
+       Token  string
+       Client *http.Client
 }
 
 // quayResp models the JSON structure returned by Quay's /api/v1/repository 
endpoint.
 type quayResp struct {
-       Repositories []Image `json:"repositories"`
+       Repositories  []Image `json:"repositories"`
+       HasAdditional bool    `json:"has_additional"`
+       NextPage      string  `json:"next_page"`
 }
 
 func (q *Quay) Name() string {
@@ -137,69 +182,96 @@
        q.Token = registryToken
 }
 
+func (q *Quay) WithClient(client *http.Client) {
+       q.Client = client
+}
+
 // ListImages lists all images under a Quay namespace.
+// API reference:
+//
+//     GET 
https://quay.io/api/v1/repository?namespace=<namespace>&public=true&private=true&next_page=<token>
+//
+// We keep following next_page while has_additional is true.
 func (q *Quay) ListImages(ctx context.Context, namespace string) ([]string, 
error) {
        quayNamespace := path.Base(namespace) // quay.io/<namespace> -> 
namespace
-       url := &url.URL{
+       baseURL := &url.URL{
                Scheme: "https",
                Host:   "quay.io",
                Path:   path.Join("api", "v1", "repository"),
        }
 
-       query := url.Query()
-       query.Set("namespace", quayNamespace)
-       query.Set("public", "true")
-       query.Set("private", "true")
-       url.RawQuery = query.Encode()
-
-       req, err := http.NewRequestWithContext(ctx, http.MethodGet, 
url.String(), http.NoBody)
-       if err != nil {
-               return nil, err
-       }
+       allImages := []string{}
+       nextPageToken := ""
 
-       if q.Token != "" {
-               req.Header.Set("Authorization", "Bearer "+q.Token)
-       }
+       for {
+               if err := registryRateLimiter.Wait(ctx); err != nil {
+                       return nil, err
+               }
 
-       resp, err := defaultHTTPClient.Do(req)
-       if err != nil {
-               return nil, err
-       }
+               u := *baseURL
+               query := u.Query()
+               query.Set("namespace", quayNamespace)
+               query.Set("public", "true")
+               query.Set("private", "true")
+
+               // Quay's API controls page size internally; we just fetch page 
by page.
+               if nextPageToken != "" {
+                       query.Set("next_page", nextPageToken)
+               }
+               u.RawQuery = query.Encode()
 
-       defer func() {
-               _, _ = io.Copy(io.Discard, resp.Body)
-               _ = resp.Body.Close()
-       }()
+               req, err := http.NewRequestWithContext(ctx, http.MethodGet, 
u.String(), http.NoBody)
+               if err != nil {
+                       return nil, err
+               }
 
-       responseBodyByte, err := io.ReadAll(resp.Body)
-       if err != nil {
-               return nil, err
-       }
+               if q.Token != "" {
+                       req.Header.Set("Authorization", "Bearer "+q.Token)
+               }
+
+               client := q.Client
+               if client == nil {
+                       client = defaultHTTPClient
+               }
+               resp, err := client.Do(req)
+               if err != nil {
+                       return nil, err
+               }
+
+               body, err := io.ReadAll(resp.Body)
+               discardBody(resp)
+               if err != nil {
+                       return nil, err
+               }
 
-       switch resp.StatusCode {
-       case http.StatusOK:
-               var allImages = make([]string, 0)
+               if resp.StatusCode != http.StatusOK {
+                       return nil, fmt.Errorf("failed to list quay images: 
unexpected status code: %d", resp.StatusCode)
+               }
 
-               var listImagesResp quayResp
-               if err := json.Unmarshal(responseBodyByte, &listImagesResp); 
err != nil {
+               var page quayResp
+               if err := json.Unmarshal(body, &page); err != nil {
                        return nil, err
                }
 
-               for _, image := range listImagesResp.Repositories {
+               for _, image := range page.Repositories {
                        allImages = append(allImages, fmt.Sprintf("%s/%s", 
namespace, image.Name)) // quay.io/<namespace>/<image_name>
                }
 
-               return allImages, nil
-       default:
-               return nil, fmt.Errorf("failed to list quay images: unexpected 
status code: %d", resp.StatusCode)
+               if !page.HasAdditional || page.NextPage == "" {
+                       break
+               }
+               nextPageToken = page.NextPage
        }
+
+       return allImages, nil
 }
 
 // === GHCR Registry ===
 
 // GHCR implements the Registry interface for GHCR.io.
 type GHCR struct {
-       Token string // https://github.com/github/roadmap/issues/558
+       Token  string // https://github.com/github/roadmap/issues/558
+       Client *http.Client
 }
 
 func (g *GHCR) Name() string {
@@ -210,60 +282,134 @@
        g.Token = registryToken
 }
 
-// ListImages lists all images under a Quay namespace.
+func (g *GHCR) WithClient(client *http.Client) {
+       g.Client = client
+}
+
+// GHCR paginates results and includes pagination links in the HTTP Link 
header.
+// The Link header contains URLs for "next", "prev", "first", and "last" pages.
+// Example Link header:
+//
+// 
<https://api.github.com/user/abc/packages?package_type=container&per_page=100&page=2>;
 rel="next",
+// 
<https://api.github.com/user/abc/packages?package_type=container&per_page=100&page=5>;
 rel="last"
+func parseNextLinkURL(linkHeader string) string {
+       if linkHeader == "" {
+               return ""
+       }
+
+       parts := strings.Split(linkHeader, ",")
+       for _, part := range parts {
+               section := strings.Split(strings.TrimSpace(part), ";")
+               if len(section) < 2 {
+                       continue
+               }
+
+               linkPart := strings.TrimSpace(section[0])
+               if !strings.HasPrefix(linkPart, "<") || 
!strings.HasSuffix(linkPart, ">") {
+                       continue
+               }
+               urlStr := strings.Trim(linkPart, "<>")
+
+               rel := ""
+               for _, attr := range section[1:] {
+                       attr = strings.TrimSpace(attr)
+                       if strings.HasPrefix(attr, "rel=") {
+                               rel = strings.Trim(strings.TrimPrefix(attr, 
"rel="), "\"")
+                               break
+                       }
+               }
+
+               if rel == "next" {
+                       return urlStr
+               }
+       }
+
+       return ""
+}
+
+// ListImages lists all images under a GHCR namespace.
+// For GitHub Container Registry the package listing endpoint is:
+//
+//     GET 
https://api.github.com/users/{namespace}/packages?package_type=container&per_page=100
+//
+// The GitHub API is paginated via the Link response header.
 func (g *GHCR) ListImages(ctx context.Context, namespace string) ([]string, 
error) {
        ghcrNamespace := path.Base(namespace) // ghcr.io/<namespace> -> 
namespace
 
-       url := &url.URL{
+       baseURL := &url.URL{
                Scheme: "https",
                Host:   "api.github.com",
                Path:   path.Join("users", ghcrNamespace, "packages"),
        }
 
-       query := url.Query()
-       query.Set("package_type", "container")
-       url.RawQuery = query.Encode()
-
-       req, err := http.NewRequestWithContext(ctx, http.MethodGet, 
url.String(), http.NoBody)
-       if err != nil {
-               return nil, err
-       }
+       allImages := []string{}
+       nextURL := func() string {
+               u := *baseURL
+               q := u.Query()
+               q.Set("package_type", "container")
+               q.Set("per_page", fmt.Sprint(maxRegistryPageSize)) // fetch 
images in batches of 100 per page
+               u.RawQuery = q.Encode()
+               return u.String()
+       }()
 
-       // 
https://stackoverflow.com/questions/72732582/using-github-packages-without-personal-access-token
-       if g.Token != "" {
-               req.Header.Set("Authorization", "Bearer "+g.Token)
-       }
+       for nextURL != "" {
+               if err := registryRateLimiter.Wait(ctx); err != nil {
+                       return nil, err
+               }
 
-       resp, err := defaultHTTPClient.Do(req)
-       if err != nil {
-               return nil, err
-       }
+               req, err := http.NewRequestWithContext(ctx, http.MethodGet, 
nextURL, http.NoBody)
+               if err != nil {
+                       return nil, err
+               }
 
-       defer func() {
-               _, _ = io.Copy(io.Discard, resp.Body)
-               _ = resp.Body.Close()
-       }()
+               // 
https://stackoverflow.com/questions/72732582/using-github-packages-without-personal-access-token
+               if g.Token != "" {
+                       req.Header.Set("Authorization", "Bearer "+g.Token)
+               }
+               // GitHub recommends explicitly sending the v3 media type.
+               req.Header.Set("Accept", "application/vnd.github+json")
 
-       responseBodyByte, err := io.ReadAll(resp.Body)
-       if err != nil {
-               return nil, err
-       }
+               client := g.Client
+               if client == nil {
+                       client = defaultHTTPClient
+               }
+               resp, err := client.Do(req)
+               if err != nil {
+                       return nil, err
+               }
+
+               body, err := io.ReadAll(resp.Body)
+               discardBody(resp)
+               if err != nil {
+                       return nil, err
+               }
 
-       switch resp.StatusCode {
-       case http.StatusOK:
-               var allImages = make([]string, 0)
+               if resp.StatusCode != http.StatusOK {
+                       return nil, fmt.Errorf("failed to list ghcr images: 
unexpected status code: %d", resp.StatusCode)
+               }
 
-               var listImagesResp []Image
-               if err := json.Unmarshal(responseBodyByte, &listImagesResp); 
err != nil {
+               // The GHCR packages list returns an array of package objects. 
We only
+               // care about the "name" field at this layer, so reuse the 
Image struct.
+               var page []Image
+               if err := json.Unmarshal(body, &page); err != nil {
                        return nil, err
                }
 
-               for _, image := range listImagesResp {
+               for _, image := range page {
                        allImages = append(allImages, fmt.Sprintf("%s/%s", 
namespace, image.Name)) // ghcr.io/<namespace>/<image_name>
                }
 
-               return allImages, nil
-       default:
-               return nil, fmt.Errorf("failed to list ghcr images: unexpected 
status code: %d", resp.StatusCode)
+               // Determine if there's another page via the Link header.
+               nextURL = parseNextLinkURL(resp.Header.Get("Link"))
+       }
+
+       return allImages, nil
+}
+
+// Function to discard response body
+func discardBody(resp *http.Response) {
+       if resp != nil && resp.Body != nil {
+               _, _ = io.Copy(io.Discard, resp.Body)
+               _ = resp.Body.Close()
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/trufflehog-3.92.3/pkg/sources/docker/registries_test.go 
new/trufflehog-3.92.4/pkg/sources/docker/registries_test.go
--- old/trufflehog-3.92.3/pkg/sources/docker/registries_test.go 2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/sources/docker/registries_test.go 2025-12-19 
16:15:11.000000000 +0100
@@ -2,6 +2,7 @@
 
 import (
        "fmt"
+       "net/http"
        "slices"
        "testing"
 
@@ -60,3 +61,42 @@
 
        assert.Equal(t, ghcrImages, []string{"ghcr.io/mongodb/kingfisher"})
 }
+
+func TestDockerHubListImages_RateLimitError(t *testing.T) {
+       t.Parallel()
+
+       // Dockerhub registry
+       dockerhub := MakeRegistryFromNamespace("trufflesecurity") // no 
authentication
+
+       // Cast dockerhub to *DockerHub registry to override the HTTP client
+       
dockerhub.WithClient(common.ConstantResponseHttpClient(http.StatusTooManyRequests,
 "{}"))
+       dockerImages, err := dockerhub.ListImages(context.Background(), 
"trufflesecurity") // namespace without any prefix defaults to dockerhub 
registry
+       assert.Error(t, err)
+       assert.Nil(t, dockerImages)
+}
+
+func TestQuayListImages_RateLimitError(t *testing.T) {
+       t.Parallel()
+
+       // Quay.io registry
+       quay := MakeRegistryFromNamespace("quay.io/truffledockerman") // no 
authentication
+       // Cast quay to *Quay registry to override the HTTP client
+       
quay.WithClient(common.ConstantResponseHttpClient(http.StatusTooManyRequests, 
"{}"))
+
+       quayImages, err := quay.ListImages(context.Background(), 
"quay.io/truffledockerman")
+       assert.Error(t, err)
+       assert.Nil(t, quayImages)
+}
+
+func TestGHCRListImages_RateLimitError(t *testing.T) {
+       t.Parallel()
+
+       // GHCR registry
+       ghcr := MakeRegistryFromNamespace("ghcr.io/mongodb") // no 
authentication
+       // Cast ghcr to *GHCR registry to override the HTTP client
+       
ghcr.WithClient(common.ConstantResponseHttpClient(http.StatusTooManyRequests, 
"{}"))
+
+       ghcrImages, err := ghcr.ListImages(context.Background(), 
"ghcr.io/mongodb")
+       assert.Error(t, err)
+       assert.Nil(t, ghcrImages)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/sources/s3/checkpointer.go 
new/trufflehog-3.92.4/pkg/sources/s3/checkpointer.go
--- old/trufflehog-3.92.3/pkg/sources/s3/checkpointer.go        2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/sources/s3/checkpointer.go        2025-12-19 
16:15:11.000000000 +0100
@@ -98,6 +98,7 @@
 type ResumeInfo struct {
        CurrentBucket string `json:"current_bucket"` // Current bucket being 
scanned
        StartAfter    string `json:"start_after"`    // Last processed object 
key
+       Role          string `json:"role"`           // Role used for scanning
 }
 
 // ResumePoint retrieves the last saved checkpoint state if one exists.
@@ -121,7 +122,7 @@
                return resume, nil
        }
 
-       return ResumeInfo{CurrentBucket: resumeInfo.CurrentBucket, StartAfter: 
resumeInfo.StartAfter}, nil
+       return ResumeInfo{CurrentBucket: resumeInfo.CurrentBucket, StartAfter: 
resumeInfo.StartAfter, Role: resumeInfo.Role}, nil
 }
 
 // Complete marks the entire scanning operation as finished and clears the 
resume state.
@@ -215,7 +216,7 @@
                return nil
        }
 
-       encoded, err := json.Marshal(&ResumeInfo{CurrentBucket: bucket, 
StartAfter: lastKey})
+       encoded, err := json.Marshal(&ResumeInfo{CurrentBucket: bucket, 
StartAfter: lastKey, Role: role})
        if err != nil {
                return fmt.Errorf("failed to encode resume info: %w", err)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/trufflehog-3.92.3/pkg/sources/s3/checkpointer_test.go 
new/trufflehog-3.92.4/pkg/sources/s3/checkpointer_test.go
--- old/trufflehog-3.92.3/pkg/sources/s3/checkpointer_test.go   2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/sources/s3/checkpointer_test.go   2025-12-19 
16:15:11.000000000 +0100
@@ -61,6 +61,56 @@
        assert.Equal(t, "key-11", finalResumeInfo.StartAfter)
 }
 
+func TestCheckpointerResumptionWithRole(t *testing.T) {
+       ctx := context.Background()
+
+       // First scan - process 6 objects then interrupt.
+       initialProgress := &sources.Progress{}
+       tracker := NewCheckpointer(ctx, initialProgress)
+       role := "test-role"
+
+       firstPage := &s3.ListObjectsV2Output{
+               Contents: make([]s3types.Object, 12), // Total of 12 objects
+       }
+       for i := range 12 {
+               key := fmt.Sprintf("key-%d", i)
+               firstPage.Contents[i] = s3types.Object{Key: &key}
+       }
+
+       // Process first 6 objects.
+       for i := range 6 {
+               err := tracker.UpdateObjectCompletion(ctx, i, "test-bucket", 
role, firstPage.Contents)
+               assert.NoError(t, err)
+       }
+
+       // Verify resume info is set correctly.
+       resumeInfo, err := tracker.ResumePoint(ctx)
+       require.NoError(t, err)
+       assert.Equal(t, "test-bucket", resumeInfo.CurrentBucket)
+       assert.Equal(t, "key-5", resumeInfo.StartAfter)
+       assert.Equal(t, role, resumeInfo.Role)
+
+       // Resume scan with existing progress.
+       resumeTracker := NewCheckpointer(ctx, initialProgress)
+
+       resumePage := &s3.ListObjectsV2Output{
+               Contents: firstPage.Contents[6:], // Remaining 6 objects
+       }
+
+       // Process remaining objects.
+       for i := range len(resumePage.Contents) {
+               err := resumeTracker.UpdateObjectCompletion(ctx, i, 
"test-bucket", role, resumePage.Contents)
+               assert.NoError(t, err)
+       }
+
+       // Verify final resume info.
+       finalResumeInfo, err := resumeTracker.ResumePoint(ctx)
+       require.NoError(t, err)
+       assert.Equal(t, "test-bucket", finalResumeInfo.CurrentBucket)
+       assert.Equal(t, "key-11", finalResumeInfo.StartAfter)
+       assert.Equal(t, role, finalResumeInfo.Role)
+}
+
 func TestCheckpointerReset(t *testing.T) {
        tests := []struct {
                name string
@@ -112,6 +162,13 @@
                        expectedResumeInfo: ResumeInfo{CurrentBucket: 
"test-bucket", StartAfter: "test-key"},
                },
                {
+                       name: "valid resume info with role",
+                       progress: &sources.Progress{
+                               EncodedResumeInfo: 
`{"current_bucket":"test-bucket","start_after":"test-key","role":"test-role"}`,
+                       },
+                       expectedResumeInfo: ResumeInfo{CurrentBucket: 
"test-bucket", StartAfter: "test-key", Role: "test-role"},
+               },
+               {
                        name:     "empty encoded resume info",
                        progress: &sources.Progress{EncodedResumeInfo: ""},
                },
@@ -122,6 +179,13 @@
                        },
                },
                {
+                       name: "no role in resume info",
+                       progress: &sources.Progress{
+                               EncodedResumeInfo: 
`{"current_bucket":"test-bucket","start_after":"test-key"}`,
+                       },
+                       expectedResumeInfo: ResumeInfo{CurrentBucket: 
"test-bucket", StartAfter: "test-key", Role: ""},
+               },
+               {
                        name: "unmarshal error",
                        progress: &sources.Progress{
                                EncodedResumeInfo: 
`{"current_bucket":123,"start_after":"test-key"}`, // Invalid JSON
@@ -254,6 +318,122 @@
 
                        assert.Equal(t, tt.expectedLowestIncomplete, 
tracker.lowestIncompleteIdx,
                                "Incorrect lowest incomplete index")
+               })
+       }
+}
+func TestCheckpointerUpdateWithRole(t *testing.T) {
+       role := "test-role"
+       tests := []struct {
+               name                     string
+               description              string
+               completedIdx             int
+               pageSize                 int
+               preCompleted             []int
+               expectedKey              string
+               expectedRole             string
+               expectedLowestIncomplete int
+       }{
+               {
+                       name:                     "first object completed",
+                       description:              "Basic case - completing 
first object",
+                       completedIdx:             0,
+                       pageSize:                 3,
+                       expectedKey:              "key-0",
+                       expectedRole:             role,
+                       expectedLowestIncomplete: 1,
+               },
+               {
+                       name:                     "completing missing middle",
+                       description:              "Completing object when 
previous is done",
+                       completedIdx:             1,
+                       pageSize:                 3,
+                       preCompleted:             []int{0},
+                       expectedKey:              "key-1",
+                       expectedRole:             role,
+                       expectedLowestIncomplete: 2,
+               },
+               {
+                       name:                     "all objects completed in 
order",
+                       description:              "Completing final object in 
sequence",
+                       completedIdx:             2,
+                       pageSize:                 3,
+                       preCompleted:             []int{0, 1},
+                       expectedKey:              "key-2",
+                       expectedRole:             role,
+                       expectedLowestIncomplete: 3,
+               },
+               {
+                       name:                     "out of order completion 
before lowest",
+                       description:              "Completing object before 
current lowest incomplete - should not affect checkpoint",
+                       completedIdx:             1,
+                       pageSize:                 4,
+                       preCompleted:             []int{0, 2, 3},
+                       expectedKey:              "key-3",
+                       expectedRole:             role,
+                       expectedLowestIncomplete: 4,
+               },
+               {
+                       name:         "last index in max page",
+                       description:  "Edge case - maximum page size boundary",
+                       completedIdx: 999,
+                       pageSize:     1000,
+                       preCompleted: func() []int {
+                               indices := make([]int, 999)
+                               for i := range indices {
+                                       indices[i] = i
+                               }
+                               return indices
+                       }(),
+                       expectedKey:              "key-999",
+                       expectedRole:             role,
+                       expectedLowestIncomplete: 1000,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       t.Parallel()
+
+                       ctx := context.Background()
+                       progress := new(sources.Progress)
+                       tracker := &Checkpointer{
+                               progress:            progress,
+                               completedObjects:    make([]bool, tt.pageSize),
+                               completionOrder:     make([]int, 0, 
tt.pageSize),
+                               lowestIncompleteIdx: 0,
+                       }
+
+                       page := &s3.ListObjectsV2Output{Contents: 
make([]s3types.Object, tt.pageSize)}
+                       for i := range tt.pageSize {
+                               key := fmt.Sprintf("key-%d", i)
+                               page.Contents[i] = s3types.Object{Key: &key}
+                       }
+
+                       // Setup pre-completed objects.
+                       for _, idx := range tt.preCompleted {
+                               tracker.completedObjects[idx] = true
+                               tracker.completionOrder = 
append(tracker.completionOrder, idx)
+                       }
+
+                       // Find the correct lowest incomplete index after 
pre-completion.
+                       for i := range tt.pageSize {
+                               if !tracker.completedObjects[i] {
+                                       tracker.lowestIncompleteIdx = i
+                                       break
+                               }
+                       }
+
+                       err := tracker.UpdateObjectCompletion(ctx, 
tt.completedIdx, "test-bucket", role, page.Contents)
+                       assert.NoError(t, err, "Unexpected error updating 
progress")
+
+                       var info ResumeInfo
+                       err = 
json.Unmarshal([]byte(progress.EncodedResumeInfo), &info)
+                       assert.NoError(t, err, "Failed to decode resume info")
+                       assert.Equal(t, tt.expectedKey, info.StartAfter, 
"Incorrect resume point")
+                       assert.Equal(t, tt.expectedRole, info.Role, "Incorrect 
role")
+
+                       assert.Equal(t, tt.expectedLowestIncomplete, 
tracker.lowestIncompleteIdx,
+                               "Incorrect lowest incomplete index")
                })
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trufflehog-3.92.3/pkg/sources/s3/s3.go 
new/trufflehog-3.92.4/pkg/sources/s3/s3.go
--- old/trufflehog-3.92.3/pkg/sources/s3/s3.go  2025-12-11 12:40:06.000000000 
+0100
+++ new/trufflehog-3.92.4/pkg/sources/s3/s3.go  2025-12-19 16:15:11.000000000 
+0100
@@ -245,6 +245,7 @@
        startAfter string // The last processed object key within the bucket
        isNewScan  bool   // True if we're starting a fresh scan
        exactMatch bool   // True if we found the exact bucket we were 
previously processing
+       role       string // The role used during the previous scan
 }
 
 // determineResumePosition calculates where to resume scanning from based on 
the last saved checkpoint
@@ -282,6 +283,7 @@
                startAfter: resumePoint.StartAfter,
                index:      startIdx,
                exactMatch: found,
+               role:       resumePoint.Role,
        }
 }
 
@@ -306,12 +308,14 @@
                        "Resume bucket no longer available, starting from 
closest position",
                        "original_bucket", pos.bucket,
                        "position", pos.index,
+                       "role", pos.role,
                )
        default:
                ctx.Logger().Info(
                        "Resuming scan from previous scan's bucket",
                        "bucket", pos.bucket,
                        "position", pos.index,
+                       "role", pos.role,
                )
        }
 
@@ -327,7 +331,7 @@
                )
 
                var startAfter *string
-               if bucket == pos.bucket && pos.startAfter != "" {
+               if bucket == pos.bucket && pos.startAfter != "" && role == 
pos.role {
                        startAfter = &pos.startAfter
                        ctx.Logger().V(3).Info(
                                "Resuming bucket scan",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/trufflehog-3.92.3/pkg/sources/s3/s3_integration_test.go 
new/trufflehog-3.92.4/pkg/sources/s3/s3_integration_test.go
--- old/trufflehog-3.92.3/pkg/sources/s3/s3_integration_test.go 2025-12-11 
12:40:06.000000000 +0100
+++ new/trufflehog-3.92.4/pkg/sources/s3/s3_integration_test.go 2025-12-19 
16:15:11.000000000 +0100
@@ -584,3 +584,63 @@
                assert.Equal(t, wantCount, gotCount, "Chunk count mismatch for 
bucket %s", bucket)
        }
 }
+
+func TestSourceChunksResumptionWithRole(t *testing.T) {
+       t.Parallel()
+
+       ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+       defer cancel()
+       secret, err := common.GetTestSecret(ctx)
+       if err != nil {
+               t.Fatal(fmt.Errorf("failed to access secret: %v", err))
+       }
+
+       s3key := secret.MustGetField("AWS_S3_KEY")
+       s3secret := secret.MustGetField("AWS_S3_SECRET")
+
+       src := new(Source)
+       src.Progress = sources.Progress{
+               Message:           "Bucket: integration-resumption-tests",
+               EncodedResumeInfo: 
"{\"current_bucket\":\"integration-resumption-tests\",\"start_after\":\"test-dir/\",\"role\":\"arn:aws:iam::619888638459:role/s3-test-assume-role\"}",
+               SectionsCompleted: 0,
+               SectionsRemaining: 1,
+       }
+       connection := &sourcespb.S3{
+               Credential: &sourcespb.S3_AccessKey{
+                       AccessKey: &credentialspb.KeySecret{
+                               Key:    s3key,
+                               Secret: s3secret,
+                       },
+               },
+               Buckets:          []string{"integration-resumption-tests"},
+               Roles:            
[]string{"arn:aws:iam::619888638459:role/s3-test-assume-role"},
+               EnableResumption: true,
+       }
+       conn, err := anypb.New(connection)
+       require.NoError(t, err)
+
+       err = src.Init(ctx, "test name", 0, 0, false, conn, 2)
+       require.NoError(t, err)
+
+       chunksCh := make(chan *sources.Chunk)
+       var count int
+
+       cancelCtx, ctxCancel := context.WithCancel(ctx)
+       defer ctxCancel()
+
+       go func() {
+               defer close(chunksCh)
+               err = src.Chunks(cancelCtx, chunksCh)
+               assert.NoError(t, err, "Should not error during scan")
+       }()
+
+       for range chunksCh {
+               count++
+       }
+
+       // Verify that we processed all remaining data on resume.
+       // Also verify that we processed less than the total number of chunks 
for the source.
+       sourceTotalChunkCount := 19787
+       assert.Equal(t, 9638, count, "Should have processed all remaining data 
on resume")
+       assert.Less(t, count, sourceTotalChunkCount, "Should have processed 
less than total chunks on resume")
+}

++++++ trufflehog.obsinfo ++++++
--- /var/tmp/diff_new_pack.4WIDSh/_old  2025-12-27 11:29:22.972870311 +0100
+++ /var/tmp/diff_new_pack.4WIDSh/_new  2025-12-27 11:29:22.976870474 +0100
@@ -1,5 +1,5 @@
 name: trufflehog
-version: 3.92.3
-mtime: 1765453206
-commit: 05cccb53bc9e13bc6d17997db5a6bcc3df44bf2f
+version: 3.92.4
+mtime: 1766157311
+commit: ef6e76c3c4023279497fab4721ffa071a722fd05
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/trufflehog/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.trufflehog.new.1928/vendor.tar.gz differ: char 117, 
line 2

Reply via email to