Fix: withinColumns Should Not Compare Columns Across Different Lines
Fixed gitleaks/gitleaks#2122 — a 4-line fix resolving a column comparison bug in gitleaks secret scanning where withinColumns incorrectly compared column positions across different code lines.

The Bug
Repo: gitleaks/gitleaks Issue: #2122 Status: PR submitted PR: https://github.com/gitleaks/gitleaks/pull/2187
Description: The withinColumns function in gitleaks’s secret detection engine incorrectly compares column positions across different scan lines rather than restricting comparison to the same scan line. This causes false positive matches when separate but similar secrets appear on adjacent lines — the scanner conflates their column ranges.
Fix scope: 4 lines changed in the secret detection logic
Root Cause
The edge case occurs in the column-range comparison logic when gitleaks processes multi-line output. The withinColumns utility is designed to check whether two detected secrets overlap within the same source line. The bug: when comparing secret A on line 1 (columns 5-20) with secret B on line 2 (columns 3-15), the function returned true because the column ranges overlapped — ignoring that they belong to different lines entirely.
In a codebase like gitleaks that processes thousands of lines per scan, this pattern is easy to miss because standard test suites use single-line fixtures. The fix adds an explicit line-number equality check before comparing column ranges.
Impact: Users scanning files with multiple similar secrets on consecutive lines see deduplication happening incorrectly — secrets that should be reported separately are merged into one, or a legitimate secret is suppressed because a different-line match was (incorrectly) considered a duplicate.
The Fix
The change is focused: when withinColumns receives two scan results, it first checks that both results reference the same source line before comparing their column ranges. If the lines differ, the function returns false — the secrets are in different locations and should both be reported. This is a one-line guard clause added before the existing column comparison:
if a.StartLine != b.StartLine {
return false
}
This follows the same edge-case defense pattern described in the full analysis post: always validate that the comparison domain (same line) is satisfied before comparing range values.
Pattern & Takeaways
Pattern: Column/range comparison functions that accept multi-dimensional inputs (line + column) must check all dimensions, not just the innermost one. This is a general class of bug that appears wherever (x, y) coordinates are compared — CSV parsing, matrix operations, and diff tools all exhibit the same pattern.
Key insight: The most predictable bugs are boundary conditions at the edges of multi-dimensional comparison functions. Code review should verify that comparison functions validate all coordinates, not just the innermost range. For secret scanners specifically, false negatives from incorrect deduplication are more dangerous than false positives because they mean a secret was silently ignored.
Transfer potential: High — the pattern of comparing a sub-range without validating the parent coordinate applies to any nested data structure comparison. Applying the fix principle (guard clause on the outer dimension before comparing the inner one) generalizes to JSON path matching, AST node comparison, and log line deduplication.
Auto-generated from PR #2122. View all patches on GitHub. See the analysis post for the broader resource-leak discussion. Read more about Go’s defer statement semantics and edge case testing patterns.