Documentation
¶
Index ¶
- Constants
- func All() []string
- func GateErrorSince(findings []Finding, isNew map[string]bool, label, failOn string) error
- func MatchGlob(pattern, path string) bool
- func NewLineScanner(r io.Reader) *bufio.Scanner
- func Register(name string, constructor func() Module)
- func ResolveCacheDir(rootDir string, configDir string) string
- type Baseline
- type Cache
- func (c *Cache) Clear() error
- func (c *Cache) Evict(maxAge string, maxSize string) EvictResult
- func (c *Cache) Get(key string, maxAge time.Duration) ([]Finding, bool)
- func (c *Cache) Key(content []byte, moduleName string, configJSON string) string
- func (c *Cache) Put(key string, findings []Finding) error
- type CacheTTLModule
- type Confidence
- type ConfigurableModule
- type Content
- type ContentKind
- type Delta
- type Engine
- type EvictResult
- type FileInfo
- type Finding
- type Module
- type ModuleStats
- type Mutability
- type NonTextEntry
- type Provenance
- type ProvenanceEntry
- type ProvenanceKind
- type Remediation
- type RemediationSummary
- type Severity
- type SnapshotAwareModule
- type Summary
- type ToolchainAwareModule
- type WholeRepoModule
Constants ¶
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 GateErrorSince ¶ added in v0.7.0
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 ¶
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
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 ¶
Register adds a module constructor to the global registry. Called from init() in each module file.
func ResolveCacheDir ¶
ResolveCacheDir determines the cache directory using the following precedence:
- STAGEFREIGHT_CACHE_DIR env var (used as-is, caller controls the path)
- configDir from .stagefreight.yml cache_dir (resolved relative to rootDir)
- /stagefreight/cache/lint/<project-hash> (persistent runtime root, if mounted)
- 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
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
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.
type Cache ¶
Cache provides content-addressed lint result caching. Dir is the resolved cache directory (call ResolveCacheDir to compute it).
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).
- Remove entries with mtime older than maxAge (dead entries)
- 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 ¶
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).
type CacheTTLModule ¶
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 ¶
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
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 ¶
Delta detects changed files relative to a baseline.
func (*Delta) ChangedFiles ¶
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 ¶
CollectFiles walks the root directory and returns FileInfo for all regular files.
func (*Engine) ModuleNames ¶
ModuleNames returns the names of all active modules in this engine.
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.
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
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
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.
type ModuleStats ¶
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
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 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
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
CriticalNote renders the critical count, annotating the low-confidence non-blocking remainder when present: "1 critical, 1 low-confidence non-blocking".
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.
Source Files
¶
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. |