engine

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: BSD-3-Clause Imports: 14 Imported by: 0

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

This section is empty.

Functions

func Register

func Register(d Detector)

Register adds a detector. It panics on a duplicate ID, which is a build error.

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 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.

func Detectors

func Detectors() []Detector

Detectors returns every registered detector, sorted by ID.

func Select

func Select(ids []string) ([]Detector, error)

Select returns the detectors with the given IDs, or all of them when ids is empty, and loads each one.

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
}

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

type Reader interface {
	Wants(path string, head []byte, executable bool) bool
}

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

type RepoScanner interface {
	ScanRepo(ctx context.Context, dir string, opts Options) *Target
}

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 Skip

type Skip struct {
	Path   string
	Reason string
	// contains filtered or unexported fields
}

Skip records something a detector could not check, and why.

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

func ScanFS(dets []Detector, root string, opts Options) *Target

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

func ScanHost(dets []Detector, addr string, opts Options) *Target

ScanHost runs every detector that probes live hosts against addr (host, host:port or a URL).

func ScanImage

func ScanImage(dets []Detector, ref string, opts Options) *Target

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

func ScanRepo(dets []Detector, dir string, opts Options) *Target

ScanRepo runs every repository-capable detector over dir and merges what they found into one target.

func (*Target) Count

func (t *Target) Count(v Verdict) int

Count returns how many findings have verdict v.

func (*Target) Gaps

func (t *Target) Gaps() int

Gaps counts skipped files that are real coverage gaps: not deliberate exclusions (pseudo-filesystems, container storage) and not test fixtures, which are malformed on purpose.

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
	// 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"
)

func (Verdict) Cleared

func (v Verdict) Cleared() bool

Cleared reports whether v takes a finding off the list of things to act on.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL