Documentation
¶
Overview ¶
Package fix proposes and applies deterministic one-line fixes for image references that carry a PHP release vulnerable to CVE-2026-109618 and CVE-2026-109619.
A fix is proposed only when it is verified: the replacement reference exists in the registry, holds images (not an empty index), covers every platform the original covers, and every one of those platforms proves a PHP release with the fix. The edited file is then re-scanned in memory: the reference must resolve to exactly the replacement, every other reference the edit changes (an ARG shared by several FROMs) must verify the same way, and nothing else in the file may change. Anything short of that is a Decline with the reason.
The rules are conservative:
- docker.io/library/php with an exact X.Y.Z tag on a branch with a fix: the same tag with the fixed release (8.3.20-fpm-alpine3.21 becomes 8.3.36-fpm-alpine3.21), if the registry lists that tag. The OS release suffix is never changed.
- A floating tag (8.3-fpm, 8-apache, latest, fpm-alpine) moves by itself when Docker Hub publishes the fixed release; nothing to edit, unless the reference is pinned by digest, in which case the digest is replaced with the tag's current one once that is verified.
- An exact tag pinned by digest gets both the fixed tag and its digest.
- A bare digest (php@sha256:...) does not say which variant it is: decline.
- Branches before 8.2 have no upstream fix: moving to a supported branch changes the PHP minor version and is left to the developer.
- Images built on PHP (wordpress, drupal, ...) and other repositories are only bumped when pinned by digest under a tag that now verifies fixed.
The edit lands where the changing characters were written: the FROM line, the ARG default that feeds it, the Compose default or .env value, the Helm values tag line. The rest of the line, and the rest of the file, are kept byte for byte.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
Apply writes changes. Every change is checked first: its file must be a regular file inside root (no symbolic links on the way), and its line must still read exactly Old. If any check fails nothing is written. Each file is then replaced atomically (a temporary file in the same directory, synced and renamed over it), keeping its permissions and every other byte.
Types ¶
type Change ¶
type Change struct {
// File is the repository-relative path of the edited file.
File string `json:"file"`
// Line is the 1-based line edited.
Line int `json:"line"`
// Old is the line as it is now and New the line after the fix, both
// without the line terminator (which Apply keeps).
Old string `json:"old"`
New string `json:"new"`
// Reason explains the fix and its verification.
Reason string `json:"reason"`
// Before and After are the image reference before and after the fix.
Before string `json:"before"`
After string `json:"after"`
// Ref is the reference the fix is for (its File may differ from File when
// the edit is to an ARG, a .env file, ...).
Ref refs.Ref `json:"ref"`
// Verified lists the platforms checked in the replacement:
// "linux/amd64: PHP 8.3.36".
Verified []string `json:"verified"`
// Also lists other references the same edit changes, each verified.
Also []string `json:"also,omitempty"`
}
Change is one verified line edit.
func Plan ¶
func Plan(ctx context.Context, root string, ref refs.Ref, res *registry.Resolution, resolver Resolver) (*Change, error)
Plan proposes a verified fix for a vulnerable reference. res is the reference's resolution (registry.Resolver.Resolve(ref.Image)). The error is a *Decline when there is no fix to propose, or another error when the repository could not be read.
type Decline ¶
type Decline struct {
Code DeclineCode `json:"code"`
Reason string `json:"reason"`
}
Decline is returned by Plan when no fix is proposed.
type DeclineCode ¶
type DeclineCode string
DeclineCode classifies why no fix is proposed.
const ( DeclineNotVulnerable DeclineCode = "not-vulnerable" DeclineUnresolved DeclineCode = "unresolved" DeclineOffline DeclineCode = "offline" DeclineEndOfLife DeclineCode = "end-of-life-branch" DeclineMixedBranches DeclineCode = "mixed-branches" DeclineFloatingTag DeclineCode = "floating-tag" DeclineBareDigest DeclineCode = "bare-digest" DeclineDerivedImage DeclineCode = "derived-image" DeclineUnsupportedTag DeclineCode = "unsupported-tag" DeclineSuffixNotPublished DeclineCode = "suffix-not-published" DeclineNotPublished DeclineCode = "not-published-yet" DeclinePlatformsMissing DeclineCode = "platforms-missing" DeclineNotFixed DeclineCode = "replacement-not-fixed" DeclineNotEditable DeclineCode = "not-editable" DeclineMultipleLines DeclineCode = "multiple-lines" DeclineStale DeclineCode = "file-changed" DeclineVerification DeclineCode = "verification-failed" // DeclineBaseOSChanged: the replacement is built on another OS release // (php:8.3.20-fpm on Debian bookworm, php:8.3.36-fpm on trixie). The // Reason names the explicit tag that keeps the release. DeclineBaseOSChanged DeclineCode = "base-os-changed" // DeclineBaseOSUnknown: the OS release of the original or the replacement // cannot be determined and the tag does not name it. DeclineBaseOSUnknown DeclineCode = "base-os-unknown" )