lint

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: AGPL-3.0, AGPL-3.0-only Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const MaxLineBytes = 16 << 20

MaxLineBytes is the largest single line the line-oriented lint modules accept. bufio.Scanner's default token cap is 64 KiB, which aborts a scan with "bufio.Scanner: token too long" on files that put a very long line on disk — a minified JSON (e.g. a Grafana dashboard emitted on one line) routinely exceeds it. 16 MiB covers any realistic single-line file without a real memory cost (the buffer starts at 64 KiB and only grows toward the cap when a line actually needs it).

Variables

This section is empty.

Functions

func All

func All() []string

All returns sorted names of all registered modules.

func GateErrorSince added in v0.7.0

func GateErrorSince(findings []Finding, isNew map[string]bool, label, failOn string) error

GateErrorSince is the baseline-aware CI verdict: it fails only on NEWLY-introduced blocking findings — those whose fingerprint is in isNew. Pre-existing findings (already present at the baseline) are surfaced but do not block, so a known, tracked, can't-fix-now advisory stays loud without wedging the gate, while a genuinely new regression still fails. label names the baseline in the failure message.

func MatchGlob

func MatchGlob(pattern, path string) bool

MatchGlob matches a glob pattern supporting ** against a forward-slash path. Patterns and paths should use "/" separators. Exported wrapper so modules can reuse the engine's glob semantics.

func NewLineScanner added in v0.7.0

func NewLineScanner(r io.Reader) *bufio.Scanner

NewLineScanner returns a bufio.Scanner that tolerates very long lines, so one oversized line never aborts a line-oriented lint module mid-file. Use this instead of bufio.NewScanner anywhere a module scans arbitrary repository files.

func Register

func Register(name string, constructor func() Module)

Register adds a module constructor to the global registry. Called from init() in each module file.

func ResolveCacheDir

func ResolveCacheDir(rootDir string, configDir string) string

ResolveCacheDir determines the cache directory using the following precedence:

  1. STAGEFREIGHT_CACHE_DIR env var (used as-is, caller controls the path)
  2. configDir from .stagefreight.yml cache_dir (resolved relative to rootDir)
  3. /stagefreight/cache/lint/<project-hash> (persistent runtime root, if mounted)
  4. os.UserCacheDir()/stagefreight/<project-hash>/lint (XDG-aware default)

The project hash is a truncated SHA-256 of the absolute rootDir path, keeping per-project caches isolated without long nested directory names.

Types

type Baseline added in v0.7.0

type Baseline struct {
	Commit string // short-ish id of the resolved baseline commit, for display
	// contains filtered or unexported fields
}

Baseline is the prior state a run is diffed against. Comparison semantics (Paths/Content) are kept separate from the source on purpose: only a git merge-base resolver is wired today, but the type means "baseline" is not welded to git refs — a future resolver (a stored snapshot, a tarball, an attestation) could back the same two queries without changing any diff logic. One resolver now; clean seam for later.

func ResolveBaseline added in v0.7.0

func ResolveBaseline(rootDir, targetBranch string) (*Baseline, bool, error)

ResolveBaseline finds the merge-base of HEAD and the target branch and returns its tree (or HEAD's parent when HEAD == target, e.g. a push to main). It is intentionally forgiving: no git repo, no target branch, no common ancestor → (nil, false, nil), so a caller degrades to "no baseline" and never fails a lint run over git topology.

func (*Baseline) Content added in v0.7.0

func (b *Baseline) Content(path string) ([]byte, bool, error)

Content returns the baseline bytes of a path; ok=false if the path did not exist in the baseline. Used by the finding-level diff (Slice B) to obtain base findings.

func (*Baseline) NewFindings added in v0.7.0

func (b *Baseline) NewFindings(current []Finding, cfg config.LintConfig, rootDir string, cache *Cache) (map[string]bool, error)

NewFindings returns the set of fingerprints in `current` that are newly introduced relative to the baseline. For each changed file it lints the baseline version and diffs by fingerprint (line-independent identity), so a moved or reworded finding is NOT new. Unchanged files contribute nothing (same bytes → same findings → same fingerprints). A file absent at the baseline is new, so all its findings are new.

It degrades safely: any error returns what was computed so far with the error, and the caller treats a baseline-diff failure as "no diff", never as a failed lint.

func (*Baseline) Paths added in v0.7.0

func (b *Baseline) Paths() (map[string]bool, error)

Paths returns the set of file paths present in the baseline tree. Used by the new-artifact diff (Slice A): a path absent here but present now is newly introduced — a clean, content-free, unambiguous signal.

type Cache

type Cache struct {
	Dir     string
	Enabled bool
}

Cache provides content-addressed lint result caching. Dir is the resolved cache directory (call ResolveCacheDir to compute it).

func (*Cache) Clear

func (c *Cache) Clear() error

Clear removes the entire cache directory.

func (*Cache) Evict added in v0.5.0

func (c *Cache) Evict(maxAge string, maxSize string) EvictResult

Evict removes stale cache entries to bound unbounded growth. Content-addressed caches grow monotonically: every file edit creates a new entry, old entries for previous content are never read again.

Strategy: mtime-based (Get touches mtime on hit, so mtime = last access).

  1. Remove entries with mtime older than maxAge (dead entries)
  2. If still over maxSize, remove oldest entries until under limit

Both maxAge and maxSize use the same human format as retention config (e.g. "7d", "100MB"). Either can be empty to skip that phase.

func (*Cache) Get

func (c *Cache) Get(key string, maxAge time.Duration) ([]Finding, bool)

Get retrieves cached findings. Returns nil, false on cache miss. maxAge controls TTL: 0 means no expiry (content-only modules), >0 expires entries older than the duration (external-state modules).

On hit, updates the file's mtime so eviction can distinguish actively-used entries from dead ones (old file versions that are never read again).

func (*Cache) Key

func (c *Cache) Key(content []byte, moduleName string, configJSON string) string

Key computes a cache key from file content, module name, config, and the binary's build commit. Including the commit ensures that cache entries are automatically invalidated when the binary changes — no stale results from a prior build's module logic can survive a binary update.

func (*Cache) Put

func (c *Cache) Put(key string, findings []Finding) error

Put stores findings in the cache atomically.

type CacheTTLModule

type CacheTTLModule interface {
	CacheTTL() time.Duration
}

CacheTTLModule controls time-based cache expiry.

Modules that do not implement this interface are cached forever.

Semantics:

>0  → cache with expiry (e.g. 5*time.Minute)
 0  → cache forever (content-hash only)
<0  → never cache (always re-run)

type Confidence added in v0.7.0

type Confidence int

Confidence is how strongly the evidence supports a finding — ORTHOGONAL to Severity (which is impact-IF-true). A structurally-identified match is Confirmed; a strong but non-structural signal is Probable; a weak heuristic (e.g. an entropy guess) is Heuristic.

Confidence is DESCRIPTIVE — it does not silently relax enforcement. Secure-by-default, every critical blocks regardless of confidence: a Heuristic critical is "review-required" (it blocks until a human confirms or suppresses it), not an automatic pass. Confidence informs review priority and is the axis an operator may CHOOSE to relax through explicit configuration — the tool never relaxes on its own. Objectively-false findings (patched-in-lock deps, checksums, numeric/CPUID constants, emoji ZWJ) are removed by classification — they are never flagged — rather than flagged-and-then-down-gated, which would erode the meaning of the gate.

The zero value is Confirmed.

const (
	ConfidenceConfirmed Confidence = iota // structurally identified / authoritative
	ConfidenceProbable                    // strong evidence, not structural
	ConfidenceHeuristic                   // weak/ambiguous evidence — review-required, still blocks
)

func (Confidence) String added in v0.7.0

func (c Confidence) String() string

type ConfigurableModule

type ConfigurableModule interface {
	Module
	Configure(opts map[string]any) error
}

ConfigurableModule is implemented by modules that accept YAML options. The engine calls Configure after construction if the module's config section contains an options map.

type Content added in v0.7.0

type Content struct {
	Kind  ContentKind
	Magic string
}

Content is the classification result — the shared primitive other modules route on. Magic is a sniffed type label ("png", "gzip", …) or "" when unknown; it drives extension/content mismatch detection.

func (Content) IsText added in v0.7.0

func (c Content) IsText() bool

IsText reports whether the text-oriented modules should run. True ONLY for genuine text: Ambiguous is deliberately not treated as text, because parser-differential attacks live in exactly that boundary. The zero value is ContentText, so a file whose content was never classified defaults to text (the pre-classification behavior) — absence of classification never silently suppresses a check.

type ContentKind added in v0.7.0

type ContentKind int

ContentKind classifies a file's bytes so lint modules route correctly. Only Text gets the text-oriented checks (unicode, lineendings) — an "invalid UTF-8" finding on a real image is noise, not signal. Binary and Ambiguous skip those checks but are STILL scanned by byte-oriented modules (secrets, entropy), so classification can never become an evasion path: a payload doesn't escape inspection by being (or pretending to be) binary.

const (
	ContentText      ContentKind = iota // genuine text → run text modules
	ContentBinary                       // binary blob → skip text modules, scan bytes
	ContentAmbiguous                    // UTF-16 / mixed / uncertain → skip text modules
)

type Delta

type Delta struct {
	RootDir      string
	TargetBranch string
	Verbose      bool
}

Delta detects changed files relative to a baseline.

func (*Delta) ChangedFiles

func (d *Delta) ChangedFiles(ctx context.Context) (map[string]bool, error)

ChangedFiles returns the list of files changed relative to the baseline. In CI mode, it diffs against STAGEFREIGHT_TARGET_BRANCH (or auto-detects). Locally, it diffs against HEAD (uncommitted + staged changes) plus committed changes not in the default branch. Returns nil (scan everything) if git is unavailable or no baseline exists.

type Engine

type Engine struct {
	Config           config.LintConfig
	RootDir          string
	Modules          []Module
	Cache            *Cache
	Verbose          bool
	ToolchainDesired map[string]config.ToolConstraint

	// Snapshot is an optional, pre-resolved supply-chain Snapshot produced
	// once (via discovery.Discover) and shared by the caller across
	// consumers — e.g. the audition pipeline threads the same Snapshot into
	// both the lint pass and the dependency-update step so resolution runs
	// once instead of per-consumer. Set by the caller before RunWithStats,
	// mirroring ToolchainDesired. nil means "no shared Snapshot" — modules
	// implementing SnapshotAwareModule must fall back to on-demand
	// resolution (this is what keeps standalone `stagefreight lint` working).
	Snapshot *supplychain.Snapshot

	CacheHits   atomic.Int64
	CacheMisses atomic.Int64

	// BinariesScanned counts files classified non-text this run — the coverage
	// roll-up ("N binaries scanned"). Inspected ≠ emitted: a clean binary increments
	// this but produces no finding.
	BinariesScanned atomic.Int64

	// ClassifyUnreadable counts files whose bytes couldn't be read for classification
	// (permissions, IO, races). They fail OPEN — treated as text so checks still run —
	// but the count is surfaced so environmental breakage isn't silently swallowed.
	ClassifyUnreadable atomic.Int64

	// NonText is the disclosure inventory: every non-text artifact this run, for the
	// ungraded "validate these are deliberate" review surface (NOT findings). Populated
	// in the sequential classification pass, so a plain slice is safe.
	NonText []NonTextEntry

	// NonAuthored is the provenance disclosure: generated/vendored/lockfile files this
	// run. Authored-code hygiene was relaxed for them, but they stay visible — and every
	// security/supply-chain module still ran. Populated in the sequential pass.
	NonAuthored []ProvenanceEntry
	// contains filtered or unexported fields
}

Engine orchestrates lint modules across files.

func NewEngine

func NewEngine(cfg config.LintConfig, rootDir string, moduleNames []string, skipNames []string, verbose bool, cache *Cache) (*Engine, error)

NewEngine creates a lint engine with the selected modules.

func (*Engine) CollectFiles

func (e *Engine) CollectFiles() ([]FileInfo, error)

CollectFiles walks the root directory and returns FileInfo for all regular files.

func (*Engine) ModuleNames

func (e *Engine) ModuleNames() []string

ModuleNames returns the names of all active modules in this engine.

func (*Engine) Run

func (e *Engine) Run(ctx context.Context, files []FileInfo) ([]Finding, error)

Run executes all modules against the given files and returns findings.

func (*Engine) RunWithStats

func (e *Engine) RunWithStats(ctx context.Context, files []FileInfo) ([]Finding, []ModuleStats, error)

RunWithStats executes all modules and returns findings plus per-module statistics.

type EvictResult added in v0.5.0

type EvictResult struct {
	EntriesBefore int
	Evicted       int
	EvictedBytes  int64
	Reason        string // non-empty if skipped/errored
}

EvictResult records what cache eviction did.

type FileInfo

type FileInfo struct {
	Path    string // relative path from repo root
	AbsPath string // absolute path on disk
	Size    int64
	Content Content
	// Provenance is the centrally-computed origin label (authored/generated/vendored/
	// lockfile). Authored-hygiene modules relax on non-authored; security and
	// supply-chain modules ignore it. Zero value is authored (full scrutiny).
	Provenance Provenance
}

FileInfo is passed to each module for inspection. Content is the centrally-computed classification (text/binary/ambiguous): text modules route on it, byte modules ignore it. Its zero value is ContentText, so an unclassified file behaves as text.

func FilterByDelta

func FilterByDelta(files []FileInfo, changedSet map[string]bool) []FileInfo

FilterByDelta filters a file list to only include changed files. If changedSet is nil, returns all files (full scan).

type Finding

type Finding struct {
	File       string
	Line       int
	Column     int
	Module     string
	Severity   Severity
	Confidence Confidence
	Message    string
	// RuleID is a STABLE internal identifier for the finding kind (e.g.
	// "trailing-whitespace"). It is the identity surface for baseline diffing and must
	// not change for cosmetic reasons — unlike Message, which is presentation and may be
	// reworded freely. Empty is allowed; identity then falls back to Module.
	RuleID string
	// Anchor is a normalized SEMANTIC anchor (e.g. the trimmed line content) that ties a
	// finding's identity to what it is about rather than where it sits. It lets a finding
	// survive line-number shifts so a moved issue isn't mistaken for a new one. Empty is
	// allowed (coarser identity).
	Anchor string
	// Fix, when non-nil, is a proven-safe, byte-exact, reversible remediation the
	// detector emits alongside the finding. Because the edit is carried by the finding
	// itself — not re-derived by a separate fixer — "what gets fixed" equals "what was
	// reported," by construction. A nil Fix means the finding is NOT auto-fixable, so no
	// flag can ever mutate it. Disclosures are not Findings and so structurally cannot
	// carry a Fix.
	Fix *Remediation
}

Finding represents a single lint result.

func (Finding) Blocks added in v0.7.0

func (f Finding) Blocks() bool

Blocks reports whether a finding fails CI. Secure-by-default: every critical impact blocks, regardless of confidence. A heuristic critical is review-required — it blocks until a human confirms or suppresses it — never a silent pass. (Confidence is descriptive; an operator may relax a tier via explicit config, but the gate never relaxes on its own, and objectively-false findings are dropped by classification, not down-gated here.)

func (Finding) Fingerprint added in v0.7.0

func (f Finding) Fingerprint() string

Fingerprint is the line-INDEPENDENT identity of a finding, for baseline diffing: hash(File + Module + RuleID + Anchor). Deliberately excludes Line/Column (position is not identity) and Message (presentation is not identity), so a finding that merely moved or was reworded keeps the same fingerprint and is not mistaken for new. Identical anchors collide — which biases toward UNDERcounting "new" (safe silence over false accusation), the correct bias for a trust-first tool.

type Module

type Module interface {
	Name() string
	Check(ctx context.Context, file FileInfo) ([]Finding, error)
	DefaultEnabled() bool
	AutoDetect() []string // glob patterns that trigger auto-enable
}

Module is the interface every lint check implements.

func Get

func Get(name string) (Module, error)

Get returns a new instance of the named module.

type ModuleStats

type ModuleStats struct {
	Name     string
	Files    int
	Cached   int
	Findings int
	Critical int
	Warnings int
}

ModuleStats holds per-module scan statistics.

type Mutability added in v0.7.0

type Mutability struct {
	Fatal      []Finding // blocking, no mutator can clear — abort before mutating
	Remediable []Finding // blocking, a mutator is expected to clear (freshness/osv → deps)
}

Mutability is the mutation-safety classification of a set of lint findings. It answers one question — "is it safe for StageFreight to mutate this repository to clear these findings?" — a mutation-safety judgment, deliberately independent of any CI/pipeline concept (nothing here names a phase, a runner, or a gate). That independence is the tell that it belongs at the lint layer and is reusable anywhere a caller must decide whether to touch a tree.

Only BLOCKING findings (Finding.Blocks — the same predicate the CI gate uses) are classified; a non-blocking finding neither voids the source nor demands a mutator. A blocking finding from a "world" module (freshness/osv — external state a mutator repairs; see worldModules) is Remediable: a mutator is expected to clear it. Every other blocking finding is Fatal — it voids the source, and StageFreight must not mutate a void tree.

func Classify added in v0.7.0

func Classify(findings []Finding) Mutability

Classify partitions the blocking findings into Fatal vs Remediable. Non-blocking findings are ignored — they never gate and never affect mutation safety. The blocking test is Finding.Blocks, identical to the CI gate, so classification and gating can never disagree about what "blocking" means.

func (Mutability) HasFatal added in v0.7.0

func (m Mutability) HasFatal() bool

HasFatal reports whether any blocking finding voids the source. When true the caller must abort before any mutation.

func (Mutability) HasRemediable added in v0.7.0

func (m Mutability) HasRemediable() bool

HasRemediable reports whether there are blocking findings a mutator is expected to clear.

type NonTextEntry added in v0.7.0

type NonTextEntry struct {
	Path string
	Type string // magic label, or "ambiguous" / "binary"
}

NonTextEntry is one non-text artifact for the disclosure inventory — a review surface, never a graded finding.

type Provenance added in v0.7.0

type Provenance struct {
	Kind   ProvenanceKind
	Source string
}

Provenance is the classification result. Source records the evidence ("config", "marker", "lockfile:Cargo.lock", "vendor-marker:crates/x") for disclosure.

func (Provenance) IsAuthored added in v0.7.0

func (p Provenance) IsAuthored() bool

IsAuthored reports whether the file is hand-maintained. The zero value is Authored, so an unclassified file is always treated as authored — absence of provenance evidence never suppresses a check.

func (Provenance) RelaxHygiene added in v0.7.0

func (p Provenance) RelaxHygiene() bool

RelaxHygiene reports whether authored-code hygiene modules (whitespace, line endings, line length) should stand down for this file. True for generated/vendored/lockfile. Security (secrets, unicode/Trojan-source), supply-chain (freshness/osv), and concealment modules must NOT consult this — provenance is never an evasion path.

type ProvenanceEntry added in v0.7.0

type ProvenanceEntry struct {
	Path   string
	Kind   string // "generated" | "vendored" | "lockfile"
	Source string // evidence: "config", "marker", "lockfile:Cargo.lock", …
}

ProvenanceEntry is one non-authored file for the provenance disclosure roll-up.

type ProvenanceKind added in v0.7.0

type ProvenanceKind int

ProvenanceKind labels where a file CAME FROM, so modules can route on origin the way they route on content. It exists to relax *authored-code hygiene* (trailing whitespace, line endings, file length) on files no human hand-maintains — without ever relaxing security or supply-chain inspection.

Fail direction is the OPPOSITE of content classification, on purpose. Content fails OPEN to text (a missed signal means extra checks — just noise). Provenance fails CLOSED to Authored (a missed signal means full scrutiny). The dangerous mistake here is calling a hand-written file "generated" and then SKIPPING its checks — an evasion hole. So Authored is the zero value, and a file only leaves it on POSITIVE evidence.

const (
	ProvenanceAuthored  ProvenanceKind = iota // hand-written (default): every check runs
	ProvenanceGenerated                       // machine-emitted: relax authored hygiene
	ProvenanceVendored                        // third-party copy: relax authored hygiene
	ProvenanceLockfile                        // dependency lock: relax hygiene, KEEP CVE scan
)

func (ProvenanceKind) String added in v0.7.0

func (k ProvenanceKind) String() string

type Remediation added in v0.7.0

type Remediation struct {
	Kind        string // "trailing-whitespace" | "final-newline"
	Start, End  int
	Expected    string // bytes that must currently occupy [Start:End], or the CAS skips
	Replacement string
}

Remediation is a single byte-exact edit: replace File[Start:End] with Replacement ("" = deletion). Kind names the safe-edit category for granular opt-in and reporting. The applier is dumb — it performs exactly this span replacement and re-derives nothing — which is what keeps remediation tied to the reported finding.

Expected is the precondition: the exact bytes the detector saw at [Start:End]. The applier performs a compare-and-swap — it mutates ONLY if the file still holds those bytes — so a stale finding against a since-changed file (a race, a replay, an edit between detect and fix) is skipped, never misapplied. Mutates only with proof, the mirror of "classification only relaxes with proof."

type RemediationSummary added in v0.7.0

type RemediationSummary struct {
	FilesChanged int
	EditsApplied int
	Skipped      int            // edits skipped as overlapping within an otherwise-applied file
	Drifted      int            // files skipped ENTIRELY because content changed since the scan
	ByKind       map[string]int // applied edits per Kind
}

RemediationSummary reports the outcome of an ApplyRemediations pass.

func ApplyRemediations added in v0.7.0

func ApplyRemediations(findings []Finding, rootDir string, enabled map[string]bool, dryRun bool) (RemediationSummary, error)

ApplyRemediations writes every finding's Fix whose Kind is enabled back to disk. It is deliberately dumb: it performs exactly the byte spans the detectors emitted and re-derives nothing, so "what is fixed" equals "what was reported." Findings without a Fix, and Fixes whose Kind is not enabled, are left untouched — and because authored- hygiene modules never run on generated/vendored/lockfile content, no Fix exists for those files, so remediation is provenance-gated by construction.

Three safety properties beyond the span itself:

  • Compare-and-swap, transactional per file: every edit's Expected is verified against the on-disk bytes BEFORE anything is written. If ANY span has drifted (content changed, file shrank), the whole file is left untouched — a drifted file is never partially remediated into a confusing mixed state. This makes the file-level drift guard fall out of the per-span CAS: any change since the scan trips it.
  • Atomic write: each file is replaced via a temp file + fsync + rename, so a crash or full disk mid-write can never leave a half-written (corrupted) source file.
  • dryRun: validate and count exactly what WOULD change, writing nothing.

Within a non-drifted file, edits apply high offset → low so earlier edits never shift later ones; overlapping spans are skipped individually. Provide rootDir so relative finding paths resolve; file permissions are preserved.

type Severity

type Severity int

Severity indicates how serious a finding is.

const (
	SeverityInfo Severity = iota
	SeverityWarning
	SeverityCritical
)

func (Severity) String

func (s Severity) String() string

type SnapshotAwareModule added in v0.7.0

type SnapshotAwareModule interface {
	Module
	SetSnapshot(snapshot *supplychain.Snapshot)
}

SnapshotAwareModule is implemented by modules that can consume a pre-resolved supplychain.Snapshot instead of resolving dependencies themselves. The engine calls SetSnapshot after construction, before Run(), if the module implements this interface AND a Snapshot was provided by the caller (e.g. the audition pipeline, which resolves once via discovery.Discover and shares the result across lint and dependency-update rather than resolving per-consumer). Mirrors ToolchainAwareModule.

When no Snapshot is provided (Engine.Snapshot is nil), modules implementing this interface must fall back to on-demand resolution — this keeps standalone `stagefreight lint` working.

type Summary added in v0.7.0

type Summary struct {
	Total    int
	Critical int // by severity
	Warning  int
	Info     int
	Blocking int // findings that fail CI: Severity==Critical && Confidence != Heuristic (Finding.Blocks)
}

Summary is the single source of truth for rolling a set of findings up into the counts that drive BOTH presentation and the CI gate. The `stagefreight lint` CLI and the CI audition lint phase each summarize through this, so the gate policy — critical impact AND at least probable confidence — can never again diverge between the two paths (it did once: a heuristic-critical was non-blocking in the CLI but still failed the audition).

func Summarize added in v0.7.0

func Summarize(findings []Finding, failOn string) Summary

Summarize tallies findings by severity and counts the blocking subset at the given fail_on threshold (lint's importance vocabulary; "" → "critical", the historical default of blocking only critical findings).

func (Summary) CriticalNote added in v0.7.0

func (s Summary) CriticalNote() string

CriticalNote renders the critical count, annotating the low-confidence non-blocking remainder when present: "1 critical, 1 low-confidence non-blocking".

func (Summary) GateError added in v0.7.0

func (s Summary) GateError() error

GateError is the one CI-gate verdict: non-nil iff the run should fail. Both lint paths return this, so the threshold and wording stay identical.

type ToolchainAwareModule added in v0.6.0

type ToolchainAwareModule interface {
	Module
	SetToolchainDesired(desired map[string]config.ToolConstraint)
}

ToolchainAwareModule is implemented by modules that need toolchain config for version pinning. The engine calls SetToolchainDesired after construction if the module implements this interface.

type WholeRepoModule added in v0.7.0

type WholeRepoModule interface {
	Module
	CheckAll(ctx context.Context, files []FileInfo) ([]Finding, error)
}

WholeRepoModule is implemented by modules that analyze the ENTIRE file set in one pass instead of once per file. When a module implements this interface the engine invokes CheckAll exactly once — with every eligible file (engine-wide and this module's own excludes already applied) — and NEVER calls Check on it.

This is the seam for cross-file analyses that a per-file Check cannot express: canonical-vulnerability dedup across a manifest and its lockfile (which are DIFFERENT files, so a per-file reduce sees only one leg of each advisory), or an external whole-repo linter (e.g. golangci-lint) that owns its own file walking and reports over a whole module at once.

A whole-repo module still satisfies Module so it registers, configures, and auto-detects identically. Its Check is a mis-dispatch guard: because the engine routes whole-repo modules to CheckAll, a call to Check means something bypassed that dispatch, and the module should fail loud rather than silently emit nothing.

Directories

Path Synopsis
Package modules contains all built-in lint modules.
Package modules contains all built-in lint modules.
freshness
Package freshness checks for outdated dependencies across ecosystems: Dockerfile base images, pinned tool versions, Go modules, Rust crates, npm packages, Alpine APK, Debian/Ubuntu APT, and pip packages.
Package freshness checks for outdated dependencies across ecosystems: Dockerfile base images, pinned tool versions, Go modules, Rust crates, npm packages, Alpine APK, Debian/Ubuntu APT, and pip packages.
vulnerabilities
Package vulnerabilities is the single lint module that reports known vulnerabilities affecting a project's dependencies.
Package vulnerabilities is the single lint module that reports known vulnerabilities affecting a project's dependencies.

Jump to

Keyboard shortcuts

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