gitleaks/gitleaks
Archive scanning
Recursively extract zip/tar/7z and other archive types and scan their contents. Enabled by --max-archive-depth=N (default 0 = disabled).
Why it's off by default
Archive scanning can multiply scan time significantly — a single 100MB tarball might contain thousands of files. The feature is opt-in to keep default scans fast.
How it works
sources.File (sources/file.go) is the workhorse for any single io.Reader. When it sees one, it asks mholt/archives whether the content looks like a known archive format:
format, _, err := archives.Identify(ctx, s.Path, nil)
if err == nil && format != nil {
// archive!
}If it is an archive, and the current archiveDepth + 1 ≤ MaxArchiveDepth, the source extracts it and constructs a child File source per inner file:
graph TD
Outer[outer file: archive.tar.gz] --> Id[archives.Identify → tar.gz]
Id --> Decompressor[gzip Decompressor]
Decompressor --> Inner[tar inner reader]
Inner --> Inner2[archives.Identify → tar]
Inner2 --> Extractor[tar Extractor]
Extractor --> Files[per-file callback]
Files --> Recurse[new File source<br/>archiveDepth += 1]
Recurse -->|archive again?| Id2[archives.Identify…]
Recurse -->|no| Frag[normal fragment scan]Two helpers handle the two archive flavors:
extractorFragments— for archive containers (zip, tar, 7z, rar, etc.) that contain multiple files. Each inner file becomes a newFilewitharchiveDepthincremented.decompressorFragments— for single-stream compression (gzip, bzip2, xz, lzip, brotli, …). The decompressed stream is fed back intofileFragmentsdirectly without bumping depth.
Inner paths
Inside the archive, a leak's path is reported with ! (defined as InnerPathSeparator in sources/file.go) joining each level:
File: testdata/archives/nested.tar.gz!archives/files.tar!files/.env.prodThis means: .env.prod is inside files.tar, which is inside archives.tar.gz, in the testdata directory. File.FullPath() constructs this string by joining outerPaths (forward-slash normalized) with the current Path.
In the git source
sources.Git (sources/git.go) special-cases binary files that look like archives. When gitdiff.IsBinary is true and isArchive(ctx, gitdiffFile.NewName) returns true, the git source streams the blob via git cat-file blob <commit>:<path> and pipes it through a File source. This means:
gitleaks git --max-archive-depth=2will recurse into archives committed to history.- The blob is read lazily through a
blobReader(a wrapper around thegit cat-fileprocess's stdout).
The blobReader.Close() carefully drains and waits to avoid zombie processes.
Format support
Whatever mholt/archives supports. As of the current dependency version (v0.1.2):
- Compression formats: brotli, bzip2, gzip, lz4, lzip, snappy, sz, xz, zlib, zstd
- Archive formats: 7z, rar, tar, zip
If archives.Identify can't determine the format, the file is scanned as plain content (the error is swallowed — see s.Fragments in file.go).
Format identification with seekers
A few archive formats (notably 7-zip and zip) need a seekable reader. If the underlying reader doesn't satisfy io.Seeker and io.ReaderAt, the source spills the content to a temp file via os.CreateTemp("", "gitleaks-archive-"), copies the data, and feeds the temp file to the extractor. The temp file is removed on exit.
Tuning
--max-archive-depth=1 enables a single level of recursion. =0 (the default) disables it entirely. Higher values are bounded by the archive nesting in your data — recursion stops naturally when a layer doesn't look like an archive.
A warning is logged once per attempted-but-blocked archive when the feature is enabled but depth is exceeded. When the feature is disabled, the same condition logs at trace level instead, keeping the output quiet.
Related
- systems/sources —
File,Files, andGitsource details - Decoding — a different kind of recursion (encoded substrings rather than nested files)
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.