scanner

package
v0.0.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var RepoConfigNames = []string{".confessecrets.yaml", ".confessecrets.yml"}

RepoConfigNames are the file names looked for at a repository root (a directory containing a ".git" entry). The first one found provides a repo-local config that overrides the base config for files within that repository.

Functions

func ClearFindingFilters added in v0.0.8

func ClearFindingFilters()

ClearFindingFilters removes every registered global finding filter.

func DedupeFindings added in v0.0.8

func DedupeFindings(emit func([]Finding) error) func([]Finding) error

DedupeFindings wraps an emit callback so each distinct (file, secret value) pair — the value identified by its ValueSHA256 — is reported only the first time it is seen; later findings carrying the same value in the same file are dropped. The same value appearing in a *different* file is still reported: every file needing remediation surfaces, while repeats of a value within one file — or across the many historical versions of one file in a git history scan — collapse to the first occurrence. Because both Scan and ScanGitHistory emit in a stable order (walk order and oldest-commit-first respectively), "first seen" is deterministic; in a history scan the survivor is the oldest commit that introduced the value to that path.

Filtered findings (kept via IncludeFiltered) are tracked separately from unfiltered ones, so a value's filtered occurrence does not suppress a real finding of the same value. Values embedded under Correlated are not considered — a correlated partner never suppresses a later top-level finding. Batches left empty after deduplication are not emitted.

func IsNameSecretIndicator added in v0.0.8

func IsNameSecretIndicator(name string, rules []Rule) bool

IsNameSecretIndicator reports whether name signals a secret under any of rules: it matches at least one of a rule's name patterns and none of that rule's IgnoreNamePatterns. It is the name gate of the name-driven pipeline, exposed for callers embedding the scanner's heuristics.

func IsSecretValue added in v0.0.8

func IsSecretValue(name, value string, rules []Rule, path string) bool

IsSecretValue reports whether value reads as a secret under any of rules when paired with name — the value side of the name-driven pipeline, without the name gate (pair it with IsNameSecretIndicator for the full pipeline). Per rule, the ignore lists and placeholder checks run first, then definite secret evidence (JWT, private key, URL credentials, connection string) or the likely-secret heuristics decide. path, when non-empty, supplies the per-file hints (language/format, dependency manifest) some heuristics read; pass "" when no file applies.

func IsSourceFile added in v0.0.3

func IsSourceFile(path string) bool

IsSourceFile reports whether path is a source-code file handled by the tree-sitter SourceDetector (as opposed to a structured-config format). It lets callers include or exclude source scanning by file.

func ParseHumanDuration added in v0.0.8

func ParseHumanDuration(s string) (time.Duration, error)

ParseHumanDuration parses a duration written the way people say it — "2 years", "6 months", "90 days", "3w" — as well as anything Go's time.ParseDuration accepts ("72h", "30m"). Calendar units use fixed approximations: a day is 24h, a week 7 days, a month 30 days, a year 365 days. The duration must be positive.

func RegisterFindingFilter added in v0.0.8

func RegisterFindingFilter(f FindingFilter)

RegisterFindingFilter appends f to the global finding-filter chain. Every finding produced by any detector is offered to each registered filter in registration order; the first filter to return false drops it. Register all filters before scanning begins.

func Scan added in v0.0.8

func Scan(root string, resolver *ConfigResolver, opts ScanRunOptions, emit func(findings []Finding) error) error

Scan is the full scanning pipeline over a file or directory tree: it walks root, resolves the effective config for each path (honoring repo-local configs via resolver), applies the allow/deny file policy and the scan mode, scans each eligible file, and calls emit with every file's findings in walk order (files without findings are not emitted). An error from emit aborts the walk and is returned.

func ScanGitHistory added in v0.0.8

func ScanGitHistory(root string, resolver *ConfigResolver, opts GitHistoryOptions, emit func(findings []Finding) error) error

ScanGitHistory is the Scan counterpart for git history: instead of walking the working tree it visits every unique blob reachable from any ref (including deleted files and unmerged branches) and scans each one exactly once, no matter how many commits contain it. root must be inside a git repository; the repository root is discovered by climbing from root, and the whole history is scanned regardless of which subdirectory root names.

File paths on findings are repo-relative (the path the blob was first seen at), and each finding carries GitProvenance naming the oldest commit containing the blob. The effective config is resolved once at root — the current repo-local config governs the entire history — and the allow/deny policy and scan mode gate blobs by their repo-relative path just as a tree scan gates disk paths. An error from emit aborts the scan and is returned.

func ShouldScan

func ShouldScan(path string, policy FilePolicy) bool

ShouldScan reports whether path passes the configured allow/deny policy.

func Walk

func Walk(root string, fn func(string) error) error

Walk invokes fn for root if it is a file, or for every file beneath it if it is a directory.

Types

type Config

type Config struct {
	Files     FilePolicy       `yaml:"files"`
	Rules     []RuleConfig     `yaml:"rules"`
	Detectors []DetectorConfig `yaml:"detectors"`
	// Filter is an optional list of filter rules. Each rule is an expr-lang
	// expression evaluated against every finding; what happens to a match depends
	// on the rule's action (drop it, or tag it). See FilterConfig and Filter for
	// the available variables. Empty means no filtering.
	Filter FilterConfigs `yaml:"filter"`
	// Correlations is an optional list of correlation rules. Each pairs a primary
	// finding with one or more partner findings seen just before it in the same
	// file; on a match the primary embeds the partners (which are dropped from the
	// top-level results) and all are tagged. See CorrelationConfig.
	Correlations []CorrelationConfig `yaml:"correlations"`
	// Info is an optional list of informational-finding rules. Each is an expr-lang
	// predicate over a value and its key name that, on a match, surfaces the value
	// as a levelInfo finding ("info:<id>") — a recognized non-credential identifier
	// such as a cloud account/tenant/subscription ID. They extend a built-in set
	// (see builtinInfoRules) and are evaluated before it. See InfoRuleConfig.
	Info []InfoRuleConfig `yaml:"info"`
	// Stopwords are extra non-secret words applied across all rules. As with the
	// built-in stopword set, a name-driven candidate
	// is dropped when its value contains (case-insensitively) any of these as a
	// substring. They extend, never replace, the always-on built-in set. Use this
	// to silence project-specific placeholders such as "redacted".
	Stopwords []string `yaml:"stopwords"`
	// JoinedEnvPrefixes are env-variable family prefixes joined to their kind
	// word with no separator — libpq's PGPASSWORD/PGHOST/PGUSER. Separator-
	// delimited families (PG_DB_*, MYSQL_*) group automatically; joined ones are
	// invisible to word splitting and must be named here for source-code
	// env-context enrichment to pair them. Matched case-insensitively.
	JoinedEnvPrefixes []string `yaml:"joined_env_prefixes"`
}

func LoadConfig

func LoadConfig(path string) (Config, error)

LoadConfig reads and parses the scanner configuration from path.

type ConfigResolver added in v0.0.3

type ConfigResolver struct {
	// contains filtered or unexported fields
}

ConfigResolver resolves the effective scanner config for a file path. When enabled, it honors repo-local config files (see RepoConfigNames) discovered at repository roots: a file is governed by the config of its nearest enclosing repository, falling back to the base config when that repo has none. A repository root acts as a boundary — a repo without its own config uses the base config rather than inheriting an outer repo's. Results are memoized per directory, so each repo root is stat'd and parsed at most once.

func NewConfigResolver added in v0.0.3

func NewConfigResolver(base Config, baseSet RuleSet, enabled bool, root string) *ConfigResolver

NewConfigResolver builds a resolver over base/baseSet. enabled toggles repo-local config discovery; root bounds the upward search so the resolver never climbs above the scanned tree.

func (*ConfigResolver) Resolve added in v0.0.3

func (r *ConfigResolver) Resolve(path string) (Config, RuleSet)

Resolve returns the config and compiled rule set that should govern scanning path.

func (*ConfigResolver) ResolveDir added in v0.0.8

func (r *ConfigResolver) ResolveDir(dir string) (Config, RuleSet)

ResolveDir returns the config and compiled rule set governing files directly in dir. It is Resolve for callers that have a directory rather than a file path, such as the git history scan resolving config once at the repo root.

type Correlation added in v0.0.5

type Correlation struct {
	ID  string
	Tag string
	// contains filtered or unexported fields
}

Correlation is a compiled CorrelationConfig. Exactly one form is active: structured (match + partners) or expression (program != nil).

func CompileCorrelations added in v0.0.5

func CompileCorrelations(cfgs []CorrelationConfig) ([]Correlation, error)

CompileCorrelations turns parsed correlation configs into ready-to-use rules, compiling their matchers or expression. A rule with a non-empty `when` is the expression form; otherwise it is structured and must list at least one partner. The applied tag defaults to the rule id. The built-in correlations are appended after the user-configured ones, so a user rule takes precedence when both could claim the same primary (first match per primary wins).

type CorrelationConfig added in v0.0.5

type CorrelationConfig struct {
	ID       string           `yaml:"id"`
	Match    FindingMatcher   `yaml:"match"`
	Partners []FindingMatcher `yaml:"partners"`
	When     string           `yaml:"when"`
	Tag      string           `yaml:"tag"`
}

CorrelationConfig defines a relationship between findings: a primary finding (matched by Match, or the When expression's `current`) is enriched when every partner is present among the recent prior findings of the same file. It accepts two forms — structured (Match + Partners) or an expr-lang expression (When, with `current` and the `prev` finding-list in scope). On a match the partners are embedded into the primary, dropped from the top-level results, and all are tagged with Tag (defaulting to ID).

type CustomDetector added in v0.0.3

type CustomDetector struct {
	Name           string
	Keywords       []string // lowercased
	Regexes        []NamedRegex
	Primary        *regexp.Regexp // one of Regexes' patterns; supplies the reported value
	ExcludeRegexes []*regexp.Regexp
	ExcludeWords   []string // lowercased
	MinEntropy     float64
	// SecretType is the validated Finding.Type for this detector's matches, or
	// "" to derive it from the detector and key names.
	SecretType string
}

CustomDetector is a compiled DetectorConfig, ready to match against values. Regexes is kept sorted by name so iteration and primary selection stay deterministic despite Go's randomized map ordering.

func CompileDetectors added in v0.0.3

func CompileDetectors(configs []DetectorConfig) ([]CustomDetector, error)

CompileDetectors turns parsed custom-detector configs into ready-to-use CustomDetectors, compiling their regular expressions, resolving the primary regex, and lowercasing keyword/exclude-word lists for case-insensitive matching. It mirrors CompileRules and fails on the first invalid pattern.

type Detector

type Detector interface {
	Detect(file string, data []byte, set RuleSet) []Finding
}

Detector parses one file format's bytes (already BOM-stripped) into findings. Each supported format provides a concrete implementation.

type DetectorConfig added in v0.0.3

type DetectorConfig struct {
	// Name labels the detector and appears in findings as "custom:<name>".
	Name string `yaml:"name"`
	// Keywords gate the detector: at least one must be present (case-insensitive)
	// in the value or its key name before the regexes are evaluated. An empty
	// list means the detector always runs its regexes.
	Keywords []string `yaml:"keywords"`
	// Regex maps a name to a pattern; every pattern must match the value for the
	// detector to fire. A pattern's first capture group, when present, is the
	// reported secret, otherwise the whole match is.
	Regex map[string]string `yaml:"regex"`
	// PrimaryRegexName selects which regex supplies the reported/entropy-checked
	// value; it defaults to the alphabetically first regex when omitted.
	PrimaryRegexName string `yaml:"primary_regex_name"`
	// ExcludeRegexesMatch drops a match whose primary value matches any of these.
	ExcludeRegexesMatch []string `yaml:"exclude_regexes_match"`
	// ExcludeWords drops a candidate when any of these appears (case-insensitive)
	// in the value or its key name.
	ExcludeWords []string `yaml:"exclude_words"`
	// Entropy, when > 0, drops matches whose primary value has Shannon entropy
	// (bits per symbol) below the threshold.
	Entropy float64 `yaml:"entropy"`
	// SecretType, when set, is the Finding.Type reported on this detector's
	// matches. It must be one of the secretType* vocabulary (e.g. "api-key",
	// "token"); compilation fails on an unknown value. When omitted, the type is
	// derived from the detector's name and the key name as usual.
	SecretType string `yaml:"secret_type"`
}

DetectorConfig is a trufflehog-style custom value-pattern detector as parsed from YAML, recognizing a secret by the shape of the value alone. The schema mirrors trufflehog's custom detectors (https://trufflesecurity.com/docs/custom-detectors): a detector fires when a keyword is present and every named regex matches. Live HTTP `verify` endpoints are intentionally unsupported — confessecrets is an offline scanner that redacts values rather than exfiltrating them — so that field is ignored.

type DotenvDetector

type DotenvDetector struct{}

DotenvDetector handles dotenv-style files (.env and its variants). It parses with joho/godotenv and falls back to line-based scanning for input that doesn't parse cleanly, mirroring the JSON/YAML detectors.

func (DotenvDetector) Detect

func (DotenvDetector) Detect(file string, data []byte, set RuleSet) []Finding

type ExaminationFocus added in v0.0.5

type ExaminationFocus struct {
	File         string
	Path         string
	Line         int
	Name         string
	Value        string
	PrevFindings []Finding
}

ExaminationFocus bundles the single item under inspection for value-pattern detection: where it lives (File/Path/Line), how it is labelled (Name), its scalar Value, and a snapshot of the most-recent prior findings for this file, kept for correlation context. Line is the 1-based source line, 0 when unknown.

type FilePolicy

type FilePolicy struct {
	Allow []string `yaml:"allow"`
	Deny  []string `yaml:"deny"`
}

type Filter added in v0.0.3

type Filter struct {
	ID     string
	Action string
	// contains filtered or unexported fields
}

Filter is a compiled filter rule: an expr-lang (https://expr-lang.org) predicate paired with the action to take on the findings it matches. A "filter"-action rule drops matches; a "tag"-action rule keeps them and adds its ID to their tags. It lets a config suppress (or label) whole classes of findings by their computed properties, e.g. `entropy <= 4 && name_value_similarity > 0.65` to drop low-entropy near-echoes.

The variables available to an expression are:

entropy               (number) Shannon entropy of the value, bits/symbol
name_value_similarity (number) name/value similarity, 0..1
value_length          (number) length of the raw value in bytes
name                  (string) the key name
value                 (string) the raw (unredacted) value
type                  (string) what kind of secret (e.g. "password", "api-key")
reason                (string) why the value was flagged
lang                  (string) source language / config format (e.g. "json")
level                 (string) severity: "high" (secret) or "info"
line                  (number) 1-based source line, or 0 if not line-oriented
file, path, name_path, value_path (string) location fields

expr-lang built-ins (matches, contains, startsWith, endsWith, lower, len, ...) are available, so richer rules like `value matches "(?i)example$"` work too.

func (*Filter) Matches added in v0.0.5

func (f *Filter) Matches(finding Finding) (bool, error)

Matches reports whether finding satisfies the filter's expression. A nil Filter matches nothing.

type FilterConfig added in v0.0.5

type FilterConfig struct {
	// ID labels the rule; it is the tag added to findings when Action is "tag".
	ID string `yaml:"id"`
	// Action is "filter" (drop matches, the default) or "tag" (keep and tag them).
	Action string `yaml:"action"`
	// Filter is the expr-lang expression evaluated against each finding.
	Filter string `yaml:"filter"`
}

FilterConfig is one entry of the top-level filter list. It accepts two YAML forms: a bare scalar (the expression, with the default "filter" action) or a mapping with id/action/filter keys.

func (*FilterConfig) UnmarshalYAML added in v0.0.5

func (f *FilterConfig) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML accepts either a scalar expression (action defaults to "filter") or an {id, action, filter} mapping.

type FilterConfigs added in v0.0.5

type FilterConfigs []FilterConfig

FilterConfigs is the top-level filter list. It accepts a YAML sequence of entries or, for convenience, a single bare entry (scalar or mapping) treated as a one-element list.

func (*FilterConfigs) UnmarshalYAML added in v0.0.5

func (fs *FilterConfigs) UnmarshalYAML(value *yaml.Node) error

type Finding

type Finding struct {
	// File is the path to the source file. Top-level findings always set it; it is
	// cleared (and so omitted) on findings embedded under Correlated, since a
	// correlated partner is always in the same file as its primary — only its
	// in-file location (Path) is meaningful there.
	File string `json:"file,omitempty"`
	// Path is the in-document location: a structured locator such as "$.db.password"
	// for parsed config, or an INI "[section] line:N". It is empty (and omitted)
	// when File and Line already carry the full location, as in the plain
	// line-oriented formats.
	Path string `json:"path,omitempty"`
	// Line is the 1-based source line the finding sits on. It is 0 (and omitted)
	// when the format supplies no position, e.g. a relaxed-JSON fallback parse or
	// a structured path with no line index.
	Line int `json:"line,omitempty"`
	// Lang is the source language or config format of the file the finding came
	// from (e.g. "python", "json"). Omitted for unsupported file types.
	Lang string `json:"lang,omitempty"`
	// Level is the severity of the finding: "high" for a detected secret (the
	// default) or "info" for an informational match such as a recognized service
	// URL that is worth surfacing but is not itself a credential.
	Level               string  `json:"level,omitempty"`
	NamePath            string  `json:"name_path"`
	ValuePath           string  `json:"value_path"`
	Name                string  `json:"name"`
	Value               string  `json:"value"`
	RawValue            string  `json:"raw_value"`
	ValueSHA256         string  `json:"value_sha256"`
	Entropy             float64 `json:"entropy"`
	NameValueSimilarity float64 `json:"name_value_similarity"`
	// Type names what kind of secret the finding is (e.g. "password", "api-key",
	// "jwt" — see the secretType* constants), where Reason records why it was
	// flagged. Omitted on informational (levelInfo) findings, which are not
	// credentials.
	Type   string `json:"type,omitempty"`
	Reason string `json:"reason"`
	// Tags are free-form labels attached to the finding: the ID of any "tag"-action
	// filter rule the finding matched, and any correlation tag. The source language
	// or config format is carried in the dedicated Lang field rather than a tag.
	// Omitted when empty.
	Tags []string `json:"tags,omitempty"`
	// Filtered is set when the finding matched the config filter but was retained
	// (via -show-filtered) instead of dropped; FilteredReason holds the expression
	// that excluded it. Both are omitted from normal, unfiltered findings.
	Filtered       bool   `json:"filtered,omitempty"`
	FilteredReason string `json:"filtered_reason,omitempty"`
	Meta           *Meta  `json:"meta,omitempty"`
	// Git carries commit provenance when the finding came from a git history
	// scan rather than the working tree; nil (and omitted) otherwise.
	Git *GitProvenance `json:"git,omitempty"`
	// Correlated holds secondary findings folded into this primary by a
	// correlation rule (e.g. the client_id paired with this client_secret).
	// Embedded secondaries are removed from the top-level results and surface only
	// nested here.
	Correlated []Finding `json:"correlated,omitempty"`
}

func ScanBytes added in v0.0.8

func ScanBytes(path string, data []byte, set RuleSet, opts ScanOptions) ([]Finding, error)

ScanBytes runs the full detection pipeline on in-memory content. path is used only to select the detector and language — the content need not exist on disk (e.g. a blob read from git history). Unsupported file types yield no findings and no error.

func ScanFile

func ScanFile(path string, set RuleSet, opts ScanOptions) ([]Finding, error)

ScanFile reads path, selects a Detector for its format, and returns any findings. Unsupported file types yield no findings and no error.

type FindingFilter added in v0.0.8

type FindingFilter func(Finding) bool

FindingFilter is a user-defined predicate over a freshly built finding. Returning false drops the finding before it enters any results; returning true keeps it. Filters see the finding as constructed (RawValue included), before correlation, Lang stamping, or the config filter rules run.

type FindingMatcher added in v0.0.5

type FindingMatcher struct {
	NameRegex   string `yaml:"name_regex"`
	ReasonRegex string `yaml:"reason_regex"`
	PathRegex   string `yaml:"path_regex"`
	Expr        string `yaml:"expr"`
}

FindingMatcher matches a single finding. It has two mutually exclusive forms: the regex form matches by one or more finding fields (all set fields must match, AND); the expression form (Expr) is an expr-lang predicate evaluated against the finding's fields (the same variables a filter expression sees, e.g. `name`, `reason`, `entropy`). An empty matcher matches nothing.

type GitHistoryOptions added in v0.0.8

type GitHistoryOptions struct {
	ScanRunOptions
	// MaxDepth, when positive, limits the scan to the N most recent commits
	// across all refs, ordered by committer time.
	MaxDepth int
	// MaxAge, when positive, skips commits whose committer time is more than
	// MaxAge before now (see ParseHumanDuration for accepting values like
	// "2 years"). Content introduced before the cutoff but still present in an
	// in-window commit is still scanned, attributed to the oldest in-window
	// commit containing it.
	MaxAge time.Duration
}

GitHistoryOptions tunes a git history scan. The embedded ScanRunOptions carry the same knobs a tree scan takes; the zero value scans all history.

type GitProvenance added in v0.0.8

type GitProvenance struct {
	Commit     string `json:"commit"`
	Blob       string `json:"blob"`
	Author     string `json:"author,omitempty"`
	AuthorDate string `json:"author_date,omitempty"`
}

GitProvenance records where in git history a finding's content entered the repository: the oldest commit (by committer time) whose tree contains the blob the finding was detected in.

type INIDetector

type INIDetector struct{}

INIDetector handles INI-style files (sections plus key=value pairs). It parses structurally with go-ini and falls back to line-based scanning for files that don't parse cleanly, mirroring the JSON/YAML detectors.

func (INIDetector) Detect

func (INIDetector) Detect(file string, data []byte, set RuleSet) []Finding

type InfoRule added in v0.0.5

type InfoRule struct {
	ID string
	// contains filtered or unexported fields
}

InfoRule is a compiled informational-finding rule: an expr-lang (https://expr-lang.org) predicate over a value and its key name that, when it matches, yields a levelInfo finding reasoned "info:<id>". Info rules recognize non-credential identifiers worth surfacing for inventory — cloud account, tenant, and subscription IDs — without raising them to the severity of a secret. They are data-driven so new identifiers can be added via config rather than code (see builtinInfoRules for the shipped set and Config.Info to extend).

The variables and helpers available to an expression are:

name   (string)          the key name (e.g. "subscription_id")
value  (string)          the raw scalar value
words  (func) -> []string the key split into lowercase identifier words, so
                         camelCase/snake_case/kebab-case all compare equally:
                         words("subscriptionId") == ["subscription", "id"].

expr-lang built-ins (matches, contains, in, lower, startsWith, ...) are available, so a rule reads e.g.

"subscription" in words(name) && value matches "(?i)^[0-9a-f-]{36}$"

type InfoRuleConfig added in v0.0.5

type InfoRuleConfig struct {
	// ID labels the rule and appears in findings as "info:<id>".
	ID string `yaml:"id"`
	// Match is the expr-lang predicate; a true result yields the info finding.
	Match string `yaml:"match"`
}

InfoRuleConfig is one informational-finding rule as parsed from YAML. A rule fires when its expr-lang Match predicate (evaluated over the value and its key name) is true, surfacing the value as a levelInfo finding reasoned "info:<id>". It accepts two YAML forms: a mapping with id/match keys, or a bare scalar treated as the match expression (the id then defaults to the expression itself). See InfoRule for the variables and helpers a Match can reference.

func (*InfoRuleConfig) UnmarshalYAML added in v0.0.5

func (c *InfoRuleConfig) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML accepts either a scalar match expression (id defaults to the expression) or an {id, match} mapping.

type JSONDetector

type JSONDetector struct{}

JSONDetector handles .json/.jsonc files. It parses structurally when possible and falls back to line-based scanning for non-standard JSON.

func (JSONDetector) Detect

func (JSONDetector) Detect(file string, data []byte, set RuleSet) []Finding

type JWT added in v0.0.5

type JWT struct {
	Issuer     string                 `json:"issuer,omitempty"`
	Iat        int64                  `json:"iat,omitempty"`
	Expiration int64                  `json:"expiration,omitempty"`
	IsExpired  bool                   `json:"is_expired,omitempty"`
	Header     map[string]interface{} `json:"header,omitempty"`
	Extra      map[string]interface{} `json:"extra,omitempty"`
}

JWT holds claims and the header decoded from a JSON Web Token value. The standard iss/iat/exp claims map to dedicated fields; the decoded header (alg/typ/kid, ...) lives in Header and every other claim in Extra. All fields are optional and omitted from output when absent.

type Meta

type Meta struct {
	JWT       *JWT                   `json:"jwt,omitempty"`
	Username  string                 `json:"username,omitempty"`
	AppId     string                 `json:"app_id,omitempty"`
	Host      string                 `json:"host,omitempty"`
	URL       string                 `json:"url,omitempty"`
	ClientID  string                 `json:"client_id,omitempty"`
	ClientKey string                 `json:"client_key,omitempty"`
	Extra     map[string]interface{} `json:"extra,omitempty"`
}

Meta holds optional, value-derived metadata attached to a finding. JWT is populated from JSON Web Token values; the identity fields (Username, Host, URL, ClientID, ClientKey) are derived from URL credentials, connection-string values, and correlated partner findings. All fields are optional and omitted from output when absent.

type NameRegexEntry added in v0.0.3

type NameRegexEntry struct {
	Name  string
	Regex string
}

NameRegexEntry is one entry of a rule's name_regexes list. It accepts two YAML forms: a bare scalar (the regex, unnamed) or a mapping with `name` and `regex` keys. The name is an optional human-readable label for the pattern.

func (*NameRegexEntry) UnmarshalYAML added in v0.0.3

func (e *NameRegexEntry) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML accepts either a scalar regex string or a {name, regex} mapping.

type NamedRegex added in v0.0.3

type NamedRegex struct {
	Name  string
	Regex *regexp.Regexp
}

NamedRegex is a compiled name pattern with its optional label.

type PropertiesDetector

type PropertiesDetector struct{}

PropertiesDetector handles Java-style .properties files. It parses with magiconair/properties and falls back to line-based scanning for input that doesn't parse cleanly, mirroring the JSON/YAML detectors.

func (PropertiesDetector) Detect

func (PropertiesDetector) Detect(file string, data []byte, set RuleSet) []Finding

type Rule

type Rule struct {
	NamePaths              []string
	ValuePaths             []string
	NameRegexes            []NamedRegex
	MinValueLen            int
	MinEntropy             float64
	HighEntropyThreshold   float64
	MaxNameValueSimilarity float64
	IgnoreNamePatterns     []*regexp.Regexp
	IgnoreValuePrefixes    []string
	IgnoreValuePatterns    []*regexp.Regexp
	// Stopwords are the effective extra non-secret words (lowercased) checked by
	// substring containment on top of the built-in stopword set. They come from
	// the global Config.Stopwords and are copied onto every rule at compile time
	// (see CompileConfig). Nil when none are configured.
	Stopwords []string
}

func CompileRules

func CompileRules(configs []RuleConfig) ([]Rule, error)

CompileRules turns parsed rule configs into ready-to-use Rules, compiling their regular expressions and applying defaults.

type RuleConfig

type RuleConfig struct {
	NamePaths  []string `yaml:"name_paths"`
	ValuePaths []string `yaml:"value_paths"`
	// NameRegexes is a list of name patterns. Each entry is either a bare regex
	// string or a {name, regex} mapping; a name matching any entry signals a
	// secret.
	NameRegexes []NameRegexEntry `yaml:"name_regexes"`
	MinValueLen int              `yaml:"min_value_len"`
	// MinEntropy, when > 0, gates name-driven findings: a value flagged because
	// its key name looks secret-y must have at least this Shannon entropy
	// (bits/symbol) or it is treated as a placeholder and dropped. Values that
	// carry a definite secret reason (JWT, private key, URL credentials) bypass
	// the gate.
	MinEntropy float64 `yaml:"min_entropy"`
	// HighEntropyThreshold, when > 0, enables a generic detector that flags any
	// value whose Shannon entropy meets the threshold, regardless of key name.
	HighEntropyThreshold float64 `yaml:"high_entropy_threshold"`
	// MaxNameValueSimilarity, when > 0, drops name-driven findings whose value is
	// at least this similar (0..1) to the key name — near-echoes such as
	// password="password1" or passwd="passw0rd" that the exact-echo check misses.
	// Similarity is the max of normalized Levenshtein and Jaro-Winkler.
	MaxNameValueSimilarity float64  `yaml:"max_name_value_similarity"`
	IgnoreNamePatterns     []string `yaml:"ignore_name_patterns"`
	IgnoreValuePrefixes    []string `yaml:"ignore_value_prefixes"`
	IgnoreValuePatterns    []string `yaml:"ignore_value_patterns"`
}

type RuleSet added in v0.0.3

type RuleSet struct {
	Rules        []Rule
	Detectors    []CustomDetector
	Filters      []*Filter
	Correlations []Correlation
	// InfoRules recognize non-credential identifiers (cloud account/tenant/
	// subscription IDs) and surface them as levelInfo findings. See InfoRule.
	InfoRules []InfoRule
	// JoinedEnvPrefixes are the normalized (lowercased) no-separator env family
	// prefixes from Config.JoinedEnvPrefixes, read by the source detector's
	// env-context enrichment.
	JoinedEnvPrefixes []string
}

RuleSet is the compiled detection configuration applied to a file: the name-driven Rules, the value-shape Detectors (custom, trufflehog-style), the post-detection Filters, and the cross-finding Correlations. They travel together so every file is scanned under one effective config, including repo-local overrides.

func CompileConfig added in v0.0.3

func CompileConfig(cfg Config) (RuleSet, error)

CompileConfig compiles a parsed Config into the RuleSet used to scan a file, pairing its name-driven rules with its value-shape custom detectors. It is the single entry point for turning loaded config (base or repo-local) into a ready-to-use RuleSet.

type ScanMode added in v0.0.8

type ScanMode string

ScanMode selects which file classes a scan visits: everything, only source code, or only structured config.

const (
	ScanAll    ScanMode = "all"
	ScanSource ScanMode = "source"
	ScanConfig ScanMode = "config"
)

func (ScanMode) Allows added in v0.0.8

func (m ScanMode) Allows(path string) bool

Allows reports whether path should be scanned under the mode: ScanSource restricts the scan to source-code files, ScanConfig excludes them, and ScanAll (or an empty mode) permits everything.

func (ScanMode) Valid added in v0.0.8

func (m ScanMode) Valid() bool

Valid reports whether m is one of the recognized scan modes.

type ScanOptions added in v0.0.3

type ScanOptions struct {
	// IncludeFiltered keeps findings excluded by the config filter in the results,
	// marking each Filtered with its FilteredReason, instead of dropping them.
	IncludeFiltered bool
}

ScanOptions tunes how ScanFile post-processes findings.

type ScanRunOptions added in v0.0.8

type ScanRunOptions struct {
	// Mode selects which file classes are visited; the zero value behaves as
	// ScanAll.
	Mode ScanMode
	// IncludeFiltered keeps findings excluded by the config filter in the
	// results, marking each Filtered with its FilteredReason, instead of
	// dropping them.
	IncludeFiltered bool
	// OnSkip, when non-nil, is called for each file that could not be scanned
	// (e.g. its bytes could not be read). Skipped files do not stop the walk.
	OnSkip func(path string, err error)
}

ScanRunOptions tunes a tree-level Scan.

type SlnDetector added in v0.0.8

type SlnDetector struct{}

SlnDetector handles Visual Studio solution (.sln) files. The format is line-oriented: top-level "key = value" headers (e.g. VisualStudioVersion), Project declarations, and GlobalSection/ProjectSection blocks of key/value pairs. There is no maintained Go parser for the format, so this walks it directly, mirroring the INI detector's line scan. The successor .slnx and .slnf formats are XML and JSON respectively and route to those detectors.

func (SlnDetector) Detect added in v0.0.8

func (SlnDetector) Detect(file string, data []byte, set RuleSet) []Finding

type SourceDetector added in v0.0.3

type SourceDetector struct{}

SourceDetector scans general-purpose source code (Python, JavaScript, TypeScript, Go, Java, C#, Ruby, PHP, Kotlin, Rust) for hardcoded secrets using tree-sitter. Unlike the structured-config detectors, it distinguishes a secret *literal* from a runtime lookup: it flags `password = "hunter2"` (the value node is a string literal) but not `password = os.environ.get("SECRET")` (the value node is a call), which regex-only scanners get wrong.

Parsing uses a pure-Go tree-sitter runtime with the grammars embedded in the binary (github.com/odvcencio/gotreesitter), so there is nothing to install, download, or compile at runtime, and the build stays CGO-free and cross-compilable.

func (SourceDetector) Detect added in v0.0.3

func (SourceDetector) Detect(file string, data []byte, set RuleSet) []Finding

type ValuePattern

type ValuePattern struct {
	ID    string
	Regex *regexp.Regexp
	// Allow are per-rule allowlist regexes (carried from the source ruleset): when the
	// Regex matches but any Allow regex also matches the value, it is suppressed
	// (e.g. AWS example keys ending in EXAMPLE). Nil for the hand-curated rules.
	Allow []*regexp.Regexp
}

ValuePattern is a value-shape rule that recognizes a secret by the shape of the value itself, independent of any surrounding key name.

type XMLDetector

type XMLDetector struct{}

XMLDetector handles .xml files via streaming token scanning.

func (XMLDetector) Detect

func (XMLDetector) Detect(file string, data []byte, set RuleSet) []Finding

type YAMLDetector

type YAMLDetector struct{}

YAMLDetector handles .yaml/.yml files, falling back to line-based scanning for templated YAML that doesn't parse cleanly.

func (YAMLDetector) Detect

func (YAMLDetector) Detect(file string, data []byte, set RuleSet) []Finding

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL