Documentation
¶
Overview ¶
Package diffx computes the semantic difference between two lockfiles.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Change ¶
type Change struct {
Name string `json:"name"`
Ecosystem string `json:"ecosystem"`
Kind Kind `json:"kind"`
Old []string `json:"old,omitempty"`
New []string `json:"new,omitempty"`
Level vers.Level `json:"-"`
LevelString string `json:"level,omitempty"`
// Why the package is in the tree, when the lockfile records its
// dependency graph. Origin is "direct", "transitive" or "" (unknown).
// Via is the chain of dependencies from a direct dependency down to
// (but excluding) this package, e.g. ["react-scripts", "webpack"].
Origin string `json:"origin,omitempty"`
Via []string `json:"via,omitempty"`
// Filled in by the OSV layer.
IntroducedVulns []Vuln `json:"introduced_vulns,omitempty"` // affect new, not old
FixedVulns []Vuln `json:"fixed_vulns,omitempty"` // affected old, not new
ExistingVulns []Vuln `json:"existing_vulns,omitempty"` // affect both
// Filled in by the deps.dev layer (registry metadata for the
// version this change introduces).
PublishedAt string `json:"published_at,omitempty"` // RFC3339, UTC
AgeDays int `json:"age_days,omitempty"`
Fresh bool `json:"fresh,omitempty"` // younger than the cooldown window
Deprecated bool `json:"deprecated,omitempty"`
DeprecatedReason string `json:"deprecated_reason,omitempty"`
// Unlisted: at least one incoming version is missing from the
// registry metadata (deps.dev) even though other versions of the
// same package are listed. That is what an unpublished or deleted
// release looks like — registries pull malicious versions, so a
// lockfile that still pins one is a red flag. (A release published
// minutes ago may also not be indexed yet.)
Unlisted bool `json:"unlisted,omitempty"`
UnlistedVersions []string `json:"unlisted_versions,omitempty"`
// ScriptsAdded: the outgoing version ran no install scripts, the
// incoming one does (npm only; preinstall/install/postinstall, per
// the registry's hasInstallScript). Adding execution-on-install in
// an ordinary-looking bump is how several real npm supply-chain
// attacks shipped their payload. ScriptedVersions lists the
// incoming versions that carry scripts.
ScriptsAdded bool `json:"install_scripts_added,omitempty"`
ScriptedVersions []string `json:"scripted_versions,omitempty"`
// ProvenanceDropped: every known outgoing version of this package
// was published with sigstore provenance attestations, the incoming
// one wasn't (npm, PyPI, crates.io). Legitimate CI keeps attesting; a stolen
// publish token can publish but cannot make the pipeline attest,
// so a silent drop is worth a look before trusting the release.
// UnattestedVersions lists the incoming versions without provenance.
ProvenanceDropped bool `json:"provenance_dropped,omitempty"`
UnattestedVersions []string `json:"unattested_versions,omitempty"`
// IntegrityChanged: a version pinned on BOTH sides now records a
// different content hash — the lockfile expects different bytes for
// the same version string. Registries never change a published
// artifact, so outside a registry migration this means the tarball
// was replaced (registry-side tampering, a hijacked mirror, or a
// hand-edited lockfile). Compared per hash algorithm: a lockfile
// upgrade that switches sha1 → sha512 is NOT flagged.
IntegrityChanged bool `json:"integrity_changed,omitempty"`
IntegrityVersions []string `json:"integrity_changed_versions,omitempty"`
// DigestChanged: a container image tag pinned on both sides now pins
// a different digest. Unlike registry tarballs, image tags MOVE by
// design (base images are rebuilt for security fixes), so a digest
// bump under an unchanged tag is routine — the row is shown
// neutrally, and the ocireg layer verifies the new digest against
// the registry instead: DigestVerified means the registry serves
// exactly this digest for the tag today. A pin the registry does NOT
// serve for the tag surfaces via TagMismatch; a digest the registry
// has never seen at all surfaces via Unlisted. Conan recipe
// revisions ride the same lane ("rrev:" pins): recipes are
// re-exported for old versions routinely, so a same-version rrev
// change is shown neutrally as a recipe-revision update.
DigestChanged bool `json:"digest_changed,omitempty"`
DigestVerified bool `json:"digest_verified,omitempty"`
OldDigest string `json:"old_digest,omitempty"`
NewDigest string `json:"new_digest,omitempty"`
// OldHost/NewHost: the registry hosts the package resolved from on
// each side, when the lockfile records them and they differ.
// RegistryMoved marks the dependency-confusion direction: the
// resolution moved FROM a private/alternate host TO the ecosystem's
// public registry — exactly what an attacker's higher-versioned
// public squat of an internal package name looks like. Whole-file
// mirror migrations (many packages moving between the same two
// hosts) are recognized and not flagged.
OldHost string `json:"old_host,omitempty"`
NewHost string `json:"new_host,omitempty"`
RegistryMoved bool `json:"registry_moved,omitempty"`
// ResolvedRefs maps a pinned ref to the release tag it equals, when
// the upstream repository's tags were checked (GitHub Actions pins: a
// commit SHA that is exactly the tag vX.Y.Z, or a floating major tag
// v4 currently pointing at vX.Y.Z). Display and vulnerability
// matching use the resolved version where available.
ResolvedRefs map[string]string `json:"resolved_refs,omitempty"`
// TyposquatOf: this package just entered the tree, its release is
// young (or of unknown age), and its name is at most one edit away
// from the named popular package on the same registry — the shape of
// a typosquatting attack. Set by the squat layer; empty means clean.
TyposquatOf string `json:"typosquat_of,omitempty"`
// Channel: the registry channel the package resolves from, when
// the lockfile records one — the conda channel for conda formats
// (conda.anaconda.org/<ch>/…), the chart repository URL for Helm.
// The condareg/helmreg layers key their lookups on it.
Channel string `json:"-"`
// NewPins: what the new lockfile itself pins for each incoming
// version (a content hash, or "commit:<sha>" for git-resolved
// formats), for layers that verify pins against the outside world.
// Not serialized — pin-to-pin differences already surface as
// IntegrityChanged.
NewPins map[string]string `json:"-"`
// TagMismatch: the lockfile pins a version at a commit that is NOT
// what the upstream repository's tag for that version points at
// today (SwiftPM: Package.resolved records both the version and the
// resolved commit). Released tags are supposed to be immutable —
// either the tag has been re-pointed since resolution (how the
// tj-actions attack shipped), or the lockfile was edited to fetch a
// different commit while displaying an innocent version.
TagMismatch bool `json:"tag_mismatch,omitempty"`
TagMismatches []string `json:"tag_mismatches,omitempty"`
// NonRegistry: the lockfile says this package doesn't come from the
// public registry (workspace member, path/git dependency). Suppresses
// the unlisted flag; not serialized.
NonRegistry bool `json:"-"`
// License strings as the registry reports them (per side), and
// whether the bump changes the license. Only set when deps.dev
// knows both sides.
OldLicense string `json:"old_license,omitempty"`
NewLicense string `json:"new_license,omitempty"`
LicenseChanged bool `json:"license_changed,omitempty"`
// Filled in by the changelog layer: the upstream repository and, when
// both versions match real tags there, links that are verified not
// to 404.
SourceRepo string `json:"source_repo,omitempty"`
CompareURL string `json:"compare_url,omitempty"` // upstream diff old → new
ReleaseURL string `json:"release_url,omitempty"` // release/tag page for new
// Filled in by the release-notes layer (opt-in via -changelogs):
// upstream release notes covering the versions this bump pulls in,
// newest first.
ReleaseNotes []ReleaseNote `json:"release_notes,omitempty"`
// Findings acknowledged via a .lockvetignore file. Ignored lists the
// suppressed finding kinds ("fresh", "major", …); IgnoredVulns holds
// advisories moved out of IntroducedVulns. Both stay visible in JSON
// and as a dim marker in reports, but no longer count toward the
// summary or -fail-on gates.
Ignored []string `json:"ignored,omitempty"`
IgnoredVulns []Vuln `json:"ignored_vulns,omitempty"`
}
Change describes what happened to one package.
func (*Change) HasIgnored ¶ added in v0.4.1
HasIgnored reports whether the finding kind was suppressed on this change via a .lockvetignore rule.
type FileDiff ¶
type FileDiff struct {
Path string `json:"path"`
Kind string `json:"lockfile"`
Ecosystem string `json:"ecosystem"`
Changes []Change `json:"changes"`
}
FileDiff is the set of changes within one lockfile.
func Filter ¶
Filter keeps only the changes whose package name — or any package in their via chain — matches one of the comma-separated glob patterns. Matching is case-insensitive; '*' matches any run of characters (including '/', so "*sys*" matches "golang.org/x/sys") and '?' matches one character. Matching via chains means "-only jiff" also shows every transitive change that jiff dragged in.
type ReleaseNote ¶ added in v0.3.3
type ReleaseNote struct {
Tag string `json:"tag"`
Title string `json:"title,omitempty"`
URL string `json:"url"`
Excerpt string `json:"excerpt,omitempty"`
}
ReleaseNote is one upstream release's notes, excerpted.
type Summary ¶
type Summary struct {
Total, Major, Minor, Patch, Added, Removed, Downgraded int
VulnsIntroduced, VulnsFixed, VulnsExisting int
Fresh, Deprecated, LicenseChanged, Unlisted int
ScriptsAdded int // npm bumps that newly run install scripts
Typosquats int // young additions confusable with a popular package
ProvenanceDropped int // npm bumps that silently stop attesting provenance
IntegrityChanged int // pins whose recorded content hash changed for an unchanged version
TagMismatch int // pins whose commit is not what the upstream tag points at
RegistryMoved int // resolutions moved from a private host to the public registry
Direct, Transitive int // 0/0 when the formats record no graph
Ignored int // findings acknowledged via .lockvetignore
}
Summary aggregates counts across file diffs.
type Vuln ¶
type Vuln struct {
ID string `json:"id"`
Summary string `json:"summary,omitempty"`
Severity string `json:"severity,omitempty"`
URL string `json:"url,omitempty"`
// FixedIn is the smallest version that clears this advisory for the
// pinned version(s), read from the advisory's own ranges. Empty when
// no fix has been released or the ranges don't say.
FixedIn string `json:"fixed_in,omitempty"`
}
Vuln is a known vulnerability reference.