Documentation
¶
Overview ¶
Package lock parses dependency lockfiles into a common representation.
Index ¶
Constants ¶
const SniffBudget = 20
SniffBudget is the default cap PathFilter puts on content-sniff candidates per remote fetch.
Variables ¶
var CIInstanceHost string
CIInstanceHost is the GitLab instance host the files being parsed run their pipelines on, when the caller knows it. The `mr`/`compare`/`queue` modes set it from the MR or compare URL itself (via ghpr.Result.CIHost) around parsing, and reset it to "" afterwards. When set, component pins written as `$CI_SERVER_FQDN/ns/proj/name@1.2.3` resolve against this host and get the full tag-verification treatment; when empty (local files, other forges) they stay claim-free — a directory on disk does not record which instance runs its pipelines.
Functions ¶
func HostOf ¶ added in v0.4.6
HostOf extracts the lowercase host from a URL string; "" when it can't.
func ImageHost ¶ added in v0.5.12
ImageHost returns the registry host an image name resolves from and the repository path on that registry, following the daemon's rules: no host part means Docker Hub, and single-component Hub names live under library/.
func KnownBasenames ¶
func KnownBasenames() []string
KnownBasenames lists every lockfile filename lockvet understands.
func PathFilter ¶ added in v0.5.19
PathFilter returns a fresh predicate for remote diff fetches: every known lockfile path always passes, and up to sniffBudget other YAML files (SniffableYAML) pass for content sniffing — the cap keeps a big refactor PR from ballooning the number of file downloads. The closure is stateful; make one per fetch and do not share across goroutines.
func Sanitize ¶
Sanitize makes untrusted strings (lockfile- or registry-derived) safe to render: it enforces valid UTF-8 and strips control characters (so hostile input cannot smuggle ANSI escape sequences into terminal output).
func SniffableYAML ¶ added in v0.5.19
SniffableYAML reports whether a changed file that no basename or path convention claims is still worth a content sniff as a Kubernetes manifest. GitOps repos keep workload manifests under arbitrary layouts (default/nzbget/nzbget.yaml); the strict top-level apiVersion: + kind: gate inside the parser makes over-matching free, so in diff modes — where the changed-file list is small — every other YAML file qualifies. Helm chart templates stay excluded: their image references are {{ interpolated }}. Directory walks (audit) keep convention-based discovery instead: sniffing every YAML in a large tree would read far more than it finds.
Types ¶
type Ecosystem ¶
type Ecosystem string
Ecosystem is an OSV.dev ecosystem identifier.
const ( NPM Ecosystem = "npm" CratesIO Ecosystem = "crates.io" PyPI Ecosystem = "PyPI" Go Ecosystem = "Go" Packagist Ecosystem = "Packagist" RubyGems Ecosystem = "RubyGems" Hex Ecosystem = "Hex" Pub Ecosystem = "Pub" Maven Ecosystem = "Maven" NuGet Ecosystem = "NuGet" SwiftURL Ecosystem = "SwiftURL" CocoaPods Ecosystem = "CocoaPods" // CRAN and Bioconductor cover R packages from renv.lock. A single // renv.lock mixes both; Bioconductor packages are marked per-package // via File.PkgEco. CRAN Ecosystem = "CRAN" Bioconductor Ecosystem = "Bioconductor" // Nix has no OSV.dev ecosystem and no semver: flake inputs pin git // revisions. lockvet still explains what moved and by how much time. Nix Ecosystem = "Nix" // Conda covers pixi.lock and conda-lock.yml. Conda channels have no // OSV.dev ecosystem or deps.dev coverage, so conda packages get diff, // graph and version-jump treatment only; pip/pypi packages inside the // same lockfile are marked PyPI per-package (File.PkgEco) and get // full vulnerability, age and deprecation data. Conda Ecosystem = "conda" // Terraform providers (.terraform.lock.hcl, Terraform & OpenTofu) // and Helm charts (Chart.lock) have no OSV.dev ecosystem or deps.dev // coverage: changes are diffed, classified and explained without // vulnerability or age claims. Terraform Ecosystem = "Terraform" Helm Ecosystem = "Helm" // Julia covers Manifest.toml (General registry packages and stdlibs; // OSV ecosystem "Julia"). Hackage covers stack.yaml.lock and // cabal.project.freeze/cabal.config (OSV ecosystem "Hackage"). Julia Ecosystem = "Julia" Hackage Ecosystem = "Hackage" // GitHubActions covers pkg:github purls in SBOMs (OSV ecosystem // "GitHub Actions"). GitHubActions Ecosystem = "GitHub Actions" // Conan covers conan.lock (C/C++). "ConanCenter" is a valid OSV // ecosystem (currently near-empty — advisories will surface here // automatically if it fills in); deps.dev has no Conan system, so // internal/conanreg is the metadata layer for these lockfiles. Conan Ecosystem = "ConanCenter" // Bazel covers MODULE.bazel.lock (bzlmod). There is no OSV.dev // ecosystem and no deps.dev system for Bazel modules; internal/bzlreg // reads the Bazel Central Registry directly (yanked versions with // reasons, version lists, source repositories). Bazel Ecosystem = "Bazel" // Zig covers build.zig.zon: Zig has no lockfile beyond it, no central // registry, and no OSV.dev ecosystem. Dependencies pin source archive // URLs plus content hashes, so lockvet explains bumps and checks the // pins themselves (integrity, resolution source) without registry or // vulnerability claims. Zig Ecosystem = "Zig" // Docker covers container base-image pins: Dockerfile / Containerfile // FROM lines and Compose file image: values. There is no OSV.dev // ecosystem for whole images; internal/ocireg verifies tags and // digest pins against the image registries themselves (Docker Hub // ages included). Docker Ecosystem = "Docker" // PreCommit covers .pre-commit-config.yaml `rev:` pins: each entry // names a hook repository (on any git forge — names keep their host) // at an exact tag or commit that pre-commit clones and runs. No // OSV.dev ecosystem; internal/actreg verifies revs against the hook // repositories' real tags. PreCommit Ecosystem = "pre-commit" // Ansible covers Galaxy requirements.yml files: collections // (namespace.name) and classic roles (owner.name), which OSV.dev and // deps.dev both lack entirely. internal/ansreg reads galaxy.ansible.com // itself (the v3 collection index and v1 role index): release ages, // collection deprecation, registry-verified unlisted versions, source // repositories. Roles are marked per-package via File.PkgEco. Ansible Ecosystem = "Ansible Galaxy" AnsibleRole Ecosystem = "Ansible Galaxy role" // GitLabCI covers .gitlab-ci.yml `include: component:` pins (CI/CD // Catalog components) and `include: project:` refs. Component pins // are verified against the component project's real tags // (internal/actreg); project includes stay claim-free — the file // never records which GitLab instance hosts them. Image pulls in the // same file are marked Docker per-package (File.PkgEco). GitLabCI Ecosystem = "GitLab CI" // CircleCI covers .circleci/config.yml `orbs:` pins: reusable CI // packages published to the CircleCI orb registry at exact semver // versions (Renovate's circleci manager bumps them like lockfile // entries, and the orb's code runs in every pipeline). No OSV.dev // ecosystem and no deps.dev system; internal/orbreg asks the orb // registry itself for ages, full version lists (unlisted detection) // and source repositories. Docker executor image pulls in the same // file are marked Docker per-package (File.PkgEco). CircleCI Ecosystem = "CircleCI" // SBOMEco is the file-level ecosystem of an SBOM: a single CycloneDX // or SPDX document mixes ecosystems, so each package carries its own // (File.PkgEco) and this value is only a label / fallback. SBOMEco Ecosystem = "SBOM" // GradleDist is the Gradle distribution itself, pinned by // gradle-wrapper.properties: no OSV ecosystem and no deps.dev // system — internal/gradlereg verifies pins against // services.gradle.org (ages, broken releases, unlisted versions, // official checksums). Maven's wrapper pins, by contrast, are // ordinary Maven coordinates and use the Maven ecosystem. GradleDist Ecosystem = "Gradle" // MiseTool covers asdf/mise toolchain pins (.tool-versions, // mise.toml): each entry pins the exact tool release a project runs. // No OSV ecosystem and no registry — internal/actreg verifies pins // against the tool's own repository tags via a curated tool→repo map // (per-tool tag conventions: go1.23.4, ruby v3_3_4, OTP-27.1). // Backend-prefixed entries (npm:, cargo:, pipx:, gem:, dotnet:, go:) // are real registry packages and are marked per-package via // File.PkgEco; gradle/maven/sbt map onto their existing registries. MiseTool Ecosystem = "mise/asdf" // Vcpkg covers vcpkg.json / vcpkg-configuration.json (C/C++). There // is no OSV ecosystem and no deps.dev system — internal/vcpkgreg is // the metadata layer: baseline commits are verified against the // registry repository itself (existence + commit date via the GitHub // API) and override pins against the registry's append-only versions // database. Vcpkg Ecosystem = "vcpkg" )
func ToolEntryEco ¶ added in v0.6.0
ToolEntryEco maps an asdf/mise tool name to the package name and ecosystem lockvet records it as: backend-prefixed names onto their real registry ecosystems (npm:prettier → npm), gradle/maven/sbt onto the registries that already verify them, everything else onto the "mise/asdf" ecosystem. ok=false means the entry names nothing usable. nonRegistry marks plugin-sourced tools whose pins claim nothing.
func (Ecosystem) HasOSV ¶
HasOSV reports whether the ecosystem can be queried on OSV.dev. This is a whitelist: SBOMs introduce open-ended ecosystem strings (Linux distro packages, unknown purl types) and OSV rejects a whole batch when one query names an invalid ecosystem.
func (Ecosystem) HasSemver ¶
HasSemver reports whether version-jump levels (major/minor/patch) are meaningful for the ecosystem. Nix pins git revisions; Debian/Ubuntu/RPM version strings carry epochs and distro revisions where semver labels would be noise.
func (Ecosystem) PublicRegistryHost ¶ added in v0.4.6
PublicRegistryHost reports whether the host is the ecosystem's canonical public registry (or its official CDN). Used to tell "resolution moved to the public registry" — the dependency-confusion direction — apart from ordinary mirror hops.
type File ¶
type File struct {
Path string
Kind string // e.g. "package-lock.json"
Ecosystem Ecosystem
Packages map[string][]string
// Dependency-graph info, filled in only when the lockfile format
// records it. Deps maps package name -> names it depends on.
// Roots are the project's *direct* dependencies when the lockfile
// itself says so (npm root entry, pnpm importers, Gemfile.lock
// DEPENDENCIES, go.mod without "// indirect", ...).
// RootsKnown distinguishes "no roots recorded" from "empty roots".
Deps map[string][]string
Roots []string
RootsKnown bool
// PkgEco overrides the file-level Ecosystem per package. Only SBOMs
// use it: one CycloneDX/SPDX document mixes npm, PyPI, distro
// packages and more. nil for ordinary lockfiles.
PkgEco map[string]Ecosystem
// NonRegistry marks packages the lockfile itself says do NOT come
// from the format's public registry: workspace members, path and
// git dependencies, alternate indexes. Registry-metadata checks
// (like the unlisted-version flag) skip them.
NonRegistry map[string]bool
// PkgChannel records the registry "channel" a package resolves
// from, when the lockfile names one: the conda channel
// (conda.anaconda.org/<channel>/… artifact URLs) for the conda
// formats, or the chart repository URL for Helm charts. The
// condareg/helmreg layers key their lookups on it. nil elsewhere.
PkgChannel map[string]string
// PkgRepo records the source repository URL the lockfile itself
// resolves a package from, when the format pins repositories rather
// than registry releases (Nix flake inputs). diffx copies it onto
// Change.SourceRepo so compare links work without any registry
// lookup. nil elsewhere.
PkgRepo map[string]string
// PkgYanked records versions the lockfile ITSELF admits are yanked
// from their registry, keyed "name@version" → reason. Only Bazel's
// MODULE.bazel.lock fills it (selectedYankedVersions, written when
// the build passes --allow_yanked_versions): a fully-offline
// deprecation signal. nil elsewhere.
PkgYanked map[string]string
// PinsOnly marks files whose whole job is recording pins for a
// sibling manifest (go.sum next to go.mod): version churn in them
// duplicates the manifest's rows, so diffing surfaces ONLY
// same-version repins — the tampered-hash shape — and nothing else.
PinsOnly bool
// Pins records what the lockfile itself pins for a (package, version)
// beyond the version string: the content hash it expects and the
// registry host it resolves from. Filled only by formats that write
// integrity hashes or resolution URLs (npm, pnpm, yarn, Cargo, uv,
// poetry, Pipfile, requirements --hash, Gemfile.lock remotes).
// name → version → meta.
Pins map[string]map[string]PinMeta
}
File is a parsed lockfile: package name -> set of pinned versions. A lockfile may legitimately contain multiple versions of one package (npm nesting, cargo duplicate majors), hence the set.
type Parser ¶
type Parser struct {
Kind string
Ecosystem Ecosystem
Parse func(p string, data []byte) (*File, error)
}
Parser turns lockfile bytes into a File.
func ByBasename ¶
ByBasename returns the parser responsible for a given file path, or nil. Both forward and backward slashes separate: forge and git paths always use "/", but file mode (`lockvet diff`, MCP vet_files) gets OS paths, and no supported lockfile basename contains a backslash.
func FallbackParser ¶ added in v0.5.0
FallbackParser guesses a parser for a file whose name is not a known lockfile basename (explicit two-file diffs, playground drops): YAML files are tried as CI workflows — strict: at least one `uses:` pin — then as Kubernetes manifests (top-level apiVersion: and kind: required), so a mis-named real lockfile still gets a helpful error — and everything else goes to SBOM content sniffing.
func SBOMParser ¶ added in v0.1.16
func SBOMParser() *Parser
SBOMParser returns the format-sniffing SBOM parser. `lockvet diff` uses it for files whose names aren't recognizable lockfile names.
func SniffParser ¶ added in v0.5.19
func SniffParser() *Parser
SniffParser parses files admitted by SniffableYAML: Kubernetes YAML gets the manifest treatment, anything else parses to an empty file.
type PinMeta ¶ added in v0.4.6
type PinMeta struct {
// Integrity is a space-joined set of content hashes in whatever
// notation the format uses ("sha512-…", "sha256:…", bare hex,
// yarn-berry "10c0/…"). A set because Python lockfiles record one
// hash per artifact (sdist + wheels) for the same version.
Integrity string
// Host is the registry/CDN host the package resolves from, e.g.
// "registry.npmjs.org". Empty when the format doesn't record it.
Host string
}
PinMeta is the integrity/resolution metadata a lockfile records for one pinned (package, version).
Source Files
¶
- actions.go
- ansible.go
- bazel.go
- bazelmodule.go
- buildgradle.go
- circleci.go
- conan.go
- conda.go
- devcontainer.go
- docker.go
- gitlabci.go
- gradle.go
- helmvalues.go
- infra.go
- k8s.go
- lock.go
- miselock.go
- more.go
- more2.go
- more3.go
- npm.go
- others.go
- pom.go
- precommit.go
- pylock.go
- rebar.go
- sbom.go
- sbt.go
- toolversions.go
- vcpkg.go
- versionfile.go
- wrapper.go
- zig.go