Documentation
¶
Overview ¶
Package policy is a deterministic, dependency-free policy-as-code engine for docker-security. It compiles a versioned policy document (rules written in a small boolean expression language) and evaluates it against a unified Input — scan findings, image identity, workload security context, and supply-chain attestation state — to produce an allow / warn / deny Decision.
The engine is the shared core behind two enforcement points: a CI gate (the internal/modules/policy Module, shift-left over scan results) and a Kubernetes ValidatingWebhook (internal/admission). Both call the same Evaluate, so a rule behaves identically in a pipeline and at admission time.
Two properties are load-bearing. First, evaluation is pure: it reads only its Input and an injected clock (for waiver expiry), never the wall clock or a random source, so the same inputs always yield the same Decision — that is what makes policy testable as code. Second, it fails closed: a malformed policy or an evaluation error is never silently treated as "allow"; callers surface it and, in admission, deny.
Index ¶
- Variables
- type AttestationState
- type Case
- type CaseInput
- type CaseResult
- type DecisionType
- type Effect
- type Engine
- type Explanation
- type FindingJSON
- type Image
- type Input
- type Kind
- type Mode
- type Policy
- type ReportJSON
- type Result
- type Rule
- type RuleExplanation
- type RuleResult
- type Scope
- type StaticAttestation
- type Suite
- type SuiteResult
- type Value
- type Waiver
- type Waivers
- type Workload
Constants ¶
This section is empty.
Variables ¶
var ErrNotCompiled = errors.New("policy: engine not compiled")
ErrNotCompiled is returned by helpers that require a compiled engine.
Functions ¶
This section is empty.
Types ¶
type AttestationState ¶
type AttestationState interface {
// Signed reports whether the artifact carries a verified signature.
Signed() bool
// Verified reports whether an attestation of the given predicate-type URI
// was present and verified.
Verified(predicateType string) bool
// VerifiedPredicates lists all verified predicate-type URIs.
VerifiedPredicates() []string
}
AttestationState is the minimal supply-chain view the policy engine needs. It is deliberately tiny so the verify/attest packages can satisfy it with a thin adapter (a master action) without the policy engine depending on them, keeping the two phases decoupled and independently testable.
type Case ¶
type Case struct {
Name string `json:"name"`
// Now is the evaluation time (RFC3339). Empty uses the suite's default,
// keeping waiver-expiry tests deterministic and independent of the wall clock.
Now string `json:"now,omitempty"`
// Input is the world to evaluate.
Input CaseInput `json:"input"`
// Expect is the required aggregate decision.
Expect DecisionType `json:"expect"`
// ExpectFiring, when set, is the exact set of rule ids that must fire
// unwaived (order-independent). Nil means "do not check individual rules".
ExpectFiring []string `json:"expect_firing,omitempty"`
}
Case is one policy test: given Input at time Now, expect Decision.
type CaseInput ¶
type CaseInput struct {
Findings []FindingJSON `json:"findings,omitempty"`
Image Image `json:"image,omitempty"`
Workload Workload `json:"workload,omitempty"`
Attest StaticAttestation `json:"attest,omitempty"`
Licenses []string `json:"licenses,omitempty"`
Packages []string `json:"packages,omitempty"`
}
CaseInput is the JSON-friendly form of an Input (string severities, a static attestation state) that a suite file can express directly.
type CaseResult ¶
type CaseResult struct {
Name string `json:"name"`
Pass bool `json:"pass"`
Want DecisionType `json:"want"`
Got DecisionType `json:"got"`
Detail string `json:"detail,omitempty"`
}
CaseResult is the outcome of one case.
type DecisionType ¶
type DecisionType string
DecisionType is the aggregate verdict for an evaluation.
const ( // DecisionAllow means nothing blocking or warning fired. DecisionAllow DecisionType = "allow" // DecisionWarn means a warn rule fired (or a deny fired under audit mode), // but nothing blocks. DecisionWarn DecisionType = "warn" // DecisionDeny means a deny rule fired in enforce mode and was not waived // or overridden by an explicit allow. DecisionDeny DecisionType = "deny" )
func (DecisionType) Blocks ¶
func (d DecisionType) Blocks() bool
Blocks reports whether this decision should stop the workload/pipeline.
type Effect ¶
type Effect string
Effect is what a matched rule does to the decision.
const ( // EffectDeny blocks in enforce mode (and would-block in audit mode). EffectDeny Effect = "deny" // EffectWarn surfaces a warning but never blocks. EffectWarn Effect = "warn" // EffectAllow is an explicit allowance: a matched allow rule exempts the // input from denial, expressing a carve-out ("allow this base image even // though a later rule would deny it"). EffectAllow Effect = "allow" )
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is a compiled, ready-to-evaluate policy. It is immutable and safe for concurrent use: Evaluate reads only its rules and the per-call Input.
func Compile ¶
Compile validates and compiles a policy into an Engine. It aggregates every rule error into one message so an author fixes all problems in a single pass rather than one recompile at a time.
func CompileBytes ¶
CompileBytes is a convenience that parses then compiles a JSON policy.
func (*Engine) Evaluate ¶
Evaluate runs the compiled policy against an Input at time now (injected so waiver expiry is deterministic). It never returns an error: a rule that fails to evaluate is recorded on its RuleResult and treated as fail-closed, because a gate that crashed is a gate an attacker just walked through.
func (*Engine) Explain ¶
func (e *Engine) Explain(res *Result, in *Input) *Explanation
Explain renders a Result into an Explanation. It is a pure projection of the already-computed result plus the input (used to recover the predicate values), so calling it never changes a decision. It classifies rules through the same Denials/Warnings/WaivedRules accessors the CI gate and admission layer use, so an explanation can never disagree with the decision it explains.
type Explanation ¶
type Explanation struct {
Policy string `json:"policy"`
Decision DecisionType `json:"decision"`
Summary string `json:"summary"`
Denials []RuleExplanation `json:"denials,omitempty"`
Warnings []RuleExplanation `json:"warnings,omitempty"`
Waived []RuleExplanation `json:"waived,omitempty"`
// Remediation is the de-duplicated set of actions that, if taken, would
// clear the current denials — the agent's to-do list to get admitted.
Remediation []string `json:"remediation,omitempty"`
EvaluatedAt time.Time `json:"evaluated_at"`
}
Explanation is the structured, model-consumable account of a decision.
type FindingJSON ¶
type FindingJSON struct {
RuleID string `json:"rule_id"`
Module string `json:"module"`
Severity string `json:"severity"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Resource string `json:"resource,omitempty"`
Remediation string `json:"remediation,omitempty"`
References []string `json:"references,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
FindingJSON mirrors engine.Finding for decoding, with a string severity.
type Image ¶
type Image struct {
Reference string `json:"reference,omitempty"`
Registry string `json:"registry,omitempty"`
Repository string `json:"repository,omitempty"`
Tag string `json:"tag,omitempty"`
Digest string `json:"digest,omitempty"`
}
Image is the identity of the artifact under policy.
type Input ¶
type Input struct {
// Findings are the security findings under judgement (from a scan Report).
Findings []engine.Finding
// Image identifies the artifact being gated.
Image Image
// Workload is the Kubernetes security context, when gating a manifest.
Workload Workload
// Attest is the supply-chain verification state. It is an interface so the
// policy engine never imports the attestation package — Phase 2 supplies an
// adapter; tests and the CI gate use StaticAttestation.
Attest AttestationState
// Licenses lists SPDX license identifiers discovered in the artifact's SBOM.
Licenses []string
// Packages lists package names discovered in the artifact's SBOM.
Packages []string
}
Input is the immutable world a policy sees. It is assembled by a caller (the CI gate builds it from a scan Report; the admission webhook builds it from an AdmissionReview) and then evaluated without further I/O.
type Policy ¶
type Policy struct {
// Version is the policy schema version. Only "1" is understood today; an
// explicit version means a future engine can refuse or migrate old files.
Version string `json:"version"`
// Name identifies the policy in decisions and reports.
Name string `json:"name"`
// Description is a one-line human summary.
Description string `json:"description,omitempty"`
// Mode is enforce (default) or audit.
Mode Mode `json:"mode,omitempty"`
// Rules are evaluated in document order; order only affects report/explain
// ordering, not the final decision (deny always wins over warn).
Rules []Rule `json:"rules"`
// Waivers are documented, expiring exceptions.
Waivers []Waiver `json:"waivers,omitempty"`
}
Policy is a versioned, reviewable set of rules plus governed waivers.
type ReportJSON ¶
type ReportJSON struct {
TargetType string `json:"target_type"`
Target string `json:"target"`
Findings []FindingJSON `json:"findings"`
}
ReportJSON is the subset of engine.Report the policy engine consumes.
func LoadReport ¶
func LoadReport(data []byte) (*ReportJSON, error)
LoadReport decodes a scan report JSON document.
func (*ReportJSON) EngineFindings ¶
func (r *ReportJSON) EngineFindings() []engine.Finding
EngineFindings returns the report's findings in the engine model.
type Result ¶
type Result struct {
Policy string `json:"policy"`
Mode Mode `json:"mode"`
Decision DecisionType `json:"decision"`
// Rules holds every rule's outcome in policy document order.
Rules []RuleResult `json:"rules"`
// EvaluatedAt is the injected evaluation time (for waiver expiry). It is
// recorded so a decision is self-describing and auditable.
EvaluatedAt time.Time `json:"evaluated_at"`
}
Result is the full outcome of evaluating a policy against one Input.
func (*Result) Denials ¶
func (r *Result) Denials() []RuleResult
Denials returns the rule results that actually blocked admission. Because a block is a property of the aggregate decision (an allow override or audit mode can neutralize a would-be deny), this is empty unless Decision is deny — so a caller never emits a "violation" that contradicts an allow verdict.
func (*Result) Firing ¶
func (r *Result) Firing(includeWaived bool) []RuleResult
Firing returns the rule results that matched (optionally including waived).
func (*Result) WaivedRules ¶
func (r *Result) WaivedRules() []RuleResult
WaivedRules returns the firings that a matching waiver suppressed.
func (*Result) Warnings ¶
func (r *Result) Warnings() []RuleResult
Warnings returns rule results surfaced as warnings: matched warn rules, plus any rule that fired or errored but did not block (audit mode or an allow override). Waived firings are excluded.
type Rule ¶
type Rule struct {
// ID is a stable, human-meaningful identifier, unique within the policy.
ID string `json:"id"`
// Description explains the intent for reviewers.
Description string `json:"description,omitempty"`
// Match is the boolean condition; the rule fires when it evaluates to true.
Match string `json:"match"`
// Effect is deny, warn, or allow.
Effect Effect `json:"effect"`
// Severity is the finding severity when this rule fires. Empty defaults from
// the effect (deny -> HIGH, warn -> MEDIUM).
Severity string `json:"severity,omitempty"`
// Message is the human explanation attached to a firing.
Message string `json:"message,omitempty"`
// Remediation tells an operator (or an agent) how to become compliant.
Remediation string `json:"remediation,omitempty"`
// References are standards/citations (CIS, NIST, internal runbooks).
References []string `json:"references,omitempty"`
}
Rule is one policy statement: when Match evaluates true, Effect applies.
type RuleExplanation ¶
type RuleExplanation struct {
RuleID string `json:"rule_id"`
Effect Effect `json:"effect"`
Severity string `json:"severity,omitempty"`
Reason string `json:"reason,omitempty"`
Condition string `json:"condition"`
Facts map[string]string `json:"facts,omitempty"`
Remediation string `json:"remediation,omitempty"`
References []string `json:"references,omitempty"`
Waived bool `json:"waived,omitempty"`
WaiverReason string `json:"waiver_reason,omitempty"`
}
RuleExplanation explains one rule's firing, including the concrete input values that made it fire ("facts") so the reason is verifiable, not asserted.
type RuleResult ¶
type RuleResult struct {
RuleID string `json:"rule_id"`
Description string `json:"description,omitempty"`
Effect Effect `json:"effect"`
Matched bool `json:"matched"`
Message string `json:"message,omitempty"`
Remediation string `json:"remediation,omitempty"`
References []string `json:"references,omitempty"`
// Severity is the finding severity name for a matched deny/warn.
Severity string `json:"severity,omitempty"`
// Waived is set when a matching, unexpired waiver suppressed this firing.
Waived bool `json:"waived,omitempty"`
// WaiverReason carries the waiver's justification and expiry when waived.
WaiverReason string `json:"waiver_reason,omitempty"`
// Error, when set, is the evaluation error for this rule (fail-closed:
// a rule that errors is treated as if it denied).
Error string `json:"error,omitempty"`
}
RuleResult records one rule's outcome for a specific evaluation.
type Scope ¶
type Scope struct {
// ImagePattern is a regexp matched against the image reference. Empty = any.
ImagePattern string `json:"image_pattern,omitempty"`
// Registry is an exact registry host match. Empty = any.
Registry string `json:"registry,omitempty"`
}
Scope narrows a waiver to specific artifacts, so "we accept this CVE on the legacy image" does not silently excuse it everywhere.
type StaticAttestation ¶
type StaticAttestation struct {
IsSigned bool `json:"signed,omitempty"`
Predicates []string `json:"predicates,omitempty"`
}
StaticAttestation is a plain-data AttestationState. The CI gate builds one from a scan Report's verification verdict; tests construct it directly. A nil AttestationState is treated as "nothing verified" (the fail-closed default).
func InferAttestation ¶
func InferAttestation(findings []engine.Finding) StaticAttestation
InferAttestation derives supply-chain state from a combined scan report without importing the verify module: it reads the verification verdict finding that the verify module emits (metadata verdict=PASSED and a comma-separated verified_levels). This lets a policy reference `signed` / `verified(...)` when the scan already ran verification, and otherwise reports nothing verified — the fail-closed default. The verify rule namespace ("DS-RAT-SUP-") is matched by prefix so a rename of a specific rule id does not silently break inference.
func (StaticAttestation) Signed ¶
func (s StaticAttestation) Signed() bool
func (StaticAttestation) Verified ¶
func (s StaticAttestation) Verified(predicateType string) bool
func (StaticAttestation) VerifiedPredicates ¶
func (s StaticAttestation) VerifiedPredicates() []string
type Suite ¶
type Suite struct {
// Policy is the path to the policy under test, relative to the suite file.
Policy string `json:"policy"`
// Cases are the individual test cases.
Cases []Case `json:"cases"`
}
Suite is a policy test file.
type SuiteResult ¶
type SuiteResult struct {
Results []CaseResult `json:"results"`
Passed int `json:"passed"`
Failed int `json:"failed"`
}
SuiteResult aggregates a run.
type Value ¶
type Value struct {
Kind Kind
// contains filtered or unexported fields
}
Value is a single dynamically-typed policy value.
func (Value) AsBool ¶
AsBool returns the boolean value, or an error if the value is not a bool. The final result of a rule's match expression must be a bool; using a number or string in a boolean position is a policy error, not a coercion, so mistakes surface at evaluation rather than silently passing a gate.
func (Value) Contains ¶
Contains reports whether a list value contains x. It is the backing for the in(x, list) builtin and returns an error if the receiver is not a list.
func (Value) Equal ¶
Equal reports value equality. Cross-kind comparison is defined (and false) rather than an error, so `registry == 5` is simply false instead of aborting a whole policy evaluation. Lists compare element-wise.
type Waiver ¶
type Waiver struct {
// RuleID is the rule to suppress. Matching is exact.
RuleID string `json:"rule_id"`
// Reason is the mandatory justification recorded in the audit trail.
Reason string `json:"reason"`
// Owner is who accepted the risk (for accountability).
Owner string `json:"owner,omitempty"`
// Expires is when the waiver stops applying (RFC3339, or a bare YYYY-MM-DD
// treated as end-of-day UTC). A missing or unparseable value is treated as
// already expired — a waiver must have an end date to be honored.
Expires string `json:"expires"`
// Scope optionally narrows the waiver to matching images.
Scope Scope `json:"scope,omitempty"`
}
Waiver suppresses one rule's firing until it expires.
type Waivers ¶
type Waivers struct {
// contains filtered or unexported fields
}
Waivers is an ordered set of waivers.
type Workload ¶
type Workload struct {
Present bool `json:"present,omitempty"`
Privileged bool `json:"privileged,omitempty"`
RunAsRoot bool `json:"run_as_root,omitempty"`
HostNetwork bool `json:"host_network,omitempty"`
HostPID bool `json:"host_pid,omitempty"`
HostIPC bool `json:"host_ipc,omitempty"`
ReadOnlyRootFS bool `json:"read_only_root_fs,omitempty"`
AllowPrivilegeEscalation bool `json:"allow_privilege_escalation,omitempty"`
UsesHostPath bool `json:"uses_host_path,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
Images []string `json:"images,omitempty"`
}
Workload is the security-relevant projection of a Kubernetes pod spec, flat enough for a policy rule to reference directly (privileged, runs_as_root, …).