Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package gitleaks for openSUSE:Factory checked in at 2025-01-01 23:07:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/gitleaks (Old) and /work/SRC/openSUSE:Factory/.gitleaks.new.1881 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "gitleaks" Wed Jan 1 23:07:57 2025 rev:16 rq:1234128 version:8.22.1 Changes: -------- --- /work/SRC/openSUSE:Factory/gitleaks/gitleaks.changes 2024-12-22 18:04:25.608315980 +0100 +++ /work/SRC/openSUSE:Factory/.gitleaks.new.1881/gitleaks.changes 2025-01-01 23:08:24.889709264 +0100 @@ -1,0 +2,14 @@ +Tue Dec 31 10:22:01 UTC 2024 - opensuse_buildserv...@ojkastl.de + +- Update to version 8.22.1: + * Entropy trace (#1659) + * build: add 'toolchain' to go.mod (#1682) + * refactor(detect): create readUntilSafeBoundary + add tests + (#1676) + * twitter really does suck ass now + * chore(tests): test cases for generate.go (#1623) + * fix: only use non-empty secret groups (#1632) + * build: upgrade sprig v2->v3 (#1674) + * fix: generate report file even if no findings (#1673) + +------------------------------------------------------------------- Old: ---- gitleaks-8.22.0.obscpio New: ---- gitleaks-8.22.1.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ gitleaks.spec ++++++ --- /var/tmp/diff_new_pack.JcLv6n/_old 2025-01-01 23:08:25.545736204 +0100 +++ /var/tmp/diff_new_pack.JcLv6n/_new 2025-01-01 23:08:25.549736369 +0100 @@ -18,7 +18,7 @@ Name: gitleaks -Version: 8.22.0 +Version: 8.22.1 Release: 0 Summary: Protect and discover secrets using Gitleaks License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.JcLv6n/_old 2025-01-01 23:08:25.589738011 +0100 +++ /var/tmp/diff_new_pack.JcLv6n/_new 2025-01-01 23:08:25.593738175 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/zricethezav/gitleaks</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v8.22.0</param> + <param name="revision">v8.22.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.JcLv6n/_old 2025-01-01 23:08:25.621739326 +0100 +++ /var/tmp/diff_new_pack.JcLv6n/_new 2025-01-01 23:08:25.625739490 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/zricethezav/gitleaks</param> - <param name="changesrevision">a91c6717a0bac43c292234ea0ead531e99ed2c55</param></service></servicedata> + <param name="changesrevision">b69b5157a01fe4234a74a9acd186044152a80c43</param></service></servicedata> (No newline at EOF) ++++++ gitleaks-8.22.0.obscpio -> gitleaks-8.22.1.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/README.md new/gitleaks-8.22.1/README.md --- old/gitleaks-8.22.0/README.md 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/README.md 2024-12-30 17:15:51.000000000 +0100 @@ -19,9 +19,6 @@ <a href="https://github.com/gitleaks/gitleaks-action"> <img alt="gitleaks badge" src="https://img.shields.io/badge/protected%20by-gitleaks-blue"> </a> - <a href="https://twitter.com/intent/follow?screen_name=zricethezav"> - <img src="https://img.shields.io/twitter/follow/zricethezav?label=Follow%20zricethezav&style=social&color=blue" alt="Follow @zricethezav" /> - </a> </p> </p> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/cmd/generate/config/utils/generate_test.go new/gitleaks-8.22.1/cmd/generate/config/utils/generate_test.go --- old/gitleaks-8.22.0/cmd/generate/config/utils/generate_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/gitleaks-8.22.1/cmd/generate/config/utils/generate_test.go 2024-12-30 17:15:51.000000000 +0100 @@ -0,0 +1,235 @@ +package utils + +import ( + "testing" +) + +func TestGenerateSemiGenericRegex(t *testing.T) { + tests := []struct { + name string + identifiers []string + secretRegex string + isCaseInsensitive []bool + validStrings []string + invalidStrings []string + }{ + { + name: "secret is case sensitive, if isCaseInsensitive is false", + identifiers: []string{"api_key"}, + secretRegex: `[a-z]{3}`, + isCaseInsensitive: []bool{false}, + validStrings: []string{"api_key=xxx"}, + invalidStrings: []string{"api_key=XXX", "api_key=xXx"}, + }, + { + name: "secret is case insensitive, if isCaseInsensitive is true", + identifiers: []string{"api_key"}, + secretRegex: `[a-z]{3}`, + isCaseInsensitive: []bool{true}, + validStrings: []string{"api_key=xxx", "api_key=XXX", "api_key=xXx"}, + invalidStrings: []string{"api_key=x!x"}, + }, + { + name: "identifier is case insensitive, regardless of isCaseInsensitive", + identifiers: []string{"api_key"}, + secretRegex: `[a-z]{3}`, + isCaseInsensitive: []bool{true, false}, + validStrings: []string{"api_key=xxx", "ApI_KeY=xxx", "aPi_kEy=xxx", "API_KEY=xxx"}, + invalidStrings: []string{"api!key=xxx"}, + }, + { + name: "identifier can be case sensitive", + identifiers: []string{"(?-i:[Aa]pi_?[Kk]ey|API_?KEY)"}, + secretRegex: `[a-z]{3}`, + isCaseInsensitive: []bool{true, false}, + validStrings: []string{"apikey=xxx", "ApiKey=xxx", "Apikey=xxx", "APIKEY=xxx", "api_key=xxx"}, + invalidStrings: []string{"ApIKeY=xxx", "aPikEy=xxx"}, + }, + { + name: "identifier can be part of a longer word", + identifiers: []string{"key"}, + secretRegex: `[a-z]{3}`, + isCaseInsensitive: []bool{true, false}, + validStrings: []string{"mykey=xxx", "keys=xxx", "key1=xxx", "keystore=xxx", "monkey=xxx"}, + invalidStrings: []string{}, + }, + { + name: "identifier may be followed by specific characters", + identifiers: []string{"api_key"}, + secretRegex: `[a-z]{3}`, + isCaseInsensitive: []bool{true, false}, + validStrings: []string{ + "api_key-----=xxx", + "api_key.....=xxx", + "api_key_____=xxx", + "'''api_key'''=xxx", + `"""api_key"""=xxx`, + "api_key =xxx", + "api_key\t\t\t\t\t=xxx", + "api_key\n\n\n=xxx", // potentially invalid?, + "api_key\r\n=xxx", + "api_key|||=xxx", + }, + invalidStrings: []string{ + "api_key&=xxx", + "$api_key$=xxx", + "%api_key%=xxx", + "api_key[0]=xxx", + "api_key/*REMOVE*/=xxx", + }, + }, + { + name: "identifier and secret must be separated by specific operators", + identifiers: []string{"api_key"}, + secretRegex: `[a-z]{3}`, + isCaseInsensitive: []bool{true, false}, + validStrings: []string{ + "api_key=xxx", + "api_key: xxx", + "<api_key>xxx", + "api_key:=xxx", + "api_key:::=xxx", + "api_key||:=xxx", + "api_key <= xxx", + "api_key => xxx", + "api_key ?= xxx", + }, + invalidStrings: []string{ + "api_keyxxx", + "api_key\txxx", // potentially valid in a tab-separated file + "api_key, xxx", + "api_key; xxx", + "api_key<xxx>", + "api_key&xxx", + "api_key = true ? 'xxx' : 'yyy'", + }, + }, + { + name: "secret is limited by specific boundaries", + identifiers: []string{"api_key"}, + secretRegex: `[a-z]{3}`, + isCaseInsensitive: []bool{true, false}, + validStrings: []string{ + "api_key= xxx ", + "api_key=xxx\n", + "api_key=xxx\r\n", + "api_key=\n\n\n\n\nxxx", // potentially invalid (e.g. .env.example) + "api_key=\r\n\r\nxxx", + "api_key=\t\t\t\txxx\t", + "api_key======xxx;", + "api_key='''xxx'''", + `api_key="""xxx"""`, + "api_key=```xxx```", + `api_key="xxx'`, // could try to match only same opening and closing quotes, might not be worth the complexity + `api_key="don't do it!"`, + `api_key="xxx;notpartofthematch"`, + }, + invalidStrings: []string{ + "api_key=_xxx", + "api_key=xxx_", + "api_key=$xxx", + "api_key=%xxx%", + "api_key=[xxx]", + "api_key=(xxx)", + "api_key=<xxx>", + "api_key={xxx}", + "<api_key>xxx</api_key>", // potentially valid + "example.com?api_key=xxx&other=yyy", // potentially valid + }, + }, + // Note: these test cases do not necessarily prescribe the expected behavior of the function, + // but rather document the current behavior, to ensure that future changes are intentional. + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for _, isCaseInsensitive := range tt.isCaseInsensitive { + regex := GenerateSemiGenericRegex(tt.identifiers, tt.secretRegex, isCaseInsensitive) + for _, validString := range tt.validStrings { + if !regex.MatchString(validString) { + t.Errorf("Expected match, but got none, \nfor GenerateSemiGenericRegex(%v, /%v/, caseInsensitive=%v).MatchString(`%v`)\n%v", + tt.identifiers, tt.secretRegex, isCaseInsensitive, validString, regex) + } + } + for _, invalidString := range tt.invalidStrings { + if regex.MatchString(invalidString) { + t.Errorf("Expected no match, but got one, \nfor GenerateSemiGenericRegex(%v, /%v/, caseInsensitive=%v).MatchString(`%v`)\n%v", + tt.identifiers, tt.secretRegex, isCaseInsensitive, invalidString, regex) + } + } + } + }) + } +} + +func TestGenerateUniqueTokenRegex(t *testing.T) { + tests := []struct { + name string + secretRegex string + isCaseInsensitive bool + validStrings []string + invalidStrings []string + }{ + { + name: "case sensitive secret", + secretRegex: `[a-c]{3}`, + isCaseInsensitive: false, + validStrings: []string{"abc"}, + invalidStrings: []string{"ABC", "Abc"}, + }, + { + name: "case insensitive secret", + secretRegex: `[a-c]{3}`, + isCaseInsensitive: true, + validStrings: []string{"abc", "ABC", "Abc"}, + invalidStrings: []string{"123"}, + }, + { + name: "allowed boundaries", + secretRegex: `[a-c]{3}`, + isCaseInsensitive: false, + validStrings: []string{ + "abc", + " abc ", + "\nabc\n", + "\r\nabc\r\n", + "\tabc\t", + "'abc'", + `"abc"`, + "```abc```", + "my abc's", + ".com?abc", + }, + invalidStrings: []string{ + "abcabc", + "_abc_", + ".com?abc&def", // potentially valid + "/*abc*/", + "<abc>", + "<str>abc</str>", // potentially valid + "{{{abc}}}", + "abc, d", + }, + }, + // Note: these test cases do not necessarily prescribe the expected behavior of the function, + // but rather document the current behavior, to ensure that future changes are intentional. + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + regex := GenerateUniqueTokenRegex(tt.secretRegex, tt.isCaseInsensitive) + for _, validString := range tt.validStrings { + if !regex.MatchString(validString) { + t.Errorf("Expected match, but got none, \nfor GenerateUniqueTokenRegex(/%v/, caseInsensitive=%v).MatchString(`%v`)\n%v", + tt.secretRegex, tt.isCaseInsensitive, validString, regex) + } + } + for _, invalidString := range tt.invalidStrings { + if regex.MatchString(invalidString) { + t.Errorf("Expected no match, but got one, \nfor GenerateUniqueTokenRegex(/%v/, caseInsensitive=%v).MatchString(`%v`)\n%v", + tt.secretRegex, tt.isCaseInsensitive, invalidString, regex) + } + } + }) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/cmd/root.go new/gitleaks-8.22.1/cmd/root.go --- old/gitleaks-8.22.0/cmd/root.go 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/cmd/root.go 2024-12-30 17:15:51.000000000 +0100 @@ -378,7 +378,7 @@ } // write report if desired - if detector.Reporter != nil && len(findings) > 0 { + if detector.Reporter != nil { var ( file io.WriteCloser reportErr error diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/detect/detect.go new/gitleaks-8.22.1/detect/detect.go --- old/gitleaks-8.22.0/detect/detect.go 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/detect/detect.go 2024-12-30 17:15:51.000000000 +0100 @@ -411,21 +411,39 @@ finding.Secret = groups[r.SecretGroup] } else { // If |secretGroup| is not set, we will use the first suitable capture group. - if len(groups) == 2 { - // Use the only group. - finding.Secret = groups[1] - } else { - // Use the first non-empty group. - for _, s := range groups[1:] { - if len(s) > 0 { - finding.Secret = s - break - } + for _, s := range groups[1:] { + if len(s) > 0 { + finding.Secret = s + break } } } } + // check entropy + entropy := shannonEntropy(finding.Secret) + finding.Entropy = float32(entropy) + if r.Entropy != 0.0 { + if entropy <= r.Entropy { + logger.Trace(). + Float32("entropy", finding.Entropy). + Msg("Skipping finding due to low entropy") + // entropy is too low, skip this finding + continue + } + // NOTE: this is a goofy hack to get around the fact there golang's regex engine + // does not support positive lookaheads. Ideally we would want to add a + // restriction on generic rules regex that requires the secret match group + // contains both numbers and alphabetical characters, not just alphabetical characters. + // What this bit of code does is check if the ruleid is prepended with "generic" and enforces the + // secret contains both digits and alphabetical characters. + // TODO: this should be replaced with stop words + if strings.HasPrefix(r.RuleID, "generic") { + if !containsDigit(finding.Secret) { + continue + } + } + } // check if the regexTarget is defined in the allowlist "regexes" entry // or if the secret is in the list of stopwords globalAllowlistTarget := finding.Secret @@ -495,29 +513,6 @@ continue MatchLoop } } - - // check entropy - entropy := shannonEntropy(finding.Secret) - finding.Entropy = float32(entropy) - if r.Entropy != 0.0 { - if entropy <= r.Entropy { - // entropy is too low, skip this finding - continue - } - // NOTE: this is a goofy hack to get around the fact there golang's regex engine - // does not support positive lookaheads. Ideally we would want to add a - // restriction on generic rules regex that requires the secret match group - // contains both numbers and alphabetical characters, not just alphabetical characters. - // What this bit of code does is check if the ruleid is prepended with "generic" and enforces the - // secret contains both digits and alphabetical characters. - // TODO: this should be replaced with stop words - if strings.HasPrefix(r.RuleID, "generic") { - if !containsDigit(finding.Secret) { - continue - } - } - } - findings = append(findings, finding) } return findings diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/detect/directory.go new/gitleaks-8.22.1/detect/directory.go --- old/gitleaks-8.22.0/detect/directory.go 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/detect/directory.go 2024-12-30 17:15:51.000000000 +0100 @@ -1,6 +1,7 @@ package detect import ( + "bufio" "bytes" "io" "os" @@ -49,64 +50,32 @@ } } - // Buffer to hold file chunks - buf := make([]byte, chunkSize) - totalLines := 0 + var ( + // Buffer to hold file chunks + reader = bufio.NewReaderSize(f, chunkSize) + buf = make([]byte, chunkSize) + totalLines = 0 + ) for { - n, err := f.Read(buf) - if n > 0 { - // TODO: optimization could be introduced here - if mimetype, err := filetype.Match(buf[:n]); err != nil { - return nil - } else if mimetype.MIME.Type == "application" { - return nil // skip binary files - } - - // If the chunk doesn't end in a newline, peek |maxPeekSize| until we find one. - // This hopefully avoids splitting - // See: https://github.com/gitleaks/gitleaks/issues/1651 - var ( - peekBuf = bytes.NewBuffer(buf[:n]) - tempBuf = make([]byte, 1) - newlineCount = 0 // Tracks consecutive newlines - ) - for { - data := peekBuf.Bytes() - if len(data) == 0 { - break - } - - // Check if the last character is a newline. - lastChar := data[len(data)-1] - if lastChar == '\n' || lastChar == '\r' { - newlineCount++ - - // Stop if two consecutive newlines are found - if newlineCount >= 2 { - break - } - } else { - newlineCount = 0 // Reset if a non-newline character is found - } + n, err := reader.Read(buf) - // Stop growing the buffer if it reaches maxSize - if (peekBuf.Len() - n) >= maxPeekSize { - break - } - - // Read additional data into a temporary buffer - m, readErr := f.Read(tempBuf) - if m > 0 { - peekBuf.Write(tempBuf[:m]) + // "Callers should always process the n > 0 bytes returned before considering the error err." + // https://pkg.go.dev/io#Reader + if n > 0 { + // Only check the filetype at the start of file. + if totalLines == 0 { + // TODO: could other optimizations be introduced here? + if mimetype, err := filetype.Match(buf[:n]); err != nil { + return nil + } else if mimetype.MIME.Type == "application" { + return nil // skip binary files } + } - // Stop if EOF is reached - if readErr != nil { - if readErr == io.EOF { - break - } - return readErr - } + // Try to split chunks across large areas of whitespace, if possible. + peekBuf := bytes.NewBuffer(buf[:n]) + if readErr := readUntilSafeBoundary(reader, n, maxPeekSize, peekBuf); readErr != nil { + return readErr } // Count the number of newlines in this chunk @@ -145,3 +114,73 @@ return d.findings, nil } + +// readUntilSafeBoundary consumes |f| until it finds two consecutive `\n` characters, up to |maxPeekSize|. +// This hopefully avoids splitting. (https://github.com/gitleaks/gitleaks/issues/1651) +func readUntilSafeBoundary(r *bufio.Reader, n int, maxPeekSize int, peekBuf *bytes.Buffer) error { + if peekBuf.Len() == 0 { + return nil + } + + // Does the buffer end in consecutive newlines? + var ( + data = peekBuf.Bytes() + lastChar = data[len(data)-1] + newlineCount = 0 // Tracks consecutive newlines + ) + if isWhitespace(lastChar) { + for i := len(data) - 1; i >= 0; i-- { + lastChar = data[i] + if lastChar == '\n' { + newlineCount++ + + // Stop if two consecutive newlines are found + if newlineCount >= 2 { + return nil + } + } else if lastChar == '\r' || lastChar == ' ' || lastChar == '\t' { + // The presence of other whitespace characters (`\r`, ` `, `\t`) shouldn't reset the count. + // (Intentionally do nothing.) + } else { + break + } + } + } + + // If not, read ahead until we (hopefully) find some. + newlineCount = 0 + for { + data = peekBuf.Bytes() + // Check if the last character is a newline. + lastChar = data[len(data)-1] + if lastChar == '\n' { + newlineCount++ + + // Stop if two consecutive newlines are found + if newlineCount >= 2 { + break + } + } else if lastChar == '\r' || lastChar == ' ' || lastChar == '\t' { + // The presence of other whitespace characters (`\r`, ` `, `\t`) shouldn't reset the count. + // (Intentionally do nothing.) + } else { + newlineCount = 0 // Reset if a non-newline character is found + } + + // Stop growing the buffer if it reaches maxSize + if (peekBuf.Len() - n) >= maxPeekSize { + break + } + + // Read additional data into a temporary buffer + b, err := r.ReadByte() + if err != nil { + if err == io.EOF { + break + } + return err + } + peekBuf.WriteByte(b) + } + return nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/detect/directory_test.go new/gitleaks-8.22.1/detect/directory_test.go --- old/gitleaks-8.22.0/detect/directory_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/gitleaks-8.22.1/detect/directory_test.go 2024-12-30 17:15:51.000000000 +0100 @@ -0,0 +1,72 @@ +package detect + +import ( + "bufio" + "bytes" + "io" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_readUntilSafeBoundary(t *testing.T) { + // Arrange + cases := []struct { + name string + r io.Reader + expected string + }{ + // Current split is fine, exit early. + { + name: "safe original split - LF", + r: strings.NewReader("abc\n\ndefghijklmnop\n\nqrstuvwxyz"), + expected: "abc\n\n", + }, + { + name: "safe original split - CRLF", + r: strings.NewReader("a\r\n\r\nbcdefghijklmnop\n"), + expected: "a\r\n\r\n", + }, + // Current split is bad, look for a better one. + { + name: "safe split - LF", + r: strings.NewReader("abcdefg\nhijklmnop\n\nqrstuvwxyz"), + expected: "abcdefg\nhijklmnop\n\n", + }, + { + name: "safe split - CRLF", + r: strings.NewReader("abcdefg\r\nhijklmnop\r\n\r\nqrstuvwxyz"), + expected: "abcdefg\r\nhijklmnop\r\n\r\n", + }, + { + name: "safe split - blank line", + r: strings.NewReader("abcdefg\nhijklmnop\n\t \t\nqrstuvwxyz"), + expected: "abcdefg\nhijklmnop\n\t \t\n", + }, + // Current split is bad, exhaust options. + { + name: "no safe split", + r: strings.NewReader("abcdefg\nhijklmnopqrstuvwxyz"), + expected: "abcdefg\nhijklmnopqrstuvwx", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + buf := make([]byte, 5) + n, err := c.r.Read(buf) + require.NoError(t, err) + + // Act + reader := bufio.NewReader(c.r) + peekBuf := bytes.NewBuffer(buf[:n]) + err = readUntilSafeBoundary(reader, n, 20, peekBuf) + require.NoError(t, err) + + // Assert + t.Logf(peekBuf.String()) + require.Equal(t, c.expected, string(peekBuf.Bytes())) + }) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/detect/reader.go new/gitleaks-8.22.1/detect/reader.go --- old/gitleaks-8.22.0/detect/reader.go 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/detect/reader.go 2024-12-30 17:15:51.000000000 +0100 @@ -2,6 +2,7 @@ import ( "bufio" + "bytes" "io" "github.com/zricethezav/gitleaks/v8/report" @@ -10,18 +11,23 @@ // DetectReader accepts an io.Reader and a buffer size for the reader in KB func (d *Detector) DetectReader(r io.Reader, bufSize int) ([]report.Finding, error) { reader := bufio.NewReader(r) - buf := make([]byte, 0, 1000*bufSize) + buf := make([]byte, 1000*bufSize) findings := []report.Finding{} for { - n, err := reader.Read(buf[:cap(buf)]) + n, err := reader.Read(buf) // "Callers should always process the n > 0 bytes returned before considering the error err." // https://pkg.go.dev/io#Reader if n > 0 { - buf = buf[:n] + // Try to split chunks across large areas of whitespace, if possible. + peekBuf := bytes.NewBuffer(buf[:n]) + if readErr := readUntilSafeBoundary(reader, n, maxPeekSize, peekBuf); readErr != nil { + return findings, readErr + } + fragment := Fragment{ - Raw: string(buf), + Raw: peekBuf.String(), } for _, finding := range d.Detect(fragment) { findings = append(findings, finding) @@ -32,10 +38,10 @@ } if err != nil { - if err != io.EOF { - return findings, err + if err == io.EOF { + break } - break + return findings, err } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/detect/reader_test.go new/gitleaks-8.22.1/detect/reader_test.go --- old/gitleaks-8.22.0/detect/reader_test.go 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/detect/reader_test.go 2024-12-30 17:15:51.000000000 +0100 @@ -1,11 +1,12 @@ package detect import ( - "github.com/stretchr/testify/require" "io" "strings" "testing" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) @@ -13,11 +14,17 @@ type mockReader struct { data []byte + read bool } func (r *mockReader) Read(p []byte) (n int, err error) { + if r.read { + return 0, io.EOF + } + // Copy data to the provided buffer. n = copy(p, r.data) + r.read = true // Return io.EOF along with the bytes. return n, io.EOF diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/detect/utils.go new/gitleaks-8.22.1/detect/utils.go --- old/gitleaks-8.22.0/detect/utils.go 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/detect/utils.go 2024-12-30 17:15:51.000000000 +0100 @@ -190,3 +190,7 @@ } return false } + +func isWhitespace(ch byte) bool { + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/go.mod new/gitleaks-8.22.1/go.mod --- old/gitleaks-8.22.0/go.mod 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/go.mod 2024-12-30 17:15:51.000000000 +0100 @@ -2,9 +2,11 @@ go 1.23 +toolchain go1.23.4 + require ( github.com/BobuSumisu/aho-corasick v1.0.3 - github.com/Masterminds/sprig v2.22.0+incompatible + github.com/Masterminds/sprig/v3 v3.3.0 github.com/charmbracelet/lipgloss v0.5.0 github.com/fatih/semgroup v1.2.0 github.com/gitleaks/go-gitdiff v0.9.1 @@ -18,12 +20,12 @@ ) require ( + dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect @@ -32,6 +34,7 @@ github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 // indirect github.com/muesli/termenv v0.15.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/tetratelabs/wazero v1.8.2 // indirect github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect golang.org/x/crypto v0.29.0 // indirect @@ -48,7 +51,7 @@ github.com/pelletier/go-toml v1.9.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/afero v1.6.0 // indirect - github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/go.sum new/gitleaks-8.22.1/go.sum --- old/gitleaks-8.22.0/go.sum 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/go.sum 2024-12-30 17:15:51.000000000 +0100 @@ -36,6 +36,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8a+4nPE9g= github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= @@ -43,10 +45,10 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -81,6 +83,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/semgroup v1.2.0 h1:h/OLXwEM+3NNyAdZEpMiH1OzfplU09i2qXPVThGZvyg= github.com/fatih/semgroup v1.2.0/go.mod h1:1KAD4iIYfXjE4U13B48VM4z9QUwV5Tt8O4rS879kgm8= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -186,8 +190,6 @@ github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -198,11 +200,13 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb h1:w1g9wNDIE/pHSTmAaUhv4TZQuPBS6GV3mMz5hkgziIU= @@ -255,12 +259,16 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -268,8 +276,9 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gitleaks-8.22.0/report/template.go new/gitleaks-8.22.1/report/template.go --- old/gitleaks-8.22.0/report/template.go 2024-12-20 16:53:01.000000000 +0100 +++ new/gitleaks-8.22.1/report/template.go 2024-12-30 17:15:51.000000000 +0100 @@ -6,7 +6,7 @@ "os" "text/template" - "github.com/Masterminds/sprig" + "github.com/Masterminds/sprig/v3" ) type TemplateReporter struct { ++++++ gitleaks.obsinfo ++++++ --- /var/tmp/diff_new_pack.JcLv6n/_old 2025-01-01 23:08:25.909751153 +0100 +++ /var/tmp/diff_new_pack.JcLv6n/_new 2025-01-01 23:08:25.917751482 +0100 @@ -1,5 +1,5 @@ name: gitleaks -version: 8.22.0 -mtime: 1734709981 -commit: a91c6717a0bac43c292234ea0ead531e99ed2c55 +version: 8.22.1 +mtime: 1735575351 +commit: b69b5157a01fe4234a74a9acd186044152a80c43 ++++++ vendor.tar.gz ++++++ ++++ 17126 lines of diff (skipped)