Documentation
¶
Overview ¶
Package pipeline is the owner of "run an analysis".
Both the CLI's scan family and the language server reach analysis through this package, which is what AGENTS.md means by "every capability has exactly one owner, reached through one options-struct entry function". Before this package existed, the analysis lived in cmd/runLocalScan: 1,600 lines, 40 positional parameters, and hard-wired to a terminal.
Reporter is the seam that made the extraction possible. The analysis emits progress and diagnostics through this interface instead of writing to a terminal, so the same code path can drive a spinner on stderr, an LSP $/progress token, or nothing at all in a test.
Index ¶
- type Level
- type LicensePolicy
- type Options
- type Reporter
- type TerminalReporter
- func (r *TerminalReporter) Complete(stage string)
- func (r *TerminalReporter) Degradations() []string
- func (r *TerminalReporter) Degraded(note string)
- func (r *TerminalReporter) Fail(stage string)
- func (r *TerminalReporter) Logf(level Level, format string, args ...any)
- func (r *TerminalReporter) Progress() *display.Progress
- func (r *TerminalReporter) Stage(stage string)
- func (r *TerminalReporter) Update(done int, stage string)
- func (r *TerminalReporter) Writer() io.Writer
- type WriterReporter
- func (r *WriterReporter) Complete(stage string)
- func (r *WriterReporter) Degradations() []string
- func (r *WriterReporter) Degraded(note string)
- func (r *WriterReporter) Fail(stage string)
- func (r *WriterReporter) Logf(level Level, format string, args ...any)
- func (r *WriterReporter) Stage(stage string)
- func (r *WriterReporter) Update(_ int, stage string)
- func (r *WriterReporter) Writer() io.Writer
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Level ¶
type Level int
Level classifies a Reporter log line. It maps onto LSP MessageType (1=Error, 2=Warning, 3=Info) without translation.
type LicensePolicy ¶
type LicensePolicy struct {
// Mode is "inclusive" or "individual".
Mode string
// AllowCSV is a comma-separated SPDX allowlist.
AllowCSV string
// AllowFile is a path to a newline-separated SPDX allowlist.
AllowFile string
// PolicyFile and ExceptionsFile are the declarative forms of the same
// decision: a category-based policy and its approved exceptions. Empty
// means "discover the default path under the scanned root, and fall back to
// the built-in policy" — so a project that has neither behaves exactly as
// it did before they existed.
PolicyFile string
ExceptionsFile string
}
LicensePolicy is the licence policy as the scan-family flags express it, handed to the licence owner (runLicensePipeline) when that stage runs.
type Options ¶
type Options struct {
// Files are the manifests and SBOMs already detected under RootPath. The
// caller runs detection so it can report and filter before committing to a
// scan.
Files []scan.DetectedFile
// RootPath is the absolute directory being analysed.
RootPath string
// Depth bounds directory recursion during manifest detection (--depth).
Depth int
// Excludes are glob patterns removed from the walk (--exclude).
Excludes []string
// Deployment labels where these results are deployed and who owns them
// (--project/--cluster/--namespace/--environment/--tag). It is inert for
// the analysis itself — nothing branches on it — and exists so the labels
// reach the CycloneDX metadata and the upload envelope, where the server
// can join results across repositories. See internal/cdx/deployment.go.
Deployment cdx.DeploymentContext
// VEXFiles are third-party VEX documents to apply to the findings before
// the quality gates are evaluated (--vex-file). Empty when --no-vex was
// passed, so the engine has one thing to check rather than two.
VEXFiles []string
NoSCA bool
NoSASTRules bool
NoSecrets bool
NoContainers bool
NoIAC bool
NoLicenses bool
// NoExploits and NoRemediation drop the corresponding VDB enrichment
// passes; the SCA lookup itself still runs.
NoExploits bool
NoRemediation bool
// SeverityThreshold is validated against scan.ValidSeverityThresholds.
SeverityThreshold string
// ExploitThreshold is validated against scan.ValidExploitThresholds.
ExploitThreshold string
BlockMalware bool
BlockEOL bool
BlockUnpinned bool
VersionLag int
CooldownDays int
// Jail runs the organisation's jail policy against this repository after the
// scan has uploaded, and gates on the verdict.
//
// Unlike the thresholds above, which grade THIS run's findings, the jail
// policy grades the repository's accumulated state across every tool and
// category that has reported for it. A breach exits 1 alongside the other
// gates; a rule that cannot be evaluated against current scan coverage exits
// 3, which is a different problem with a different owner.
Jail bool
// DisableDefaultRules drops the embedded corpus, leaving only RuleRefs.
DisableDefaultRules bool
// RuleRefs are external rule packs, cloned from RuleRegistry.
RuleRefs []sast.RuleRef
// RuleRegistry defaults to sast.DefaultRegistry when empty.
RuleRegistry string
// RuleID restricts the run to a single VNX-NNNN rule.
RuleID string
// LockedKinds restricts the run to these rule kinds, embedded and external
// alike. Nil means "no lock", which is the generic scan command's
// behaviour. The language server sets this per trigger: the keystroke path
// runs sast/iac/oci only, because the 1,092 secrets rules cost roughly 20x
// what every other kind costs combined.
LockedKinds []string
// SnippetContext shapes SARIF snippets: -1 picks a width from the finding
// span, 0 disables snippets.
SnippetContext int
// Reachability selects the tree-sitter reachability mode applied to the
// CVEs the SCA pass produces: "direct" scans only each vulnerable
// package's installed directory, "transitive" sweeps the rest of the
// project for callers, "both" does both, and "off" skips the analysis (and
// the server-side query fetch) entirely. Empty means "both".
//
// It is a string rather than a reachability.Mode so internal/pipeline stays
// free of the CGo tree-sitter dependency; the value is validated by
// internal/scanopts before it gets here.
Reachability string
IgnoreGlobs []string
IgnoreGit bool
IgnoreBinaries bool
GitHistory bool
GitHistoryMaxCommits int
GitHistoryMaxFiles int
// RespectGitignore prunes files matched by .gitignore.
RespectGitignore bool
SCAAutofix bool
SCAAutofixOpts fix.Options
// AutofixResolved carries findings a previous autofix pass fixed, so the
// confirmation re-scan can mark them resolved rather than re-reporting.
AutofixResolved []*triage.TriageFinding
License LicensePolicy
GitCtx *gitctx.GitContext
SysInfo *gitctx.SystemInfo
// SeedBOM and VulnetixSeedBOM carry components from an existing SBOM into
// the produced document, so a scan never loses what a previous one found.
SeedBOM *cdx.BOM
VulnetixSeedBOM *cdx.BOM
// ShowPaths includes the dependency chain that introduced each package.
ShowPaths bool
// ResultsOnly suppresses all output when the run is clean.
ResultsOnly bool
// DryRun performs detection, parsing and memory reads with zero API calls.
DryRun bool
// NoProgress suppresses the live progress row. Kept here because it also
// reaches callees that build their own display contexts.
NoProgress bool
}
Options is everything an analysis run needs, as named fields.
This replaces the 40 positional parameters cmd/runLocalScan used to take, a signature where `true, noSCA, true, true, true` was a real call site and adding a parameter meant touching every caller. More importantly it is the entry point AGENTS.md asks for: one options struct that both the scan subcommands and the language server fill in, so neither owns a private version of "what a scan is".
Deliberately absent: anything about presentation. Where results are written, how they are rendered, and whether there is a terminal at all are the caller's business. Progress and log output go through Reporter.
type Reporter ¶
type Reporter interface {
// Stage changes the current stage label without changing numeric progress.
Stage(stage string)
// Update sets numeric progress out of the total declared at construction,
// and, when stage is non-empty, the stage label with it.
Update(done int, stage string)
// Logf writes one diagnostic line. Callers must not append a newline.
Logf(level Level, format string, args ...any)
// Degraded records that part of the analysis did not run to completion:
// files skipped over the size cap, git history unavailable, a stage that
// failed without failing the run. These are collected and surfaced, because
// "no findings" and "did not look" must not be indistinguishable.
Degraded(note string)
// Degradations returns everything passed to Degraded, in order.
Degradations() []string
// Complete and Fail finalise the run exactly once. Subsequent calls are
// ignored, so a deferred Complete after an early Fail is harmless.
Complete(stage string)
Fail(stage string)
// Writer is the transitional escape hatch for callees that still take an
// io.Writer directly (sast.LoadAllModules, autofix.RunInstall,
// postScanSARIF). Everything written to it is ordinary log output. New code
// should use Logf; this exists so the extraction could land without also
// rewriting every callee's signature in the same change.
Writer() io.Writer
}
Reporter receives progress, log output and degradation notices from an analysis run.
Implementations must be safe for concurrent use: the scan stages fan out across goroutines, and the language server evaluates shards in parallel.
A nil Reporter is not valid; use Discard() when output is not wanted.
type TerminalReporter ¶
type TerminalReporter struct {
// contains filtered or unexported fields
}
TerminalReporter drives the existing display.Progress activity: one live line on stderr, with log output routed through Progress.Writer so a log line clears and redraws the progress row instead of leaving duplicates behind.
This is the reporter the CLI uses, and it reproduces exactly what cmd/runLocalScan did inline before the extraction. The language server installs a different implementation that turns the same calls into $/progress notifications.
func NewTerminalReporter ¶
func NewTerminalReporter(title string, total int, silent, noProgress bool) *TerminalReporter
NewTerminalReporter starts a progress activity titled `title` with `total` numbered steps, rendering to stderr.
The arguments mirror the CLI's own flags: silent suppresses everything, noProgress keeps log output but drops the live line. Both are honoured by display.Progress itself, so a disabled progress activity is a safe no-op rather than a nil that every call site has to check.
func NewTerminalReporterFrom ¶
func NewTerminalReporterFrom(progress *display.Progress) *TerminalReporter
NewTerminalReporterFrom wraps an already-started progress activity. Use it when the caller owns the display context, for instance because it renders results through the same context after the analysis finishes.
func (*TerminalReporter) Complete ¶
func (r *TerminalReporter) Complete(stage string)
func (*TerminalReporter) Degradations ¶
func (r *TerminalReporter) Degradations() []string
func (*TerminalReporter) Degraded ¶
func (r *TerminalReporter) Degraded(note string)
func (*TerminalReporter) Fail ¶
func (r *TerminalReporter) Fail(stage string)
func (*TerminalReporter) Logf ¶
func (r *TerminalReporter) Logf(level Level, format string, args ...any)
func (*TerminalReporter) Progress ¶
func (r *TerminalReporter) Progress() *display.Progress
Progress exposes the underlying activity for the few call sites that still need it directly. Kept narrow on purpose: everything reachable through Reporter should go through Reporter.
func (*TerminalReporter) Stage ¶
func (r *TerminalReporter) Stage(stage string)
func (*TerminalReporter) Update ¶
func (r *TerminalReporter) Update(done int, stage string)
func (*TerminalReporter) Writer ¶
func (r *TerminalReporter) Writer() io.Writer
type WriterReporter ¶
type WriterReporter struct {
// contains filtered or unexported fields
}
WriterReporter writes plain lines to w and keeps a degradation ledger. It is the implementation used by tests that want to assert on output, and by any caller that has a writer but no terminal.
func NewWriterReporter ¶
func NewWriterReporter(w io.Writer) *WriterReporter
NewWriterReporter returns a Reporter writing to w. A nil w discards.
func (*WriterReporter) Complete ¶
func (r *WriterReporter) Complete(stage string)
func (*WriterReporter) Degradations ¶
func (r *WriterReporter) Degradations() []string
func (*WriterReporter) Degraded ¶
func (r *WriterReporter) Degraded(note string)
func (*WriterReporter) Fail ¶
func (r *WriterReporter) Fail(stage string)
func (*WriterReporter) Logf ¶
func (r *WriterReporter) Logf(level Level, format string, args ...any)
func (*WriterReporter) Stage ¶
func (r *WriterReporter) Stage(stage string)
func (*WriterReporter) Update ¶
func (r *WriterReporter) Update(_ int, stage string)
func (*WriterReporter) Writer ¶
func (r *WriterReporter) Writer() io.Writer