Documentation
¶
Overview ¶
Package plugin is the Draugr plugin SDK. It defines the three extension kinds — Scanner, Controller, and Surveyor — the shared value types (Target, Config), and the transports (in-process built-ins, gRPC out-of-process plugins, and declarative tool adapters).
See docs/plugin-api.md.
Index ¶
- func ValidateConfig(schema json.RawMessage, cfg Config) error
- type CacheKey
- type CacheVersioner
- type Config
- type ControlResult
- type Controller
- type ControllerInfo
- type Effect
- type EffectKind
- type HostTarget
- type ImageTarget
- type InfraTarget
- type Prewarmer
- type RepositoryTarget
- type ScanJob
- type Scanner
- type ScannerInfo
- type Scope
- type Summary
- type SurveyScope
- type Surveyor
- type SurveyorInfo
- type Target
- type TargetKind
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ValidateConfig ¶ added in v0.29.0
func ValidateConfig(schema json.RawMessage, cfg Config) error
ValidateConfig checks cfg against a scanner's declared JSON Schema (ScannerInfo.ConfigSchema). An empty schema accepts any config. Validation is fail-fast: the first violation is returned as a human-readable error naming the offending option, so a mistyped Saga key or wrong value type is reported before any scan runs.
It supports the subset of JSON Schema Draugr emits for scanner config: object schemas with "properties", "required", and "additionalProperties"; and scalar/array leaves with "type" (string, boolean, integer, number, array, object) and "enum". This is deliberately narrow — scanner schemas are authored in-repo (a typed allowlist of options), not accepted from users — while remaining valid JSON Schema that external tooling and the config wizard can consume.
Types ¶
type CacheKey ¶
type CacheKey string
CacheKey uniquely identifies the inputs of a scan, so an unchanged target is never re-scanned. See ComputeCacheKey.
func ComputeCacheKey ¶
ComputeCacheKey derives a stable cache key from the scan inputs: the scanner name and version, the target kind and identity, and the effective config. It is deterministic and independent of config map ordering.
type CacheVersioner ¶ added in v0.10.0
CacheVersioner is an optional interface a Scanner may implement to contribute a tool/data version to its cache key — so that an update to the underlying tool or its data (e.g. a vulnerability database) invalidates cached results, not just the TTL. The engine calls CacheVersion only when caching is enabled, and folds a non-empty return into the cache key. It is resolved lazily; implementations should memoize any probe and return "" when the version can't be determined (the key then falls back to Info().Version). Unlike Info(), CacheVersion may perform I/O.
type Config ¶
Config is scanner/plugin configuration. It is validated against the plugin's declared JSON Schema (ScannerInfo.ConfigSchema) before use.
type ControlResult ¶
ControlResult is a control's outcome after aggregation.
type Controller ¶
type Controller interface {
Info() ControllerInfo
// Plan expands a component (or the project, when comp is nil) into scan jobs.
Plan(model saga.Model, comp *saga.Component) ([]ScanJob, error)
// Aggregate merges and deduplicates this control's scanner outputs into one result.
Aggregate(results []sarif.Report) (ControlResult, error)
}
Controller orchestrates one or more scanners for a single security control. It plans the work for a component (or the project) and aggregates the scanners' results.
type ControllerInfo ¶
type ControllerInfo struct {
// Name is the control name, e.g. "images", "sast", "opensource".
Name string
Scope Scope
// Summary is a one-line description of what the control does, for `draugr controls`.
Summary string
// DefaultScanners lists the scanner(s) the control runs by default. Some controls accept
// additional opt-in scanners (see controllers.<name>.scanners); those are discovered from
// the registry rather than listed here.
DefaultScanners []string
}
ControllerInfo describes a controller.
type Effect ¶ added in v0.45.0
type Effect struct {
Kind EffectKind
// Detail is one line, addressed to whoever has to approve it: what will happen, in terms of
// the target rather than the implementation.
Detail string
}
Effect is something a scanner does to a target beyond reading it.
type EffectKind ¶ added in v0.45.0
type EffectKind string
EffectKind categorises what a scanner does beyond reading its target.
const ( // EffectNetwork sends traffic to the target rather than reading an artifact. Probing a host // you do not own is unlawful in many jurisdictions, which is why it is worth stating even // though it changes nothing. EffectNetwork EffectKind = "network" // EffectMutate creates or changes something that outlives the scan. EffectMutate EffectKind = "mutate" // EffectPrivilege needs access beyond what reading the target requires. EffectPrivilege EffectKind = "privilege" )
The kinds of effect a scanner can declare.
The distinction that matters is the **target**. Fetching a vulnerability database from a vendor is a network call, but it is not a consequence for the thing being scanned, and it is not an effect. Sending traffic to the customer's endpoint is.
func (EffectKind) RequiresConsent ¶ added in v0.45.0
func (k EffectKind) RequiresConsent() bool
RequiresConsent reports whether an effect must be acknowledged before a scanner runs.
Mutating a target or asking for elevated access is a decision someone should make on purpose. Network traffic is declared and recorded but not gated: the controls that send it exist to send it, and demanding consent per run for the thing the control is *for* trains people to agree without reading. The obligation it carries — that you are entitled to probe the host — is set out in the scope and disclaimer.
type HostTarget ¶
HostTarget is a running endpoint. Type is "browser" or "api".
type ImageTarget ¶
ImageTarget is a container image. Identity prefers the immutable digest.
func (ImageTarget) Identity ¶
func (t ImageTarget) Identity() string
Identity returns the digest when set, otherwise the ref. Keying on the immutable digest makes the cache content-addressed: a rebuilt image under the same tag has a new digest and so a new key, while an unchanged image reuses its result.
func (ImageTarget) PinnedRef ¶ added in v0.11.0
func (t ImageTarget) PinnedRef() string
PinnedRef returns the reference a scanner should actually pull: the ref pinned to the digest (e.g. "repo:tag@sha256:…") when a digest is known, so the bytes scanned match the digest the result is cached under. The tag is kept for readable scanner output. Falls back to the ref alone (or a repo-less digest) when either part is missing or the ref is already digest-pinned.
type InfraTarget ¶
type InfraTarget struct {
Platform string
Ref string
// Namespaces narrows the audit to part of the cluster. Empty means all of it.
Namespaces []string
}
InfraTarget is an infrastructure surface (e.g. a Kubernetes cluster). Platform is the kind of infrastructure (e.g. "kubernetes"); Ref names the concrete instance.
func (InfraTarget) Identity ¶
func (t InfraTarget) Identity() string
Identity returns the platform and ref, e.g. "kubernetes/prod". Identity names what was assessed, and therefore what a cached result may be reused for.
The namespaces belong in it: two components auditing the same cluster with different scopes are asking different questions, and a cache keyed on the cluster alone would answer the second with the first one's findings.
type Prewarmer ¶ added in v0.11.0
Prewarmer is an optional interface a Scanner may implement to warm shared, expensive state once before a run's concurrent fan-out — e.g. downloading a vulnerability database — so that many parallel scans don't each cold-start it (a thundering herd). The engine calls Prewarm once per distinct scanner, before scans start; a returned error is best-effort (logged, not fatal — the scan will surface any real problem). Implementations should memoize.
type RepositoryTarget ¶
RepositoryTarget is a source repository at a revision, optionally scoped to paths.
func (RepositoryTarget) Identity ¶
func (t RepositoryTarget) Identity() string
Identity returns the URL and revision, e.g. "https://git/x@1.0".
func (RepositoryTarget) Kind ¶
func (RepositoryTarget) Kind() TargetKind
Kind returns TargetRepository.
type Scanner ¶
type Scanner interface {
Info() ScannerInfo
Scan(ctx context.Context, target Target, cfg Config) (sarif.Report, error)
}
Scanner wraps a single security tool and runs one kind of scan. It is the atomic unit of work. Implementations must be side-effect-free with respect to the target and must honor ctx cancellation. Output is normalized to SARIF.
type ScannerInfo ¶
type ScannerInfo struct {
// Name is the scanner identifier, e.g. "trivy".
Name string
// Binary is the external executable the scanner shells out to, e.g. "trivy". Empty for
// scanners that need no external tool. Used by `draugr doctor` to check availability.
Binary string
// AlsoRequires names further executables that must be present for the scanner to work.
//
// Some tools shell out in turn: kube-bench's CIS policy checks are scripts that invoke
// kubectl, so a machine with kube-bench and no kubectl fails at scan time rather than at
// `draugr doctor`. Declaring the whole requirement is what lets the preflight check be
// worth running.
AlsoRequires []string
// Version is the scanner/plugin version; it participates in the cache key.
Version string
// Controls are the security controls this scanner can serve, e.g. ["images"].
Controls []string
// TargetKinds are the target kinds this scanner accepts.
TargetKinds []TargetKind
// ConfigSchema is a JSON Schema for Config; it drives validation and the config wizard.
ConfigSchema json.RawMessage
// Effects declare what this scanner does to a target beyond reading it. Empty — the common
// case — means it reads an artifact and nothing else.
//
// Draugr surfaces these before a scan runs and records them in the report afterwards, and
// refuses to run a scanner whose effects have not been acknowledged. A scanner that omits an
// effect it has is worse than one that overstates: the point is that nothing consequential
// happens to a target without the operator having agreed to it.
Effects []Effect
}
ScannerInfo describes a scanner and its capabilities.
type Scope ¶
type Scope string
Scope declares whether a controller operates on the whole project or per component.
type SurveyScope ¶
SurveyScope tells a surveyor where to look, e.g. a kube context + namespace, a GitHub org, or an ADO project.
type Surveyor ¶
type Surveyor interface {
Info() SurveyorInfo
Survey(ctx context.Context, scope SurveyScope) (saga.Fragment, error)
}
Surveyor discovers an application's surface and returns a Saga fragment, so the descriptor can write itself. Implementations must honor ctx cancellation.
type SurveyorInfo ¶
type SurveyorInfo struct {
// Name is the surveyor identifier, e.g. "k8s-images", "github-org-repos".
Name string
// Provides are the target kinds this surveyor can discover.
Provides []TargetKind
// ConfigSchema is a JSON Schema for the scope Config.
ConfigSchema json.RawMessage
}
SurveyorInfo describes a surveyor.
type Target ¶
type Target interface {
Kind() TargetKind
Identity() string
}
Target is something a scanner can act on. Identity returns a stable string that uniquely identifies the target for cache keying (a commit, an image digest, a normalized endpoint) — two targets with the same Identity are considered the same scan input.
type TargetKind ¶
type TargetKind string
TargetKind identifies the sort of surface a scanner acts on.
const ( TargetRepository TargetKind = "repository" TargetImage TargetKind = "image" TargetHost TargetKind = "host" TargetInfra TargetKind = "infrastructure" )
The kinds of target a scanner may accept.