Documentation
¶
Overview ¶
Package vex reads OpenVEX documents from a VEX Hub repository and matches their statements against findings.
A VEX hub is a vendor saying, in public and in a machine-readable form, "we looked at this CVE in this product and here is what we concluded". vexscan reaches its own conclusions from local evidence; a hub says whether someone with more context has already answered the same question. The two are kept apart on purpose -- nothing in this package sets a status.
The shape of a hub, as published:
vex-repository.json the repository's own metadata index.json product purl -> path of that product's document pkg/<type>/<path>/scan.openvex.json
The unit of a document is a statement about a (vulnerability, product) pair, where the product is the shipped artifact -- a container image, a Go main module -- and the optional subcomponents are the dependencies inside it that the vulnerability is actually filed against. A vexscan finding is one of those subcomponents, so matching means agreeing on the product, the vulnerability and the subcomponent all three.
Index ¶
Constants ¶
const ( StatusNotAffected = "not_affected" StatusAffected = "affected" StatusFixed = "fixed" StatusUnderInvestigation = "under_investigation" )
VEX statuses, as OpenVEX defines them.
Variables ¶
This section is empty.
Functions ¶
func Exculpatory ¶
Exculpatory reports whether a status says the reader has nothing to do.
not_affected and fixed answer the question; affected and under_investigation are the vendor agreeing there is one, or admitting they do not know yet, and neither should make a finding quieter.
func GoProduct ¶
GoProduct turns a Go main module path into its product purl.
No escaping is applied: a hub's golang keys are written as the plain module path, upper-case letters and all (pkg:golang/github.com/Altinity/...), which is also exactly what build info reports.
func ImageProduct ¶
ImageProduct turns a container image reference into the product purl a VEX hub indexes it under.
rancher/hardened-kubernetes:v1.30.1 -> pkg:oci/hardened-kubernetes?repository_url=index.docker.io/rancher/hardened-kubernetes
The normalization is Docker's, because that is what the hub's keys were written with: a bare name is under library/, a missing registry is index.docker.io, and docker.io is spelled index.docker.io. The tag and digest are dropped -- a hub keys a repository, not a release, and the statements inside carry their own timestamps.
Returns "" for a reference it cannot make sense of, which the caller should treat as "no product to look up" rather than as an error.
Types ¶
type Hub ¶
type Hub struct {
// URL is the hub as the user named it, for messages and for the record in
// the JSON output.
URL string
HTTP *http.Client
// contains filtered or unexported fields
}
Hub is one opened VEX repository: its index, plus whatever documents have been fetched out of it so far.
A Hub is safe for concurrent use. Lookup is the only method, and two goroutines asking for the same product will each fetch it -- the cache deduplicates the answer, not the request, which is the right trade for a handful of products per scan.
func Open ¶
Open fetches a hub's index.
The location may be a GitHub repository URL, a raw base URL, or a local directory. A GitHub URL is rewritten to raw.githubusercontent.com, which is what makes `--vexhub https://github.com/rancher/vexhub` -- the thing a reader would paste -- work without them having to know the raw form.
A deliberate deviation from the VEX Repository spec: the spec distributes a hub as the tarball named in vex-repository.json. rancher/vexhub's index is 208 KB and a scan needs one or two documents out of it, against a ~30 MB archive. Fetching the index and then only the documents actually wanted is dramatically cheaper. The tarball transport is worth adding if a hub turns up that does not serve its files individually; none does today.
func (*Hub) Location ¶ added in v0.8.0
Location is where the hub files a product's document, relative to its root, matching the product the same way Lookup does.
func (*Hub) Lookup ¶
Lookup returns the document covering a product. The bool is false when the hub has no document for it, which is the ordinary case and not an error -- a hub covers one vendor's artifacts, and most scans are of something else.
func (*Hub) Raw ¶ added in v0.8.0
Raw reads one file out of the hub verbatim, by a location the index gave.
It exists for the writer in internal/vexpr, which merges into an existing document and must reproduce every field OpenVEX allows -- including the ones Doc does not model. Lookup's parsed form cannot do that, so this returns the bytes. A file the hub does not have is ok=false rather than an error: the caller's next step for "no document yet" is to start one.
type Product ¶
Product is one artifact a statement covers, and optionally the components inside it the vulnerability belongs to.
An entry with no subcomponents covers the whole product. That is a stronger claim than a subcomponent-scoped one and is treated as such when two statements compete.
type Statement ¶
type Statement struct {
// Vulnerability is the id the vendor filed the statement under. It is not
// always a CVE: a SUSE-keyed hub writes advisory ids like
// "SUSE-RU-2026:1228-1", which OSV also publishes, so they still match.
Vulnerability string
Aliases []string
Products []Product
Status string
Justification string
// ImpactStatement is the vendor's own sentence explaining the conclusion,
// and is usually the single most useful field in the document.
ImpactStatement string
ActionStatement string
Timestamp string
}
Statement is one claim about one vulnerability in one product.
func Match ¶
Match finds the statement in d that speaks to a finding: one naming any of ids as its vulnerability, under the given product, covering subPURL.
It returns the statement and a note describing every spelling disagreement it had to tolerate to get there, or (nil, "") if nothing matched. The note is not an aside -- it is the audit trail for a deliberately loose comparison, and belongs in the evidence the caller records.
type StatusError ¶
StatusError is a non-200 answer from a hub.
func (*StatusError) Error ¶
func (e *StatusError) Error() string
func (*StatusError) Retryable ¶
func (e *StatusError) Retryable() bool
Retryable reports whether repeating the request could plausibly succeed. A 404 is the common case here -- a hub that simply has no document for this product -- and retrying it three times only makes the scan slower.