Documentation
¶
Overview ¶
Package simulate validates a proposed set of Go module bumps by doing what the melange build's bump step will do, ahead of time: clone the upstream source at the exact tag, apply the bumps with the step's own semantics (gobump for `uses: go/bump`, omnibump for `uses: bump` - see Engine), then OSV-rescan the resolved module graph and raise versions until a fixpoint. The deps list that survives is proven to (a) resolve cleanly and (b) leave no known-fixable vulnerability behind; anything unreachable is reported as a residual rather than silently dropped.
Index ¶
- Variables
- func Checkout(ctx context.Context, cloner Cloner, ...) (dir string, cleanup func(), err error)
- func CompileCgoLimited(arch string, env BuildEnv) bool
- func ModrootDir(root, modroot string) string
- func OmnibumpVersion() string
- func TargetArches(arches []string) []string
- type BuildEnv
- type BuildTarget
- type Candidate
- type Cloner
- type CompileReport
- type Compiler
- type DroppedCandidate
- type Engine
- type GoToolchain
- func (t *GoToolchain) Compile(ctx context.Context, dir string, target BuildTarget, arch string) (*CompileReport, error)
- func (t *GoToolchain) DepGoVersions(ctx context.Context, dir string) (map[string]string, error)
- func (t *GoToolchain) Get(ctx context.Context, dir, moduleAtVersion string) error
- func (t *GoToolchain) Linked(ctx context.Context, dir string, target BuildTarget) (Linkage, error)
- func (t *GoToolchain) LinkedStd(ctx context.Context, dir string, target BuildTarget) (map[string]struct{}, error)
- func (t *GoToolchain) ListModules(ctx context.Context, dir string) (map[string]string, error)
- func (t *GoToolchain) ModTidy(ctx context.Context, dir string) error
- func (t *GoToolchain) ModuleRequires(ctx context.Context, dir, modulePath, version string) (map[string]string, error)
- func (t *GoToolchain) ModuleVersions(ctx context.Context, dir, modulePath string) ([]string, error)
- func (t *GoToolchain) Replace(ctx context.Context, dir, oldPath, newPath, version string) error
- func (t *GoToolchain) Replaces(_ context.Context, dir string) (map[string]ReplaceTarget, error)
- func (t *GoToolchain) RequiredBy(ctx context.Context, dir string) (map[string]map[string]string, error)
- func (t *GoToolchain) Requirements(_ context.Context, dir string) (map[string]string, error)
- func (t *GoToolchain) ResolveQuery(ctx context.Context, dir, modulePath, query string) (string, error)
- type HygieneModule
- type Linkage
- type ModrootRequest
- type ModrootResult
- type Options
- type ReplaceTarget
- type Residual
- type Rung
- type Scanner
- type Simulator
- type Toolchain
- type UnmatchedEmbed
Constants ¶
This section is empty.
Variables ¶
var ErrCompileGate = errors.New("compile gate failed")
ErrCompileGate marks a simulation the compile gate could not finish (baseline unavailable, tool failure, budget exhausted, or a build that fails with every bump relaxed). Callers must fail closed: the candidate set was never proven to compile, so it must not be written.
var ErrInfrastructure = errors.New("go tool infrastructure failure")
ErrInfrastructure marks a go tool failure caused by the environment - the network, the module proxy or VCS host (connection errors, TLS timeouts, 5xx responses) or a per-command timeout - rather than by the module graph. It is never a verdict on a candidate: the simulation fails (closed) with it instead of shedding or residualising anything.
Functions ¶
func Checkout ¶ added in v0.4.0
func Checkout(ctx context.Context, cloner Cloner, repoURL, tag, expectedCommit, tmpDir, purpose string) (dir string, cleanup func(), err error)
Checkout clones repoURL at tag into a fresh directory under tmpDir ("" = the os.MkdirTemp default) named for purpose ("choam-<purpose>-*"). expectedCommit, when non-empty, is verified warn-only (melange enforces it at build time). cleanup removes the directory; call it once err is nil.
func CompileCgoLimited ¶ added in v0.4.0
CompileCgoLimited reports whether the compile gate must type-check arch with CGO disabled although the build enables it (see targetBuildEnv).
func ModrootDir ¶ added in v0.4.0
ModrootDir is modroot's directory within a checkout at root ("" and "." are the root itself).
func OmnibumpVersion ¶ added in v0.4.0
func OmnibumpVersion() string
OmnibumpVersion reports the omnibump version linked into this binary - the one EngineOmnibump simulates with - or "" when build info is unavailable.
func TargetArches ¶ added in v0.4.0
TargetArches normalizes a target-arch list: deduplicated, amd64 then arm64 first and any others after in lexical order (so the compile gate's primary arch never depends on input order); empty means defaultArches.
Types ¶
type BuildEnv ¶ added in v0.4.0
type BuildEnv struct {
// CGO is the spec's CGO_ENABLED: "0" or "1", "" when unset or not
// understood (the melange default then applies: enabled).
CGO string
// GOExperiment is the GOEXPERIMENT the build runs with ("" for none).
GOExperiment string
}
BuildEnv is the part of the melange build environment that changes which files are built (see targetBuildEnv).
type BuildTarget ¶ added in v0.4.0
BuildTarget is what the melange build compiles for one modroot: build package patterns (empty means ./...), tags, the GOARCHes the package is built for (empty means amd64 and arm64; see TargetArches) and the build environment.
type Candidate ¶
type Candidate struct {
Module string
Version string
FromCVE bool
VulnIDs []string
// Severity is the most severe advisory level the candidate addresses
// (scan.SeverityRank vocabulary; empty when unknown).
Severity string
// Rungs is the candidate's fix ladder, highest first: one rung per
// distinct advisory fix version (plus an existing YAML version), each
// with the advisories it first fixes. The compile gate relaxes a
// candidate whose version breaks the build one rung down. Empty means
// the single rung {Version, VulnIDs, Severity}.
Rungs []Rung
// Replace marks a user-authored YAML replace (a replace seed), applied
// as a go.mod replace directive rather than a `go get`; the loop never
// creates replace directives of its own. ReplaceOld is the directive's
// left-hand side; empty means a self-replace (ReplaceOld == Module).
Replace bool
ReplaceOld string
}
Candidate is one proposed module@version bump. FromCVE marks candidates backed by an OSV advisory: they are defended (stepped down their fix ladder, reported as residuals when unreachable), whereas coherence-only candidates (carried-forward pins) are the first to be sacrificed when the module graph won't resolve.
type Cloner ¶
type Cloner interface {
CloneAtTag(ctx context.Context, repoURL, tag, destDir string) error
HeadCommit(ctx context.Context, dir string) (string, error)
}
Cloner is the source-checkout seam; *git.Client satisfies it.
type CompileReport ¶ added in v0.4.0
type CompileReport struct {
// Failed maps each package that failed to compile to its first error
// lines ("# pkg" header stripped).
Failed map[string][]string
// Modules maps every non-standard package in the graph to its module
// path (replacement applied); "" for main-module packages.
Modules map[string]string
// Imports holds the direct imports of each failed package, for repair
// and relaxation attribution.
Imports map[string][]string
// FileModules maps module-cache file paths named in error lines
// (<GOMODCACHE>/<mod>@<ver>/...) back to "module@version".
FileModules map[string]string
}
CompileReport is the outcome of one Compiler.Compile.
type Compiler ¶ added in v0.4.0
type Compiler interface {
// Compile compiles (without linking) the transitive non-test import
// graph of the target for GOOS=linux and GOARCH=arch, reporting
// per-package failures. An error return means the go tool itself could
// not run, not that packages failed to compile.
Compile(ctx context.Context, dir string, target BuildTarget, arch string) (*CompileReport, error)
// ModuleVersions lists module's released versions, ascending.
ModuleVersions(ctx context.Context, dir, module string) ([]string, error)
// ModuleRequires returns the require entries of module@version's own
// go.mod.
ModuleRequires(ctx context.Context, dir, module, version string) (map[string]string, error)
}
Compiler is the optional compile-gate seam: a Toolchain that also implements it enables the post-convergence compile gate (see loop.compileGate), unless Options.NoCompile is set. *GoToolchain implements it.
type DroppedCandidate ¶
type DroppedCandidate struct {
Module string `json:"module" yaml:"module"`
Version string `json:"version" yaml:"version"`
Reason string `json:"reason" yaml:"reason"`
// Redundant marks an entry removed because it did no work: it matched or
// regressed upstream, or the final tidied go.mod is identical without it.
Redundant bool `json:"redundant,omitempty" yaml:"redundant,omitempty"`
// Hygiene marks a scanner-hygiene bump that was not proposed because it
// is not free (see HygieneModule); Reason says why.
Hygiene bool `json:"hygiene,omitempty" yaml:"hygiene,omitempty"`
}
DroppedCandidate records a candidate removed during simulation and why.
type Engine ¶ added in v0.4.0
type Engine int
Engine selects the apply semantics the simulation models: what the build's bump step will actually do with the written deps list.
const ( // EngineGobump models melange's `uses: go/bump` (gobump): tidy // -go=<host>, one `go get` per entry (skipped when go.mod already // requires the module above the entry), tidy -go=<host>. The zero value. EngineGobump Engine = iota // EngineOmnibump models `uses: bump` by running omnibump's own // golang.DoUpdate behind the filter its CLI applies first (see rawSkip): // the go directive only ever lowered to the build's Go and the toolchain // line dropped, initial tidy, replaces, `go get` only for modules absent // from go.mod (or non-semver targets), the EXACT version set in place for // already-required modules (no MVS ripple), final tidy without -go, and // a post-tidy verification that no entry was downgraded. EngineOmnibump )
type GoToolchain ¶
type GoToolchain struct {
// contains filtered or unexported fields
}
GoToolchain runs the local go binary. The environment passes through the user's proxy/auth configuration (GOPROXY, GOPRIVATE, netrc via HOME) and forces the settings resolution parity depends on: GOTOOLCHAIN=auto so a go.mod demanding a newer toolchain is honored, -mod=mod so a vendor/ directory doesn't mask resolution, and GOWORK=off so a stray go.work can't leak into the analysis.
func NewToolchain ¶
NewToolchain locates the go binary; the error return lets callers degrade gracefully (skip validation, report it) when no toolchain is available.
func (*GoToolchain) Compile ¶ added in v0.4.0
func (t *GoToolchain) Compile(ctx context.Context, dir string, target BuildTarget, arch string) (*CompileReport, error)
Compile type-checks and compiles (without linking) every non-test package in the transitive import graph of the target's patterns, via `go list -e -export -deps -json`: -export forces a real compile of each package to export data, and -e records each package's own compile error on its .Error instead of aborting the walk - structured, per-package, no vet noise, no link step. A package whose dependency failed is not compiled at all (only DepsErrors), so failures surface on the deepest broken package - exactly the module that needs blaming. The build target is targetBuildEnv's for arch (one arch per call). The build cache is the user's GOCACHE, shared and incremental across compiles and runs. An error return means the go tool itself failed (not that packages failed to compile).
func (*GoToolchain) DepGoVersions ¶
DepGoVersions returns the go directive of every non-main module in the build list (bare form, e.g. "1.24" or "1.24.5"), keyed by module path, with replace directives applied - mirrors ListModules' decode loop and skip-main/skip-local-replacement semantics exactly, substituting GoVersion for Version throughout.
func (*GoToolchain) Get ¶
func (t *GoToolchain) Get(ctx context.Context, dir, moduleAtVersion string) error
func (*GoToolchain) Linked ¶
func (t *GoToolchain) Linked(ctx context.Context, dir string, target BuildTarget) (Linkage, error)
Linked returns the modules AND packages in the transitive non-test import graph of the target's build patterns - exactly what the linker records in the binary's buildinfo (what APK scanners read), plus the package-level detail needed to check an advisory's vulnerable import paths (a module can be linked via one package while the vulnerable package is not - e.g. x/sys/unix linked, x/sys/windows not). Both sets come from ONE go list walk per target arch, unioned: an import linked on any arch the package is built for ships. Deliberate choices:
- Test-only imports are excluded BY DESIGN (no -test flag): they never ship in the artifact, which is precisely the narrowing this exists for.
- -e with per-package error inspection (see parseDeps): an unmatched //go:embed pattern (generated files absent from the checkout) is tolerated and reported in Linkage.Embeds - it cannot change the import graph. Any other package-load error (on any arch) fails the whole call so the caller fails OPEN (no filtering): the listing may then under-report the import graph and wrongly drop real fixes - the one unacceptable failure mode.
- Replace directives resolve to the replacement path, matching ListModules' identity space (scan findings name the replacement).
- The build environment and tags are targetBuildEnv's and the target's, shared with Compile, so the linked set and the compile gate judge the same files (CGO aside: see targetBuildEnv).
func (*GoToolchain) LinkedStd ¶
func (t *GoToolchain) LinkedStd(ctx context.Context, dir string, target BuildTarget) (map[string]struct{}, error)
LinkedStd returns the set of standard-library import paths in the transitive non-test import graph of the target's build patterns, with the same arch union, walk and error semantics as Linked (inverted filter: standard library packages instead of non-standard ones; tolerated embed gaps are Linked's to report).
func (*GoToolchain) ListModules ¶
func (*GoToolchain) ModuleRequires ¶ added in v0.4.0
func (t *GoToolchain) ModuleRequires(ctx context.Context, dir, modulePath, version string) (map[string]string, error)
ModuleRequires returns the require entries of module@version's own go.mod (`go list -m -json` fetches only the .mod/.info, never the zip), memoized per toolchain: module versions are immutable.
func (*GoToolchain) ModuleVersions ¶ added in v0.4.0
ModuleVersions lists the released versions of module (`go list -m -versions`), in ascending semver order.
func (*GoToolchain) Replace ¶
func (t *GoToolchain) Replace(ctx context.Context, dir, oldPath, newPath, version string) error
Replace applies a replace directive with gobump parity: dropreplace first (so a stale directive can't shadow the new one), then the replacement.
func (*GoToolchain) Replaces ¶
func (t *GoToolchain) Replaces(_ context.Context, dir string) (map[string]ReplaceTarget, error)
Replaces parses dir's go.mod replace directives, keyed by the replaced (old) module path. Local filesystem targets carry an empty Version. Note: modfile.ParseLax skips replace directives entirely, so this must use the full parser.
func (*GoToolchain) RequiredBy ¶ added in v0.4.0
func (t *GoToolchain) RequiredBy(ctx context.Context, dir string) (map[string]map[string]string, error)
RequiredBy returns, from `go mod graph`, each module's requirers in the build list: required module -> requiring module path -> required version. Only edges from a requirer's selected version (the highest version of it in the graph, as MVS picks) count; the main module's own requirements are excluded.
func (*GoToolchain) Requirements ¶
Requirements parses dir's go.mod require entries. No go tool invocation - the file as tidied IS the contract gobump checks deps entries against.
func (*GoToolchain) ResolveQuery ¶ added in v0.4.0
func (t *GoToolchain) ResolveQuery(ctx context.Context, dir, modulePath, query string) (string, error)
ResolveQuery resolves module@query (e.g. a commit hash) to a concrete version (`go list -m`).
type HygieneModule ¶ added in v0.4.0
type HygieneModule struct {
Module string `json:"module" yaml:"module"`
Version string `json:"version" yaml:"version"`
VulnIDs []string `json:"vuln_ids" yaml:"vuln_ids"`
}
HygieneModule is a scanner-hygiene bump: a module linked into the artifact whose advisories' vulnerable packages are not, raised anyway because the bump is free - it changes nothing in the final go.mod but this module's own version, needs no newer Go, compiles and introduces no advisory. Module-level scanners (grype) flag the module version; this is not a security fix and is never counted as one.
type Linkage ¶ added in v0.4.0
type Linkage struct {
Modules, Packages map[string]struct{}
// Embeds are the packages whose //go:embed patterns matched no files in
// the checkout (typically files generated at build time - see
// unmatchedEmbed), sorted and deduplicated across arches. They do not
// affect the import graph, so the sets above are complete regardless.
Embeds []UnmatchedEmbed
}
Linkage is one Toolchain.Linked walk: the linked modules and packages, and the go:embed gaps tolerated while computing them.
type ModrootRequest ¶
type ModrootRequest struct {
Modroot string
Seeds []Candidate
// Baseline maps module path -> effective version in the original
// manifest (replace directives applied). FinalDeps entries that don't
// move a module beyond its baseline are dropped as no-ops.
Baseline map[string]string
// Packages are the build package patterns (relative to Modroot,
// go/build's space-separated with.packages grammar) whose non-test
// import graphs define artifact reachability; empty falls back to ./...
// (over-approximate, never narrower than the artifact).
Packages []string
// Tags are the go/build steps' build tags (toolchaintags plus tags),
// applied by reachability and the compile gate so they judge the files
// the build will compile.
Tags []string
// Arches are the GOARCHes the package is built for (see BuildTarget):
// reachability is their union; the compile gate runs on the first and
// the final set is compiled once more on each of the others.
Arches []string
// Env is the build environment the spec sets (see BuildEnv).
Env BuildEnv
// VulnImports maps each seed advisory ID to its vulnerable imports (from
// the analysis scan's OSV metadata; see
// scan.Vulnerability.VulnerableImports). Seed candidates whose
// advisories all live in unlinked packages are dropped before the
// first apply. Absent/empty entries fail open (see scan.ImportsLinked).
VulnImports map[string][]scan.VulnerableImport
// BaselineVulnIDs are the advisory IDs the analysis scan found in the
// pristine graph. Residual advisories with no ID in this set were
// INTRODUCED by the bump (they apply only to a bumped-to version) and
// are reported and counted separately. Nil disables introduced-vuln
// classification (fail open).
BaselineVulnIDs map[string]struct{}
// Engine is the bump step's apply semantics (see Engine).
Engine Engine
// NoTidy mirrors a `tidy: false` bump step: the engine runs no go mod
// tidy at all.
NoTidy bool
// GoVersion is the build's Go version (bare, e.g. "1.25.9"); omnibump
// lowers the go directive to it, never raises it. "" means the host go
// (EngineOmnibump only).
GoVersion string
// ProbeTidy asks, for a NoTidy EngineOmnibump request, whether the
// pristine module would tidy under omnibump anyway (DoUpdate with tidy
// on and no entries); the answer is ModrootResult.BaselineTidies. It is
// informational only - the simulation itself still honours NoTidy.
ProbeTidy bool
}
ModrootRequest describes one module root to simulate.
type ModrootResult ¶
type ModrootResult struct {
Modroot string `json:"modroot" yaml:"modroot"`
// FinalDeps are the module@version entries proven to apply cleanly, in
// stable (indirect -> direct -> new, alphabetical within group) order,
// filtered to entries that actually move the module graph forward
// relative to the original manifest.
FinalDeps []string `json:"final_deps" yaml:"final_deps"`
// FinalReplaces are the user-authored replace directives ("old=new@version",
// gobump grammar) proven to apply cleanly, raised where an advisory
// demanded it. Note a replace pins its module exactly - future graph
// demands for a higher version are overridden until a later choam run
// re-raises the directive.
FinalReplaces []string `json:"final_replaces,omitempty" yaml:"final_replaces,omitempty"`
// Resolved is the full module graph (module -> version) after the final
// clean apply.
Resolved map[string]string `json:"-" yaml:"-"`
// Linked is the artifact-linked module set of the final clean state
// (see Toolchain.Linked); nil when reachability was unavailable and
// the loop failed open (everything treated as linked). Callers use it
// to tell which analysis-time findings never affected the artifact.
Linked map[string]struct{} `json:"-" yaml:"-"`
// LinkedPackages is the package-level companion of Linked: the import
// paths in the artifact's non-test import graph, for checking an
// advisory's vulnerable import paths (nil when unavailable - fail open).
LinkedPackages map[string]struct{} `json:"-" yaml:"-"`
// LinkageError says why Linked is nil (the last Toolchain.Linked query
// failed and reachability failed open); empty when it is known.
LinkageError string `json:"-" yaml:"-"`
// UnmatchedEmbeds are the go:embed gaps tolerated computing Linked (see
// Linkage.Embeds).
UnmatchedEmbeds []UnmatchedEmbed `json:"-" yaml:"-"`
// GateUnchecked counts the packages the compile gate excluded because
// they already fail to compile in the pristine checkout (primary arch) -
// its blind spot; GateUncheckedFirst is the first one's "pkg: error".
GateUnchecked int `json:"-" yaml:"-"`
GateUncheckedFirst string `json:"-" yaml:"-"`
// Requires are the final tidied go.mod require entries (module ->
// version) - the sustainability contract melange's gobump verifies
// deps entries against. A pin at (or below) its module's Requires
// version is proven; anything above is not.
Requires map[string]string `json:"-" yaml:"-"`
// UnrequiredModules is the degraded module-level reachability signal,
// populated ONLY when Linked is nil (the go list -deps walk failed open)
// and the tidied go.mod's go directive is >= 1.17: modules present in
// Resolved but absent from the tidied require block under any replace
// identity. Such modules provide no package in the main module's import
// closure and cannot be linked into any artifact. nil when unavailable or
// when Linked is authoritative. Module-granular: unlike Linked, membership
// of the COMPLEMENT does not imply linked (test-only deps are required but
// never ship).
UnrequiredModules map[string]struct{} `json:"-" yaml:"-"`
// MaxDepGoVersion is the highest go directive across the final resolved
// build list's non-main modules (bare form, e.g. "1.25"); empty when
// unavailable. The scratch main module's own directive is deliberately
// not consulted: the engine rewrites it (gobump: -go=<host>; omnibump:
// lowered to the build's Go).
MaxDepGoVersion string `json:"max_dep_go_version,omitempty" yaml:"max_dep_go_version,omitempty"`
// StdPackages is the stdlib slice of the final artifact import graph
// (LinkedStd over the converged checkout); nil when unknown.
StdPackages map[string]struct{} `json:"-" yaml:"-"`
// CVEBackedModules lists the modules in FinalDeps whose entries address
// at least one advisory (vs coherence-only pins) - the simulation-time
// equivalent of the checker's SecurityBumpModules.
CVEBackedModules []string `json:"cve_backed_modules,omitempty" yaml:"cve_backed_modules,omitempty"`
// HygieneModules are the FinalDeps entries proposed only for scanner
// hygiene (see HygieneModule), by module path. A module can be in both
// lists: a security fix raised further to clear an unlinked advisory.
HygieneModules []HygieneModule `json:"hygiene_modules,omitempty" yaml:"hygiene_modules,omitempty"`
// HygieneSkipped says why scanner-hygiene bumps were not evaluated
// although candidates existed (compile gate off, package reachability
// unknown, or no convergence); empty otherwise.
HygieneSkipped string `json:"hygiene_skipped,omitempty" yaml:"hygiene_skipped,omitempty"`
// BaselineTidies answers ModrootRequest.ProbeTidy: the pristine module
// tidies under omnibump (so a `tidy: false` may no longer be needed).
// Always false when no probe was requested.
BaselineTidies bool `json:"baseline_tidies,omitempty" yaml:"baseline_tidies,omitempty"`
Residuals []Residual `json:"residuals,omitempty" yaml:"residuals,omitempty"`
Dropped []DroppedCandidate `json:"dropped,omitempty" yaml:"dropped,omitempty"`
// RemainingVulnIDs are the advisory IDs still present in the final
// resolved graph (the union of all residuals' VulnIDs).
RemainingVulnIDs []string `json:"remaining_vuln_ids,omitempty" yaml:"remaining_vuln_ids,omitempty"`
Iterations int `json:"iterations" yaml:"iterations"`
Converged bool `json:"converged" yaml:"converged"`
}
ModrootResult is the outcome of simulating one module root.
func RunLoop ¶
func RunLoop(ctx context.Context, tc Toolchain, sc Scanner, dir string, req ModrootRequest, opts Options) (*ModrootResult, error)
RunLoop applies the seed candidates to the module at dir with the real go toolchain, rescans the resolved graph, raises versions for any residual advisories, and iterates to a fixpoint (see package doc). It returns a ModrootResult even on non-convergence (Converged=false); an error return means the simulation itself could not run (unreadable module, toolchain or scanner failure) and the caller should fall back to unvalidated behavior.
type Options ¶
type Options struct {
// MaxIterations caps the raise-and-rescan fixpoint loop (default 5).
MaxIterations int
// CommandTimeout bounds each go tool invocation (default 2m).
CommandTimeout time.Duration
// Budget bounds the whole per-package simulation (default 10m).
Budget time.Duration
// NoCompile disables the compile gate (see Compiler).
NoCompile bool
}
Options tunes the simulation.
func (Options) WithDefaults ¶
WithDefaults fills unset options.
type ReplaceTarget ¶
ReplaceTarget is the right-hand side of a go.mod replace directive.
type Residual ¶
type Residual struct {
Module string `json:"module" yaml:"module"`
ResolvedVersion string `json:"resolved_version" yaml:"resolved_version"`
FixedVersion string `json:"fixed_version,omitempty" yaml:"fixed_version,omitempty"` // empty: no released fix
VulnIDs []string `json:"vuln_ids,omitempty" yaml:"vuln_ids,omitempty"`
Reason string `json:"reason" yaml:"reason"`
// Introduced marks an advisory absent from the baseline analysis scan -
// it applies only to a version the bump itself moved to, not to the
// original graph. Accounting must not subtract introduced residuals from
// the baseline "found" count.
Introduced bool `json:"introduced,omitempty" yaml:"introduced,omitempty"`
}
Residual is a vulnerability the simulation could not eliminate, with the reason zero-vulnerability state is unreachable for it. Not Go-only: gobump also reuses it for Rust/Java bumps it holds back.
type Rung ¶ added in v0.4.0
Rung is one step of a candidate's fix ladder: a target version and the advisories (with their most severe level) first fixed at it.
type Scanner ¶
type Scanner interface {
ScanPackages(ctx context.Context, pkgs []scan.Package) (*scan.ScanResult, error)
}
Scanner is the vulnerability-scanning seam; *scan.VulnerabilityScanner satisfies it.
type Simulator ¶
type Simulator struct {
// contains filtered or unexported fields
}
Simulator validates per-modroot bump candidate sets against a real checkout of the upstream source (see package doc).
func NewSimulator ¶
NewSimulator wires a Simulator from its dependencies. tempDir may be empty.
func (*Simulator) Simulate ¶
func (s *Simulator) Simulate(ctx context.Context, repoURL, tag, expectedCommit string, reqs []ModrootRequest) (map[string]*ModrootResult, error)
Simulate clones repoURL at tag once and runs the fixpoint loop for each requested modroot against that checkout. A per-modroot failure is recorded on its result slot as an error return; a clone failure fails the whole simulation. expectedCommit, when non-empty, is verified warn-only (melange enforces it at build time).
type Toolchain ¶
type Toolchain interface {
// ModTidy runs `go mod tidy` in dir.
ModTidy(ctx context.Context, dir string) error
// Get runs `go get module@version` in dir.
Get(ctx context.Context, dir, moduleAtVersion string) error
// ListModules returns the resolved module graph (module path -> version)
// from `go list -m all` in dir, with replace directives applied and the
// main module and local-path replacements omitted.
ListModules(ctx context.Context, dir string) (map[string]string, error)
// Requirements returns the require entries (module path -> version) of
// dir's go.mod as written - the set melange's gobump verifies deps
// entries against after its final tidy.
Requirements(ctx context.Context, dir string) (map[string]string, error)
// DepGoVersions returns the go directive of every non-main module in the
// build list (bare form, e.g. "1.24" or "1.24.5"), keyed by module path,
// with replace directives applied (same skip-main semantics as
// ListModules).
DepGoVersions(ctx context.Context, dir string) (map[string]string, error)
// Replace applies a replace directive (gobump parity:
// `go mod edit -dropreplace=<old>` then `-replace=<old>=<new>@<version>`).
Replace(ctx context.Context, dir, oldPath, newPath, version string) error
// Replaces returns dir's go.mod replace directives keyed by Old.Path; a
// local filesystem target is reported with Version == "".
Replaces(ctx context.Context, dir string) (map[string]ReplaceTarget, error)
// Linked returns the module paths AND package import paths in the
// transitive non-test import graph of the target (see BuildTarget),
// unioned over its arches - the modules the linker records in the
// binary's buildinfo, i.e. what actually ships, plus the package-level
// detail for checking advisories' vulnerable import paths. Errors make
// reachability filtering fail OPEN (treat everything as linked).
// Unmatched go:embed patterns are tolerated and reported (see Linkage).
Linked(ctx context.Context, dir string, target BuildTarget) (Linkage, error)
// LinkedStd returns the set of standard-library import paths in the
// transitive non-test import graph of the target (same arch union and
// walk semantics as Linked, inverted filter).
LinkedStd(ctx context.Context, dir string, target BuildTarget) (map[string]struct{}, error)
}
Toolchain abstracts the go tool operations the simulation needs. The real implementation shells out to the local go binary (see NewToolchain); tests inject fakes.
type UnmatchedEmbed ¶ added in v0.4.0
UnmatchedEmbed is a //go:embed pattern of Package that matched no files.