gitleaks/gitleaks
Patterns and conventions
The unwritten rules that hold the codebase together.
Logging
All logging goes through logging/ — a thin wrapper around zerolog. Use logging.Debug(), logging.Trace(), logging.Warn(), logging.Error(), and logging.Fatal(). Don't import zerolog directly outside the logging package.
A few patterns:
- Use
.Str("path", path)/.Str("rule-id", id)consistently; downstream filtering relies on these field names. - Use
.Trace()for per-fragment / per-allowlist-hit messages —infois reserved for a small number of high-level events. logging.Fatal()callsos.Exit(1). Prefer returning errors except in unrecoverable startup paths.
Regex compilation
Always compile through the project's regexp/ package:
import "github.com/zricethezav/gitleaks/v8/regexp"
var newLineRegexp = regexp.MustCompile("\n")Importing regexp from the standard library directly will silently bypass the build-tag-driven engine selection and break users who build with -tags=gore2regex.
Configuration translation
config.ViperConfig mirrors the TOML schema; config.Config is the runtime form with compiled regexes. The translation lives in ViperConfig.Translate() (config/config.go). When adding a new config field:
- Add it to
ViperConfig(orviperRuleAllowlist/viperGlobalAllowlistfor nested fields). - Translate it into the runtime
Config/Rule/Allowliststruct. - Validate it inside
Rule.ValidateorAllowlist.Validate. - Update
cmd/generate/config/rules/config.tmplif the field needs to render into the defaultgitleaks.toml. - Add a test case to
config/config_test.go.
Backwards compatibility matters: see the AllowList (singular, deprecated) shim fields in ViperConfig for how the project handles renames without breaking existing configs.
Allowlist evaluation
Two helpers in detect/detect.go cover the two evaluation styles:
checkCommitOrPathAllowed— runs only the commit/path checks, used at fragment intake before any regex evaluation.checkFindingAllowed— runs regex/stopword checks against an already-extracted finding.
When in doubt, look at how an existing rule allowlist flows through both functions. The MatchCondition (AND vs OR) controls whether all listed criteria must pass or just one.
Concurrency
The single shared concurrency primitive is Detector.Sema, a *semgroup.Group with a hard limit of 40. Sources schedule fragment work onto it via s.Sema.Go(func() error { ... }). Don't spin up your own worker pools; reuse Sema.
Findings are appended under Detector.findingMutex and commit IDs under Detector.commitMutex. Anywhere else inside the detector should be lock-free.
Errors and exit codes
The CLI has three exit codes documented in the README:
0— no leaks found1— leaks found, or a non-flag error occurred126— unknown flag (returned incmd.Execute)
Avoid os.Exit outside of cmd/. Detection-layer code should return errors and let findingSummaryAndExit (cmd/root.go) decide what to do.
Naming and style
The Go conventions are standard, but a few project-specific patterns:
- Types like
Detector,Source,Fragment,Findingare deliberately central nouns. Don't rename them lightly; they're the public API for library consumers. Deprecated:doc comments mark v9 cleanup targets — search for them when planning the next major release.- Functions that accept a
context.Contextend inContextwhen there's a non-context counterpart (e.g.,NewGitLogCmdandNewGitLogCmdContext). - Prefer
mustGetStringFlag/mustGetIntFlag/mustGetBoolFlag(incmd/root.go) over inlinecmd.Flags().GetStringcalls; they produce uniform fatal-on-error behavior.
Linting
Configured in .golangci.yaml:
reviveruns aterrorseverity — no warnings, only failures.staticcheckis on with most checks; a small set is disabled (-QF1001,-ST1000,-ST1003,-ST1018,-ST1020,-ST1021).misspellcatches typos. There's an allowlisted exception for"addres", "busines", "clas"strings (test inputs).exhaustructenforces named-field initialization for some structs.
If you add a struct that should not be enforced by exhaustruct (e.g., it has many optional fields), add it to the linter exclusion in .golangci.yaml.
Backwards compatibility
The codebase explicitly carries deprecated APIs forward through the v8 series:
detect.Fragmentis a type alias forsources.Fragment(// TODO: This will be replaced with sources.Fragment in v9).detect.RemoteInfoanddetect.git.goexist only to keep import paths stable.- Singular allowlist names (
AllowList,[allowlist]) inViperConfigare TODO-marked for v9.
When you remove or rename anything in config/, detect/, report/, sources/, or cmd/scm/, assume external consumers depend on it and add a deprecation alias rather than a hard break.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.