Documentation
¶
Overview ¶
Package gobump is the bump orchestration for all ecosystems: vulnerability analysis per language and modroot, reconciling each language's bump pipeline steps, results, epoch and the pipeline plumbing.
Some stages and helpers are Go-only and named as such: the simulation (go_simulate.go), the stdlib staleness check (go_stdlib*.go), go-package pins (go_pin.go, go_pin_stage.go), the best-effort go-version fallback (go_version_fallback.go, go_fallback_stage.go), `go/bump` step editing and its migration to `uses: bump` (go_step.go, go_migrate.go) and Go unit discovery (go_discovery.go). The generic code reaches them only through those stages and the seams they name (goStepRewrite, goUnitDetails).
Index ¶
- Constants
- func DistinctStdlibVulnIDs(bumps []StdlibBump) []string
- func NewPipeline(analyzer *Analyzer, opts Options) *processor.Pipeline
- type Analyzer
- type ApkAdvisory
- type ApkAdvisoryStage
- type ApkAlignment
- type Applier
- type BumpAction
- type EpochReason
- type GoPackagePinStage
- type GoToolchainPin
- type GoVersionFallbackStage
- type LanguageAnalysis
- type ModrootAnalysis
- type NeededAction
- type Options
- type Processor
- func (p *Processor) AddAction(kind, subject, detail string)
- func (p *Processor) AddHygieneBump(fix SecurityFix)
- func (p *Processor) AddNote(note string)
- func (p *Processor) AddResiduals(residuals []simulate.Residual)
- func (p *Processor) AddSecurityFix(fix SecurityFix)
- func (p *Processor) AddUnreachableVulnIDs(ids []string)
- func (p *Processor) HasActualChanges() bool
- func (p *Processor) MarkActualChangesApplied()
- func (p *Processor) Skip(ctx context.Context, reason string)
- func (p *Processor) TagMessages(stage string, from int)
- func (p *Processor) ToResult() *Result
- type Result
- type SecurityFix
- type SimulationStage
- type StdlibBump
- type StdlibStage
- type VulnerabilityAnalysis
- type VulnerabilityChecker
Constants ¶
const ( SectionChanges = "Changes" SectionAdvisories = "Advisories" SectionApk = "apk" SectionStdlib = "stdlib" SectionOther = "Other" SectionHidden = "hidden" )
Report sections of the -v details, in display order; SectionHidden messages (epoch, validation, file writing) are not rendered by bump but stay in Messages, which update matches on.
const ( ActionPatch = "patch" // a first-party advisory needs a source patch ActionRerun = "rerun" // the result is incomplete; rerun once the cause is gone ActionReview = "review" // a human must decide (a fix exists but was not applied/proven) ActionAccept = "accept" // nothing to do until upstream releases a fix )
NeededAction kinds, in the order the report lists them.
Variables ¶
This section is empty.
Functions ¶
func DistinctStdlibVulnIDs ¶ added in v0.4.0
func DistinctStdlibVulnIDs(bumps []StdlibBump) []string
DistinctStdlibVulnIDs returns the sorted, deduplicated vulnerability IDs across a file's stdlib bumps (an ID recurs across go-package pin constraints).
Types ¶
type Analyzer ¶
type Analyzer struct {
// contains filtered or unexported fields
}
Analyzer holds the shared, ecosystem-agnostic infrastructure - remote manifest fetching and OSV vulnerability scanning - used across every bump/go-bump pipeline step, regardless of language.
func NewAnalyzer ¶
NewAnalyzer creates a new analyzer with the provided HTTP client
type ApkAdvisory ¶ added in v0.4.0
type ApkAdvisory struct {
ID string `json:"id" yaml:"id"` // the CGA- record ID
Upstream []string `json:"upstream" yaml:"upstream"` // CVE-/GHSA-/GO- IDs it covers
FixedVersion string `json:"fixed_version" yaml:"fixed_version"` // "<version>-r<FixedEpoch>"
FixedEpoch int64 `json:"fixed_epoch" yaml:"fixed_epoch"` // N > the pristine epoch
Status string `json:"status" yaml:"status"` // proven, unproven, first-party, not-applicable
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"` // why not proven
Modules []string `json:"modules,omitempty" yaml:"modules,omitempty"` // affected modules, from the upstream records
}
ApkAdvisory is one epoch-addressable Chainguard/Wolfi OSV advisory (CGA-) affecting the package's own APK name at its pristine version-epoch, classified by whether this build provably fixes every component its upstream records affect (see classifyApk).
func (ApkAdvisory) DisplayID ¶ added in v0.4.0
func (a ApkAdvisory) DisplayID() string
DisplayID is the advisory's report ID (see apkDisplayID).
type ApkAdvisoryStage ¶ added in v0.4.0
type ApkAdvisoryStage struct {
processor.BaseStage
Options Options
// contains filtered or unexported fields
}
ApkAdvisoryStage aligns package.epoch with the Chainguard/Wolfi advisories published for the package's own APK name: scanners match those by name and "-r<epoch>", so an advisory whose upstream issue this build provably fixes (see classifyApk) still reads as open until our epoch reaches Chainguard's. It records ApkEpochTarget for the epoch stage (see apkNewEpoch) - never past an advisory not proven fixed - and alerts on advisories affecting the package's own module (first-party, which a dependency bump cannot fix).
Best-effort like StdlibStage: an OSV failure skips with a message and never raises the epoch. Idempotent: at "<version>-r<target>" OSV no longer returns the advisories the target cleared.
func NewApkAdvisoryStage ¶ added in v0.4.0
func NewApkAdvisoryStage(analyzer *Analyzer, opts Options) *ApkAdvisoryStage
type ApkAlignment ¶ added in v0.4.0
type ApkAlignment struct {
Version string `json:"version" yaml:"version"`
TargetEpoch int64 `json:"target_epoch" yaml:"target_epoch"`
HighestFixedEpoch int64 `json:"highest_fixed_epoch" yaml:"highest_fixed_epoch"`
BlockedAt int64 `json:"blocked_at,omitempty" yaml:"blocked_at,omitempty"`
Proven int `json:"proven" yaml:"proven"`
Unproven int `json:"unproven" yaml:"unproven"`
FirstParty int `json:"first_party" yaml:"first_party"`
NotApplicable int `json:"not_applicable,omitempty" yaml:"not_applicable,omitempty"`
Ignored int `json:"ignored" yaml:"ignored"`
}
ApkAlignment summarizes the apk advisory alignment (see ApkAdvisoryStage): Version is the pristine "<version>-r<epoch>" queried; TargetEpoch the epoch aligned to (Result.ApkEpochTarget); HighestFixedEpoch the highest fix epoch among addressable advisories; BlockedAt the lowest non-proven fix epoch other than not-applicable ones (0 = none); the counts are per display ID and fix; Ignored counts the advisories no epoch can clear or that cannot be correlated, of which Uncorrelated those without an upstream CVE/GHSA/GO ID.
type Applier ¶ added in v0.4.0
Applier applies bump/go-bump pipeline changes
func NewApplier ¶ added in v0.4.0
type BumpAction ¶
type BumpAction struct {
Action string `json:"action" yaml:"action"` // "needs_bump"
Language string `json:"language" yaml:"language"` // "go", "rust", "java"
Modroots []string `json:"modroots" yaml:"modroots"` // module roots this action applies to
Dependencies []string `json:"dependencies" yaml:"dependencies"` // newly required dependency entries, in this language's grammar
Replaces []string `json:"replaces,omitempty" yaml:"replaces,omitempty"` // new/changed replace directives ("old=new@version", Go only)
Reason string `json:"reason" yaml:"reason"` // human-readable reason
}
BumpAction summarizes a change needed for one or more module roots, for reporting purposes (see Result.ActionsApplied).
type EpochReason ¶ added in v0.4.0
type EpochReason struct {
Kind string `json:"kind" yaml:"kind"` // deps, hygiene, stdlib, apk, build
Text string `json:"text" yaml:"text"`
VulnIDs []string `json:"vuln_ids,omitempty" yaml:"vuln_ids,omitempty"`
}
EpochReason is one clause of why the epoch moved (the epoch comment's wording) with the advisory IDs behind it.
type GoPackagePinStage ¶ added in v0.4.0
GoPackagePinStage is the Go-only go-package pin reconciliation: it raises too-old go-package toolchain pins on go/build|go/install steps to the per-modroot Go floors (see reconcileGoPackagePins and goPinFloors). It runs after the applier - it edits the YAML the applier rewrote, and its floors include the RequiredGoVersion the fallback/simulation recorded - and before StdlibStage, which reads the RaisedPinMinors it records.
func NewGoPackagePinStage ¶ added in v0.4.0
func NewGoPackagePinStage(analyzer *Analyzer) *GoPackagePinStage
type GoToolchainPin ¶ added in v0.3.0
type GoToolchainPin struct {
Package string // rendered with.go-package value; "" when the step has no go-package
Minor string // "1.24" when Package carries a trailing -1.X suffix; "" = unpinned
}
GoToolchainPin describes one go/build|go/install step's toolchain selection.
type GoVersionFallbackStage ¶ added in v0.4.0
type GoVersionFallbackStage struct {
processor.BaseStage
Analyzer *Analyzer
// contains filtered or unexported fields
}
GoVersionFallbackStage is the Go-only best-effort go-version fallback: for Go modroots the simulation did not prove (--no-validate, a degraded simulation, or per-modroot skips) it derives RequiredGoVersion from the candidate deps' own go.mod files on the module proxy (see fallbackRequiredGoVersion). It runs before the applier, whose go-version edits (fastPathGoVersion) and the go-package pin floors (goPinFloors) read RequiredGoVersion.
func NewGoVersionFallbackStage ¶ added in v0.4.0
func NewGoVersionFallbackStage(analyzer *Analyzer) *GoVersionFallbackStage
type LanguageAnalysis ¶ added in v0.3.0
type LanguageAnalysis struct {
Language string `json:"language" yaml:"language"`
ByModroot []ModrootAnalysis `json:"by_modroot" yaml:"by_modroot"`
}
LanguageAnalysis contains the results of scanning one language's dependencies across all of its module roots.
type ModrootAnalysis ¶ added in v0.3.0
type ModrootAnalysis struct {
Modroot string `json:"modroot" yaml:"modroot"`
// BuildPackages are this modroot's go/build steps' with.packages
// patterns (space-separated grammar, relative to Modroot), whose
// non-test import graphs define artifact reachability. Empty when no
// go/build step covers this modroot (bump-step- or annotation-declared
// roots); the simulation then over-approximates with ./....
BuildPackages []string `json:"build_packages,omitempty" yaml:"build_packages,omitempty"`
// BuildTags are this modroot's go/build steps' build tags (each step's
// toolchaintags - default netgo,osusergo - plus its tags), unioned, for
// the simulation's compile gate. Empty when no go/build step covers it.
BuildTags []string `json:"build_tags,omitempty" yaml:"build_tags,omitempty"`
// BuildArches are the GOARCHes the package is built for (from
// package.target-architecture) and BuildEnv the build environment the
// spec sets for this modroot (see analysisUnit). Go only.
BuildArches []string `json:"-" yaml:"-"`
BuildEnv simulate.BuildEnv `json:"-" yaml:"-"`
// BumpEngine is the apply semantics the build's bump step will use for
// this modroot once written (see analysisUnit.Engine); BumpMigrating
// marks a go/bump step that migrates to `uses: bump` when rewritten;
// BumpNoTidy mirrors that step's `tidy: false`; GoPackageMinor is the Go
// minor the build pins ("" when unpinned). BaselineTidies records that
// the simulation found the pristine module tidies under omnibump (only
// probed for a migrating `tidy: false` step). Go only.
BumpEngine simulate.Engine `json:"-" yaml:"-"`
BumpMigrating bool `json:"-" yaml:"-"`
BumpNoTidy bool `json:"-" yaml:"-"`
GoPackageMinor string `json:"-" yaml:"-"`
BaselineTidies bool `json:"-" yaml:"-"`
Deps *ecosystem.ModuleDeps `json:"-" yaml:"-"` // ecosystem-internal, not serialized
// ScannedPackages are the packages (at their pristine versions) the
// analysis scan queried OSV for: an advisory absent from ScanResult but
// affecting one of them does not affect the version shipped.
ScannedPackages []scan.Package `json:"-" yaml:"-"`
ScanResult *scan.ScanResult `json:"scan_result" yaml:"scan_result"`
ExistingDeps []string `json:"existing_deps" yaml:"existing_deps"` // deps currently declared for this root
DesiredDeps []string `json:"desired_deps" yaml:"desired_deps"` // deps this root should end up declaring
// Replace directives ("old=new@version", Go only): what the root's bump
// steps currently declare, and what they should end up declaring.
// Analysis only carries existing replaces forward; the simulation may
// raise or retire them (see SimulationStage and internal/simulate).
ExistingReplaces []string `json:"existing_replaces,omitempty" yaml:"existing_replaces,omitempty"`
DesiredReplaces []string `json:"desired_replaces,omitempty" yaml:"desired_replaces,omitempty"`
// RequiredGoVersion is the Go language version demanded by the proven
// candidate set (bare form, e.g. "1.25"); set only when it exceeds the
// module's own baseline (go directive / toolchain directive max - see
// pristineGoBaseline). Empty = no raise needed or unknown. Populated by
// the simulation (proven) or, when simulation didn't run, by the
// best-effort proxy fallback (direct candidates only). Go only.
RequiredGoVersion string `json:"required_go_version,omitempty" yaml:"required_go_version,omitempty"`
// ExistingGoVersion is the highest with.go-version carried by existing
// bump/go-bump steps covering this modroot (bare form); empty when none.
// Carried so reconciliation can never lower a previously written value.
// Go only.
ExistingGoVersion string `json:"existing_go_version,omitempty" yaml:"existing_go_version,omitempty"`
// SecurityBumpModules lists the rendered-grammar coordinates (see
// splitCoordVersion and Ecosystem.BumpCoords) of the dependencies OSV
// actually flagged as vulnerable for this modroot - i.e.
// scanResult.SecurityBumps, not DesiredDeps. The desired set may carry
// additional entries with no CVE of their own (e.g. coherence pins the
// simulation adds to keep the module graph resolvable); this
// narrower list lets the applier credit only genuine CVE fixes toward
// SecurityFixes/epoch-bump accounting, so a coherence-only entry can't
// masquerade as a vulnerability fix. The simulation overwrites it
// with its own CVE-backed module set (rendered paths, same coordinate
// space) when it runs.
SecurityBumpModules []string `json:"security_bump_modules" yaml:"security_bump_modules"`
// SecurityBumpsByCoord maps each of those rendered coordinates back to
// its originating OSV bump, so reporting can attribute real advisory IDs
// and prior versions to a fix (OSV's own package names don't always
// match the rendered grammar - Maven uses "groupId:artifactId", and v2+
// Go module paths lack the /vN suffix).
SecurityBumpsByCoord map[string]scan.SecurityBump `json:"security_bumps_by_coord,omitempty" yaml:"security_bumps_by_coord,omitempty"`
// Simulation outcome for this modroot (Go only; nil/false when the bump
// simulation didn't run - see SimulationStage).
Simulated bool `json:"simulated" yaml:"simulated"`
SimulationConverged bool `json:"simulation_converged" yaml:"simulation_converged"`
Residuals []simulate.Residual `json:"residuals,omitempty" yaml:"residuals,omitempty"`
Dropped []simulate.DroppedCandidate `json:"dropped,omitempty" yaml:"dropped,omitempty"`
// HygieneModules are the DesiredDeps entries the simulation proposed only
// for scanner hygiene (see simulate.HygieneModule).
HygieneModules []simulate.HygieneModule `json:"hygiene_modules,omitempty" yaml:"hygiene_modules,omitempty"`
}
ModrootAnalysis contains the dependency analysis for a single module root. A package may declare one or more module roots (via bump/go-bump pipeline steps' with.modroot, or a go/build step's with.modroot), each with its own manifest and therefore its own applicable set of security bumps.
type NeededAction ¶ added in v0.4.0
type NeededAction struct {
Kind string `json:"kind" yaml:"kind"` // patch, rerun, review, accept
Subject string `json:"subject" yaml:"subject"`
Detail string `json:"detail,omitempty" yaml:"detail,omitempty"`
}
NeededAction is one thing the user must do about a package's result.
type Options ¶ added in v0.4.0
type Options struct {
processor.ProcessorOptions
// Validate enables bump simulation (default true): Go bump candidates
// are applied to a clone of the upstream source with the real go
// toolchain and OSV-rescanned to a fixpoint before being written (see
// internal/simulate). Requires a go binary and network access; when
// unavailable the run degrades to the unvalidated behavior and says so.
Validate bool `json:"validate" yaml:"validate"`
// SimulationTimeout bounds each package's bump simulation (default 10m).
SimulationTimeout time.Duration `json:"simulation_timeout" yaml:"simulation_timeout"`
// Compile enables the simulation's compile gate (default true at the
// CLI; --no-compile disables it): the validated deps set must also
// compile the go/build packages without new failures (see
// simulate.Compiler). Moot when Validate is false.
Compile bool `json:"compile" yaml:"compile"`
// StdlibCheck enables the Go stdlib staleness check (see StdlibStage):
// estimate the toolchain the package was last built with and bump the
// epoch when a rebuild with the newest allowed Go release would fix
// stdlib vulnerabilities. On by default at the CLI.
StdlibCheck bool `json:"stdlib_check" yaml:"stdlib_check"`
// ApkAdvisoryCheck enables the Chainguard/Wolfi APK-advisory epoch
// alignment (see ApkAdvisoryStage). On by default at the CLI.
ApkAdvisoryCheck bool `json:"apk_advisory_check" yaml:"apk_advisory_check"`
}
Options configures processor behavior: the shared processor options (DryRun, BackupSuffix, TempDir; Force is unused by bump) plus the bump-specific ones below.
type Processor ¶ added in v0.4.0
type Processor struct {
*processor.BaseProcessor
// Security-specific fields
VulnerabilityAnalysis *VulnerabilityAnalysis `json:"vulnerability_analysis,omitempty"`
SecurityFixes []SecurityFix `json:"security_fixes"`
ActualChangesApplied bool `json:"actual_changes_applied"`
// HygieneBumps are the scanner-hygiene entries the applier wrote (see
// Result.HygieneBumps); like SecurityFixes they justify an epoch
// bump only together with ActualChangesApplied.
HygieneBumps []SecurityFix `json:"hygiene_bumps,omitempty"`
// Validated is true when the written deps lists were proven by
// simulation (see SimulationStage); Residuals aggregates the advisories
// simulation could not eliminate, across all modroots.
Validated bool `json:"validated"`
Residuals []simulate.Residual `json:"residuals,omitempty"`
// SimulationError is why simulation was unavailable (set when the run
// degraded to the unvalidated candidate set); "" otherwise.
SimulationError string `json:"simulation_error,omitempty"`
// UnreachableVulnIDs are advisory IDs found by the analysis scan whose
// modules are not linked into any build artifact in ANY modroot (see
// SimulationStage's reachability diff) - informational: no bump is
// proposed for them and they count neither as fixed nor residual.
UnreachableVulnIDs []string `json:"unreachable_vuln_ids,omitempty"`
// SkipReasons say why (part of) the package was not analyzed - e.g. its
// source cannot be fetched (no git-checkout step, a non-GitHub
// repository) or a detected language is unsupported. Non-empty means the
// result is SKIPPED, never clean.
SkipReasons []string `json:"skip_reasons,omitempty"`
// ExcludedReason is why the package opted out of bumping via the
// choam/bump-exclude-reason annotation ("" = not excluded). Only stdlib
// rebuild epoch bumps still apply; see ProcessFile.
ExcludedReason string `json:"excluded_reason,omitempty"`
// Notes are degradations of an otherwise complete analysis that the
// user must see at default verbosity (e.g. artifact linkage unavailable,
// so reachability classification was skipped); see AddNote.
Notes []string `json:"notes,omitempty"`
// StdlibBumps are the Go stdlib staleness findings (see StdlibStage) -
// each justifies an epoch bump on its own, independent of dependency
// changes. StdlibChecked is true when the staleness check ran to
// completion (false when skipped or degraded).
StdlibBumps []StdlibBump `json:"stdlib_bumps,omitempty"`
StdlibChecked bool `json:"stdlib_checked"`
// StdlibRebuildGoVersions are the Go releases the next build resolves to,
// one per go-package constraint (see StdlibStage); nil = unknown (stage
// skipped, or a constraint deferred), so stdlib fixes are unprovable.
StdlibRebuildGoVersions []string `json:"-"`
// ApkAdvisories, ApkEpochTarget, FirstPartyVulnIDs and ApkAlignment are
// the APK-advisory alignment outcome (see ApkAdvisoryStage and Result).
ApkAdvisories []ApkAdvisory `json:"apk_advisories,omitempty"`
ApkEpochTarget int64 `json:"apk_epoch_target,omitempty"`
FirstPartyVulnIDs []string `json:"first_party_vuln_ids,omitempty"`
ApkAlignment *ApkAlignment `json:"apk_alignment,omitempty"`
// MessageSections parallels the messages: the report section of each
// (see TagMessages).
MessageSections []string `json:"-"`
// ActionsNeeded are the stage-recorded actions (see AddAction); ToResult
// adds those derived from the final state.
ActionsNeeded []NeededAction `json:"actions_needed,omitempty"`
// LinkedStdPackages is the union, across simulated Go modroots, of
// standard-library import paths linked into the build artifacts
// (GOOS=linux; see simulate.ModrootResult.StdPackages). Non-nil only
// when EVERY simulated Go modroot contributed a validated set (see
// SimulationStage.Apply's stdComplete tracking); a modroot that was
// skipped or whose stdlib walk failed open discards the whole union
// rather than publishing a partial one. nil = unknown (simulation
// didn't run, a modroot was skipped, or any modroot's toolchain walk
// failed open) - the stdlib staleness stage then fails open.
LinkedStdPackages map[string]struct{} `json:"-"`
// RaisedPinMinors records the effective outcome of this run's go-package
// pin reconciliation (see reconcileGoPackagePins): original pin minor ->
// post-run minor, identity when the pin was already sufficient. Only
// versioned, non-templated pins record entries; nil when reconciliation
// didn't run. The stdlib staleness check applies it so the rebuild-side
// constraint reflects same-run pin raises.
RaisedPinMinors map[string]string `json:"-"`
// contains filtered or unexported fields
}
Processor extends BaseProcessor with security-specific fields
func NewProcessor ¶ added in v0.4.0
NewProcessor creates a new gobump processor
func (*Processor) AddAction ¶ added in v0.4.0
AddAction records a needed action, deduplicated on kind and subject.
func (*Processor) AddHygieneBump ¶ added in v0.4.0
func (p *Processor) AddHygieneBump(fix SecurityFix)
AddHygieneBump records a scanner-hygiene entry the applier wrote.
func (*Processor) AddNote ¶ added in v0.4.0
AddNote records a default-verbosity note (see Notes), deduplicated.
func (*Processor) AddResiduals ¶ added in v0.4.0
AddResiduals aggregates per-modroot simulation residuals, deduplicated by (module, reason) across modroots.
func (*Processor) AddSecurityFix ¶ added in v0.4.0
func (p *Processor) AddSecurityFix(fix SecurityFix)
AddSecurityFix adds a security fix to the processor
func (*Processor) AddUnreachableVulnIDs ¶ added in v0.4.0
AddUnreachableVulnIDs records advisory IDs affecting only unlinked modules, deduplicated.
func (*Processor) HasActualChanges ¶ added in v0.4.0
HasActualChanges returns true if actual file modifications were made (not just vulnerabilities found)
func (*Processor) MarkActualChangesApplied ¶ added in v0.4.0
func (p *Processor) MarkActualChangesApplied()
MarkActualChangesApplied marks that actual changes were made to the YAML This is critical for the epoch bumping logic - epoch should only bump when file changes occur
func (*Processor) Skip ¶ added in v0.4.0
Skip records why (part of) the package was not analyzed: logged now, and reported with the result (see SkipReasons).
func (*Processor) TagMessages ¶ added in v0.4.0
TagMessages assigns the messages from index from onwards to stage's section (processor.MessageTagger); earlier untagged messages, added outside any stage, are SectionOther.
func (*Processor) ToResult ¶ added in v0.4.0
ToResult converts the processor state to a Result.
Advisory accounting: VulnerabilitiesFound counts unique advisories across the package (the baseline analysis scan). When the bump was validated by simulation, the residual advisory count is exact (the final resolved graph was rescanned) and unreachable advisories (unlinked modules, informational only) are known, so VulnerabilitiesFixed = found - baseline residuals - unreachable (the count of fixedVulnIDs). Residuals the bump itself INTRODUCED (advisories absent from the baseline scan - see simulate.Residual.Introduced) still count as residual but never subtract from found: they were never in it. Without validation there is no proof of what the bump actually fixes, so Fixed falls back to the historical approximation (modules changed), which is also always reported separately as ModulesBumped, and advisories with no released fix are residual (see noFixResiduals). CriticalFixed/HighFixed count fixedVulnIDs by the analysis scan's severity.
type Result ¶ added in v0.4.0
type Result struct {
PackageName string `json:"package_name" yaml:"package_name"`
FilePath string `json:"file_path" yaml:"file_path"`
VulnerabilitiesFound int `json:"vulnerabilities_found" yaml:"vulnerabilities_found"`
VulnerabilitiesFixed int `json:"vulnerabilities_fixed" yaml:"vulnerabilities_fixed"`
VulnerabilitiesResidual int `json:"vulnerabilities_residual" yaml:"vulnerabilities_residual"`
// VulnerabilitiesUnreachable counts advisories affecting only modules
// that are not linked into any build artifact (module-level go list
// -deps reachability) - informational: no bump proposed, neither fixed
// nor residual.
VulnerabilitiesUnreachable int `json:"vulnerabilities_unreachable" yaml:"vulnerabilities_unreachable"`
UnreachableVulnIDs []string `json:"unreachable_vuln_ids,omitempty" yaml:"unreachable_vuln_ids,omitempty"`
ModulesBumped int `json:"modules_bumped" yaml:"modules_bumped"`
Validated bool `json:"validated" yaml:"validated"`
SimulationError string `json:"simulation_error,omitempty" yaml:"simulation_error,omitempty"` // why simulation was unavailable (Validated=false)
Residuals []simulate.Residual `json:"residuals,omitempty" yaml:"residuals,omitempty"`
CriticalFixed int `json:"critical_fixed" yaml:"critical_fixed"`
HighFixed int `json:"high_fixed" yaml:"high_fixed"`
SecurityFixes []SecurityFix `json:"security_fixes" yaml:"security_fixes"`
// HygieneBumps are the scanner-hygiene entries written (see
// simulate.HygieneModule): modules raised only so module-level scanners
// stop flagging advisories whose vulnerable packages are not linked.
// Never security fixes - their advisories stay unreachable, uncounted by
// Fixed; HygieneModulesBumped counts them (ModulesBumped does not).
HygieneBumps []SecurityFix `json:"hygiene_bumps,omitempty" yaml:"hygiene_bumps,omitempty"`
HygieneModulesBumped int `json:"hygiene_modules_bumped,omitempty" yaml:"hygiene_modules_bumped,omitempty"`
ActionsApplied []BumpAction `json:"actions_applied" yaml:"actions_applied"`
// SkipReasons say why (part of) the package was not analyzed (status
// SKIPPED; see Processor.SkipReasons).
SkipReasons []string `json:"skip_reasons,omitempty" yaml:"skip_reasons,omitempty"`
// ExcludedReason is the choam/bump-exclude-reason annotation value (status
// EXCLUDED; see Processor.ExcludedReason).
ExcludedReason string `json:"excluded_reason,omitempty" yaml:"excluded_reason,omitempty"`
// Notes are degradations shown at default verbosity (see
// Processor.Notes); they do not change the row's status.
Notes []string `json:"notes,omitempty" yaml:"notes,omitempty"`
// StdlibBumps are the Go stdlib staleness findings that (each) justify
// an epoch bump; StdlibChecked reports whether the staleness check ran
// to completion (false when disabled, inapplicable, or skipped).
StdlibBumps []StdlibBump `json:"stdlib_bumps,omitempty" yaml:"stdlib_bumps,omitempty"`
StdlibChecked bool `json:"stdlib_checked" yaml:"stdlib_checked"`
// ApkAdvisories are the Chainguard/Wolfi advisories for this package's
// own APK name and version (see ApkAdvisoryStage); ApkEpochTarget is the
// lowest epoch clearing the proven ones (0 = no raise); FirstPartyVulnIDs
// are those matching no dependency or stdlib advisory.
ApkAdvisories []ApkAdvisory `json:"apk_advisories,omitempty" yaml:"apk_advisories,omitempty"`
ApkEpochTarget int64 `json:"apk_epoch_target,omitempty" yaml:"apk_epoch_target,omitempty"`
FirstPartyVulnIDs []string `json:"first_party_vuln_ids,omitempty" yaml:"first_party_vuln_ids,omitempty"`
ApkAlignment *ApkAlignment `json:"apk_alignment,omitempty" yaml:"apk_alignment,omitempty"`
// ActionsNeeded are what the user must do about this result;
// EpochReasons why the epoch moved (set only when it did).
ActionsNeeded []NeededAction `json:"actions_needed,omitempty" yaml:"actions_needed,omitempty"`
EpochReasons []EpochReason `json:"epoch_reasons,omitempty" yaml:"epoch_reasons,omitempty"`
OldEpoch int64 `json:"old_epoch" yaml:"old_epoch"`
NewEpoch int64 `json:"new_epoch" yaml:"new_epoch"`
EpochChanged bool `json:"epoch_changed" yaml:"epoch_changed"`
FileWasWritten bool `json:"file_was_written" yaml:"file_was_written"`
Messages []string `json:"messages" yaml:"messages"`
// MessageSections parallels Messages (see Processor.TagMessages); a
// message beyond its end is SectionOther.
MessageSections []string `json:"-" yaml:"-"`
Error string `json:"error,omitempty" yaml:"error,omitempty"`
}
Result represents the result of a bump operation.
Counting units: VulnerabilitiesFound and VulnerabilitiesFixed/Residual count advisories; ModulesBumped counts dependency entries changed. When Validated is true, Fixed/Residual are proven by simulating the bump against a real checkout; otherwise Fixed falls back to the historical modules-changed approximation.
type SecurityFix ¶
type SecurityFix struct {
Module string `json:"module" yaml:"module"` // affected package
Vulnerability string `json:"vulnerability" yaml:"vulnerability"` // CVE or vulnerability ID
OldVersion string `json:"old_version" yaml:"old_version"` // version before fix
NewVersion string `json:"new_version" yaml:"new_version"` // version after fix
Severity string `json:"severity" yaml:"severity"` // critical, high, medium, low
}
SecurityFix represents a security vulnerability fix applied
type SimulationStage ¶ added in v0.3.0
type SimulationStage struct {
processor.BaseStage
Analyzer *Analyzer
Options Options
// contains filtered or unexported fields
}
SimulationStage validates the Go bump candidate sets computed by the vulnerability check before the applier writes them: it clones the source the build will use, applies the candidates with the real go toolchain, and OSV-rescans the resolved graph to a fixpoint (see internal/simulate). The per-modroot DesiredDeps are replaced with the proven set; anything unreachable is reported as a residual. When simulation cannot run at all (no go binary, clone failure, --no-validate) the pre-simulation candidates pass through unchanged and the result is marked unvalidated.
func NewSimulationStage ¶ added in v0.3.0
func NewSimulationStage(analyzer *Analyzer, opts Options) *SimulationStage
type StdlibBump ¶ added in v0.3.0
type StdlibBump struct {
AssumedGoVersion string `json:"assumed_go_version" yaml:"assumed_go_version"`
AssumedFromDate string `json:"assumed_from_date" yaml:"assumed_from_date"` // RFC3339 raw last-commit time (the assumed-version lookup subtracts a publication-age lag margin; see stdlibToolchainLagMargin)
GoPackagePin string `json:"go_package_pin,omitempty" yaml:"go_package_pin,omitempty"` // minor constraint the package was built under, "" = unpinned
// RebuildGoPackagePin is the minor constraint the NEXT build will use,
// set only when it differs from GoPackagePin (a same-run go-package pin
// raise, see reconcileGoPackagePins).
RebuildGoPackagePin string `json:"rebuild_go_package_pin,omitempty" yaml:"rebuild_go_package_pin,omitempty"`
RebuildGoVersion string `json:"rebuild_go_version" yaml:"rebuild_go_version"`
VulnIDs []string `json:"vuln_ids" yaml:"vuln_ids"`
UnlinkedVulnIDs []string `json:"unlinked_vuln_ids,omitempty" yaml:"unlinked_vuln_ids,omitempty"`
Validated bool `json:"validated" yaml:"validated"` // linked-import filtering applied
}
StdlibBump records why a stdlib-driven epoch bump is (or would be) applied: rebuilding with the newest allowed Go release would fix stdlib advisories present in the toolchain the package was (estimatedly) last built with. One entry per go-package pin constraint that yields fixable vulnerabilities (see evaluateStdlibStaleness).
type StdlibStage ¶ added in v0.3.0
type StdlibStage struct {
processor.BaseStage
Analyzer *Analyzer
Options Options
// contains filtered or unexported fields
}
StdlibStage checks whether the package is stale against the Go standard library: it estimates the toolchain the package was last built with (latest Go release as of the melange file's last commit, per go-package pin constraint), diffs OSV stdlib findings against the newest allowed release, and records a StdlibBump when a rebuild would fix advisories - which the epoch stage then turns into an epoch bump, even with ZERO dependency changes (the rebuild itself is the fix). Independently of git, it records the rebuild Go version(s) (StdlibRebuildGoVersions): whatever bumps the epoch, the rebuild uses them, which is the apk stage's stdlib proof.
The stage is strictly best-effort: every failure degrades to a skip-with-message and never fails the bump run. The last-commit Dirty gate doubles as the idempotency guard for the staleness trigger - once this run's epoch bump is written, the file is dirty and subsequent runs skip the trigger until the change is committed (in-run dependency edits live only in CurrentYAML, so they never trip it).
func NewStdlibStage ¶ added in v0.3.0
func NewStdlibStage(analyzer *Analyzer, opts Options) *StdlibStage
func (*StdlibStage) Apply ¶ added in v0.3.0
Apply runs the staleness check. EVERY failure path degrades to a skip-with-message and returns nil - this stage must never fail the run.
func (*StdlibStage) ShouldRun ¶ added in v0.3.0
ShouldRun gates on the option flag and on the package actually building Go code (go/build or go/install steps - see scanBuildSteps): only such packages embed the stdlib in their artifacts. Deliberately independent of vulnerability findings or bump actions - the pipeline runner evaluates each stage's ShouldRun on its own, so this stage runs even when every earlier stage was skipped (a package with no dependency changes can still need a stdlib rebuild).
type VulnerabilityAnalysis ¶
type VulnerabilityAnalysis struct {
ByLanguage []LanguageAnalysis `json:"by_language" yaml:"by_language"`
VulnerabilitiesFound int `json:"vulnerabilities_found" yaml:"vulnerabilities_found"` // aggregated across all languages
BumpActions []BumpAction `json:"bump_actions" yaml:"bump_actions"` // each carries its own Language
// Source coordinates from the package's git-checkout step, retained so
// the simulation stage can clone the exact source the build will use.
RepoURL string `json:"repo_url,omitempty" yaml:"repo_url,omitempty"`
Tag string `json:"tag,omitempty" yaml:"tag,omitempty"`
ExpectedCommit string `json:"expected_commit,omitempty" yaml:"expected_commit,omitempty"`
}
VulnerabilityAnalysis contains the results of scanning a package's dependencies across every language and module root discovered for it. A package may have more than one language (e.g. a Go service alongside a Rust CLI in the same repo).
type VulnerabilityChecker ¶
VulnerabilityChecker checks for dependency vulnerabilities
func NewVulnerabilityChecker ¶
func NewVulnerabilityChecker(analyzer *Analyzer) *VulnerabilityChecker