Open-Source Wikis

/

Gitleaks

/

How to contribute

/

Patterns and conventions

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 — info is reserved for a small number of high-level events.
  • logging.Fatal() calls os.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:

  1. Add it to ViperConfig (or viperRuleAllowlist / viperGlobalAllowlist for nested fields).
  2. Translate it into the runtime Config / Rule / Allowlist struct.
  3. Validate it inside Rule.Validate or Allowlist.Validate.
  4. Update cmd/generate/config/rules/config.tmpl if the field needs to render into the default gitleaks.toml.
  5. 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 found
  • 1 — leaks found, or a non-flag error occurred
  • 126 — unknown flag (returned in cmd.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, Finding are 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.Context end in Context when there's a non-context counterpart (e.g., NewGitLogCmd and NewGitLogCmdContext).
  • Prefer mustGetStringFlag / mustGetIntFlag / mustGetBoolFlag (in cmd/root.go) over inline cmd.Flags().GetString calls; they produce uniform fatal-on-error behavior.

Linting

Configured in .golangci.yaml:

  • revive runs at error severity — no warnings, only failures.
  • staticcheck is on with most checks; a small set is disabled (-QF1001, -ST1000, -ST1003, -ST1018, -ST1020, -ST1021).
  • misspell catches typos. There's an allowlisted exception for "addres", "busines", "clas" strings (test inputs).
  • exhaustruct enforces 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.Fragment is a type alias for sources.Fragment (// TODO: This will be replaced with sources.Fragment in v9).
  • detect.RemoteInfo and detect.git.go exist only to keep import paths stable.
  • Singular allowlist names (AllowList, [allowlist]) in ViperConfig are 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.

Patterns and conventions – Gitleaks wiki | Factory