Documentation
¶
Overview ¶
Package engine defines what a detector is and runs detectors over a target.
The engine reads each target once and hands every file to each detector that supports that kind of target. Detectors decide verdicts. The engine decides nothing about any vulnerability itself, but it does enforce one rule for every detector: a finding can only be cleared (fixed, inactive or managed) with a reason attached.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNotApplicable = errors.New("not applicable")
ErrNotApplicable is what a Prober returns when the address does not run the service it checks (no SSH server where a TLS check was asked for). The host scan skips that detector, and fails only when every detector did.
Functions ¶
Types ¶
type CVEResult ¶
type CVEResult struct {
ID string `json:"id"`
Verdict Verdict `json:"verdict"`
Reason string `json:"reason,omitempty"`
}
CVEResult is one CVE's verdict inside a finding that covers several.
type CommandOverride ¶ added in v0.5.0
CommandOverride is a deployment's replacement for an image's Entrypoint and Cmd. A nil field keeps the image's own; Compose's entrypoint without command also clears Cmd (ClearCmd).
func (*CommandOverride) Apply ¶ added in v0.5.0
func (o *CommandOverride) Apply(entrypoint, cmd []string) []string
Apply returns the command the container runs.
type Detector ¶
type Detector interface {
Manifest() Manifest
// Load parses and checks the detector's embedded data. An error means the
// data contradicts itself and the engine refuses to run.
Load() error
// NewScan starts a scan of one filesystem or image target.
NewScan(kind TargetKind) Scan
}
Detector is a detector registered with the engine.
type File ¶
type File struct {
Path string
Data []byte
Executable bool
// Member is true for a file found inside an archive or package.
Member bool
}
File is one file handed to a detector. Path is absolute within the target; archive members are "outer!/inner".
type Finding ¶
type Finding struct {
Detector string `json:"detector"`
Kind string `json:"kind"`
Path string `json:"path"`
Line int `json:"line,omitempty"` // for findings in source files
Verdict Verdict `json:"verdict"`
Reason string `json:"reason,omitempty"`
// Message is the exact fact, in words.
Message string `json:"message"`
// Method says how it was found (pem, pkcs12, go-buildinfo, elf-strings...).
Method string `json:"method,omitempty"`
// Facts are short checkable values (hashes, versions), also written to SARIF.
Facts map[string]string `json:"facts,omitempty"`
CVEs []CVEResult `json:"cves,omitempty"`
Fix string `json:"fix,omitempty"`
// Severity, when set, overrides the kind's SARIF level for this finding:
// critical and high are errors, medium and low warnings.
Severity string `json:"severity,omitempty"`
// Details is the detector's own record of the finding, kept whole.
Details any `json:"details,omitempty"`
}
Finding is one proven fact about a target.
type Fixer ¶ added in v0.2.0
type Fixer interface {
Fix(ctx context.Context, dirs []string, opts Options, stdout, stderr io.Writer) int
}
Fixer is implemented by detectors that can rewrite a source tree's references to fixed versions. It prints what it changes (or would, with opts.DryRun) and returns 0 when everything vulnerable was fixed, 1 when some could not be, 2 on error.
type Kind ¶
type Kind struct {
ID string // stable, used in SARIF rule IDs as "<detector>/<kind>"
Title string
// Level is the SARIF level for a vulnerable finding: "error", "warning" or
// "note" (something to look at that is not itself a vulnerability).
Level string
// Description explains the kind in a sentence or two (SARIF fullDescription).
Description string
// SecuritySeverity is the CVSS-style score code scanners sort by ("9.2").
SecuritySeverity string
CWEs []string
}
Kind is one type of finding a detector reports.
type Manifest ¶
type Manifest struct {
ID string
Title string
CVEs []string
Advisories []string // URLs; the first is the SARIF help link
Disclosed string // YYYY-MM-DD
Targets []TargetKind
Kinds []Kind
Tags []string
// Archives asks the engine to unpack archives and packages (jar, whl, deb,
// rpm, compressed files, PyInstaller executables) and hand every member to
// Visit as "outer!/inner".
Archives bool
// Data identifies the detector's knowledge snapshot, for the report.
Data string
}
Manifest describes a detector.
func (Manifest) Supports ¶
func (m Manifest) Supports(kind TargetKind) bool
Supports reports whether the detector runs on kind.
type Options ¶
type Options struct {
MaxFileSize int64
Platform string // image platform, e.g. linux/amd64
AsRoot bool // fs: treat the directory itself as a system root
// SystemRoot (fs) is the directory holding the system the scanned path
// belongs to, "/" by default. Paths are reported inside it, and detectors
// can read its package databases even when only a subdirectory is walked.
SystemRoot string
Timeout time.Duration // host: connect timeout; repo: registry timeout per request
// Repository scans and fixes.
Offline bool // no registry lookups
PublicOnly bool // contact only public registry addresses
Platforms []string // the platforms that are built; empty means every platform
DryRun bool // fix: print changes without writing them
// Override replaces the image's Entrypoint or Cmd for detectors that
// read the container's command (a Compose or Kubernetes setting).
Override *CommandOverride
// Guarded opens registry images through the address-checking
// transport (image references from a source tree), honouring
// PublicOnly, and refuses local archives.
Guarded bool
}
Options control a run.
type Prober ¶
type Prober interface {
// Probe connects to addr and returns the name it resolved to, how many items
// it checked (certificates, responses) and what it found.
Probe(ctx context.Context, addr string, timeout time.Duration) (target string, checked int, findings []Finding, err error)
}
Prober is implemented by detectors that check live network hosts.
type Reader ¶
Reader is implemented by detectors that only need some files read in full. A detector without it receives every file.
type RepoScanner ¶ added in v0.2.0
RepoScanner is implemented by detectors that check what a source tree builds on and deploys (the images its Dockerfiles, Compose, Kubernetes, Helm and CI files name) without building it.
type Scan ¶
type Scan interface {
Visit(f File) []Skip
Finish(ctx TargetContext) []Finding
}
Scan is one detector's work on one target. Visit sees every file the engine reads; a detector keeps the few file contents its verdicts need. Finish runs once, after the last file.
type Target ¶
type Target struct {
Name string `json:"target"`
Type TargetKind `json:"type"`
Digest string `json:"digest,omitempty"`
Platform string `json:"platform,omitempty"`
Root string `json:"root,omitempty"` // filesystem scans: SARIF paths are relative to it
Files int `json:"files_scanned"` // regular files examined
Read int `json:"files_read"` // of those, read in full and checked
Findings []Finding `json:"findings"`
Skipped []source.Skip `json:"skipped"`
// Problems are things a detector could not read fully (a damaged package
// database), which weaken its verdicts without stopping the scan.
Problems []string `json:"problems,omitempty"`
// Annotations are facts about the whole target (its OS release).
Annotations map[string]any `json:"annotations,omitempty"`
// Incomplete says why the scan could not finish (a registry failed).
Incomplete string `json:"incomplete,omitempty"`
Error string `json:"error,omitempty"`
}
Target is one scanned thing: a directory, an image or a server.
func ScanFS ¶
ScanFS scans a directory or file. Paths are reported inside a system root: "/" by default, so location rules see /etc/ssl/... even when scanning /etc/ssl; the directory itself with AsRoot (a host mounted at /host, an extracted image); or SystemRoot when the scanned path lies below it. A walk that starts at its system root skips pseudo-filesystems and container storage.
func ScanHost ¶
ScanHost runs every detector that probes live hosts against addr (host, host:port or a URL).
func ScanImage ¶
ScanImage scans a container image: a registry reference, docker-archive:FILE (from docker save), or rootfs:FILE / rootfs:- (a flat filesystem tar from docker export).
func ScanRepo ¶ added in v0.2.0
ScanRepo runs every repository-capable detector over dir and merges what they found into one target.
type TargetContext ¶
type TargetContext interface {
Kind() TargetKind
// Exists reports whether a regular file at that absolute path was scanned.
Exists(path string) bool
// Env is the image configuration's environment ("KEY=value"), nil otherwise.
Env() []string
// Command is what the image's container runs (Entrypoint then Cmd), nil
// for anything but an image.
Command() []string
// Aliases returns every path that names the same file as p (symlinks, hard
// links), p included.
Aliases(p string) []string
// Links returns every symlink (path -> target as written) and hard link
// (path -> path it names) seen in the walk, for detectors that resolve
// them their own way.
Links() (symlinks, hardlinks map[string]string)
// SystemRoot is the OS path of the system root when a directory below it
// was scanned, so a detector can read the root's package databases. It is
// empty for images and for walks that start at the root.
SystemRoot() string
// Problem records something read only partly (a damaged package database).
Problem(msg string)
// Annotate records a fact about the whole target, such as its OS release.
Annotate(key string, v any)
}
TargetContext is what a detector can ask about the rest of the target when it decides verdicts.
type TargetKind ¶
type TargetKind string
TargetKind is a kind of thing the engine scans.
const ( Filesystem TargetKind = "filesystem" Image TargetKind = "image" Host TargetKind = "host" Repository TargetKind = "repository" )
type Verdict ¶
type Verdict string
Verdict is a finding's outcome.
const ( // Vulnerable: proven exposed. Vulnerable Verdict = "vulnerable" // Unknown: the evidence does not settle it. The reason says what to check. Unknown Verdict = "unknown" // Fixed: proven to have the fix. Fixed Verdict = "fixed" // Inactive: present but provably not used. Inactive Verdict = "inactive" // Managed: another layer decides (an OS keychain, a browser's blocklist). // Listed, not counted. Managed Verdict = "managed" )