Documentation
¶
Overview ¶
Package compliance is the shared machinery for hardening-benchmark auditing (CAPABILITY_SPEC domain 10). It is deliberately engine-agnostic at its core: benchmarks are *data* (a control catalogue with framework mappings), and the per-control pass/fail logic is injected by callers via an Assessor. That separation lets dockerbench and kubebench share one runner, one aggregation model, one waiver/drift/narrative layer, and one framework-mapping vocabulary while owning only their own evidence gathering and check functions.
The engine only enters through finding.go, which projects a compliance.Report into the unified Finding stream. Everything else here has no engine import so it stays reusable and trivially testable.
Index ¶
- func Findings(moduleName string, rep *Report) []engine.Finding
- func FrameworkCoverage(controls []Control) map[Framework]int
- func Render(rep *ComplianceReport, format ExportFormat) ([]byte, error)
- type Assessment
- type Assessor
- type AttestationRegister
- type Benchmark
- type Catalog
- type Change
- type ComplianceReport
- type Control
- type ControlResult
- type Disposition
- type Drift
- type DriftEntry
- type Evidence
- type ExportFormat
- type Fix
- type Framework
- type FrameworkCoverageStat
- type FrameworkLine
- type FrameworkRef
- type Level
- type Narrative
- type NarrativeItem
- type NarrativeOptions
- type Pack
- type PackControl
- type RegisterEntry
- type Report
- type Result
- type RunOptions
- type Status
- type Waiver
- type Waivers
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Findings ¶
Findings projects a report into engine findings for the given module. It emits one leading INFO summary (score + counts, always present so the module shows up in a scan even when fully compliant) followed by a finding per non-PASS control. Effective status is used so waived controls report as INFO with the waiver reason rather than as failures. Output order follows the report's deterministic control ordering.
func FrameworkCoverage ¶
FrameworkCoverage summarizes, across a whole report, which frameworks are exercised and how many controls touch each. It powers the "one scan feeds many audits" story in the narrative and export. Keys are returned sorted by caller (see SortedFrameworks) so callers control iteration order.
func Render ¶
func Render(rep *ComplianceReport, format ExportFormat) ([]byte, error)
Render serializes a ComplianceReport in the requested format. Output is deterministic for a fixed report (ids derive from content, not randomness).
Types ¶
type Assessment ¶
type Assessment struct {
Status Status
Evidence string // human-readable: what we observed and why it passed/failed
Actual string // machine-readable observed value (empty when N/A)
}
Assessment is what an Assessor returns for a single control.
type Assessor ¶
type Assessor func(c Control) Assessment
Assessor evaluates one control against already-collected evidence. Modules close over their evidence and dispatch by control ID. Assessors must be pure: same evidence in, same assessment out.
type AttestationRegister ¶
type AttestationRegister struct {
Entries []RegisterEntry `json:"entries"`
}
AttestationRegister holds the waivers (for accepted Failed controls) and attestations (for manual/inherited controls). Both carry an owner and an expiry so accepted risk and manual evidence are auditable and time-bounded — never a permanent silent pass (COMPLIANCE_PLAN §4.4).
func LoadRegister ¶
func LoadRegister(path string) (*AttestationRegister, error)
LoadRegister reads a JSON register from disk. A missing path yields an empty register (no waivers/attestations), not an error.
func (*AttestationRegister) Attestation ¶
func (r *AttestationRegister) Attestation(framework, id string, now time.Time) (RegisterEntry, bool)
Attestation returns a valid (unexpired) attestation for a control, if one exists.
func (*AttestationRegister) Waiver ¶
func (r *AttestationRegister) Waiver(framework, id string, now time.Time) (RegisterEntry, bool)
Waiver returns a valid (unexpired) waiver for a control, if one exists.
type Benchmark ¶
type Benchmark struct {
Code string // short slug used in rule IDs, e.g. "docker", "k8s"
Name string // human title, e.g. "CIS Docker Benchmark"
Version string // benchmark version, e.g. "1.6.0"
Profile string // active profile, e.g. "self-managed", "eks"
Controls []Control
}
Benchmark is a named, versioned control catalogue plus the profile it targets (e.g. self-managed vs a managed-Kubernetes variant). Controls are stored in the order the runner should evaluate and report them; Run re-sorts by control ID for a stable, human-sensible ordering regardless of source order.
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog is the unified, normalized control catalog: every loaded pack keyed by framework, plus the crosswalk graph built from the packs' maps_to.
func LoadEmbeddedPacks ¶
LoadEmbeddedPacks loads the packs compiled into the binary.
func LoadPacksFromDir ¶
LoadPacksFromDir loads packs from a directory (for user-supplied or updated packs), falling back to nothing if the directory is empty.
func (*Catalog) Controls ¶
func (c *Catalog) Controls(framework string) []PackControl
Controls returns a framework's controls (empty if the framework is unknown).
func (*Catalog) Frameworks ¶
Frameworks lists loaded frameworks in load order.
func (*Catalog) MappedFrameworks ¶
MappedFrameworks returns the set of frameworks a control crosswalks to.
type Change ¶
type Change string
Change classifies how one control moved between two runs.
const ( // ChangeRegressed: was compliant (PASS), now failing/warning — the alarming case. ChangeRegressed Change = "regressed" // ChangeFixed: was failing/warning, now PASS — a win to celebrate in the narrative. ChangeFixed Change = "fixed" // ChangeNew: control exists now but not in the baseline (catalogue grew or first run). ChangeNew Change = "new" // ChangeRemoved: control was in the baseline but not now (profile change). ChangeRemoved Change = "removed" // ChangeUnchanged: same effective status in both runs. ChangeUnchanged Change = "unchanged" )
type ComplianceReport ¶
type ComplianceReport struct {
Target string `json:"target"`
GeneratedAt string `json:"generated_at"`
ToolVersion string `json:"tool_version"`
Frameworks []string `json:"frameworks"`
Results []ControlResult `json:"results"`
}
ComplianceReport is the full result of a compliance run.
func RunPacks ¶
func RunPacks(cat *Catalog, frameworks []string, rep *engine.Report, opts RunOptions) *ComplianceReport
RunPacks evaluates the enabled frameworks' controls against an engine Report and returns per-control dispositions with evidence and crosswalk. It is a pure function of (catalog, report, options): the same inputs always produce the same report.
type Control ¶
type Control struct {
// ID is the native benchmark control number, e.g. "2.1" for CIS Docker.
ID string `json:"id"`
// Title is the one-line requirement, phrased as the desired end state.
Title string `json:"title"`
// Section groups controls for reporting, e.g. "Docker daemon configuration".
Section string `json:"section,omitempty"`
// Level is the CIS profile (Level 1 baseline vs Level 2 hardening).
Level Level `json:"level"`
// Scored is false for "manual"/informational controls that cannot be
// auto-evaluated with confidence; those surface as INFO for human review.
Scored bool `json:"scored"`
// Description explains what the control protects against (the "why").
Description string `json:"description,omitempty"`
// Remediation is guided prose an operator can follow to fix a failure.
Remediation string `json:"remediation,omitempty"`
// Frameworks maps this control onto other compliance frameworks. Every
// control MUST carry at least one non-CIS mapping (see benchmark tests).
Frameworks []FrameworkRef `json:"frameworks,omitempty"`
// Fix is an optional structured, agent-appliable remediation bundle. It is
// advisory metadata only and never changes the deterministic scan result.
Fix *Fix `json:"fix,omitempty"`
}
Control is one benchmark requirement. A Benchmark is a catalogue of these, and framework mappings live on the control so a single scan can feed many audits (CIS + NIST + STIG + …) without hand-mapping downstream.
func (Control) References ¶
References renders a control's framework mappings into the flat []string the engine.Finding.References field expects, prefixed with the native CIS control so downstream (SARIF helpUri, tables) always shows the benchmark id first. Output is deterministic: native CIS id, then authored order, de-duplicated.
type ControlResult ¶
type ControlResult struct {
Framework string `json:"framework"`
Version string `json:"version"`
ID string `json:"id"`
Title string `json:"title"`
Assessment string `json:"assessment"`
Disposition Disposition `json:"disposition"`
Evidence Evidence `json:"evidence"`
MapsTo map[string][]string `json:"maps_to,omitempty"`
Remediation string `json:"remediation,omitempty"`
}
ControlResult is one control's assessment across the run, including the crosswalk to every framework it satisfies.
func Gaps ¶
func Gaps(rep *ComplianceReport) []ControlResult
Gaps returns the controls that are not fully resolved: Failed, unresolved Manual, or Unknown — the auditor's to-do list.
type Disposition ¶
type Disposition string
Disposition is a control's resolved state per the satisfaction contract (COMPLIANCE_PLAN §3). No control may remain Unknown at release.
const ( DispSatisfied Disposition = "Satisfied" DispFailed Disposition = "Failed" DispWaived Disposition = "Waived" DispNotApplicable Disposition = "NotApplicable" DispManual Disposition = "Manual" // awaiting attestation DispUnknown Disposition = "Unknown" )
func (Disposition) Resolved ¶
func (d Disposition) Resolved() bool
Resolved reports whether a disposition counts toward coverage (anything but Unknown and un-attested Manual).
type Drift ¶
type Drift struct {
Benchmark string `json:"benchmark"`
Regressed []DriftEntry `json:"regressed,omitempty"`
Fixed []DriftEntry `json:"fixed,omitempty"`
New []DriftEntry `json:"new,omitempty"`
Removed []DriftEntry `json:"removed,omitempty"`
ScoreFrom int `json:"score_from"`
ScoreTo int `json:"score_to"`
}
Drift is the per-control delta between an earlier baseline report and the current one. It is the raw material for "flag the specific drifted control" and for the continuous-compliance narrative's since-last-run section.
func Diff ¶
Diff compares a baseline report against the current one and returns the drift. Comparison is on effective status (waivers demoted) and keyed by control id, so it is stable across reorderings and deterministic. A nil baseline yields a drift where every current control is "new" (the honest first-run answer).
type DriftEntry ¶
type DriftEntry struct {
ControlID string `json:"control_id"`
Title string `json:"title"`
Change Change `json:"change"`
From Status `json:"from"`
To Status `json:"to"`
}
DriftEntry is one control's movement between baseline and current.
type Evidence ¶
type Evidence struct {
Check string `json:"check,omitempty"`
Observed string `json:"observed"`
Verdict string `json:"verdict"`
Timestamp string `json:"timestamp"`
Tool string `json:"tool"`
Target string `json:"target,omitempty"`
}
Evidence is the machine-readable proof attached to every assessed control.
type ExportFormat ¶
type ExportFormat string
ExportFormat names a supported compliance report serialization.
const ( ExportJSON ExportFormat = "json" ExportCSV ExportFormat = "csv" ExportOSCAL ExportFormat = "oscal" // NIST OSCAL assessment-results (subset) ExportMD ExportFormat = "md" // human auditor packet )
type Fix ¶
type Fix struct {
// Kind is the mechanism: "daemon.json", "file-perm", "sysctl",
// "kubelet-flag", "apiserver-flag", "manifest". Consumers switch on it.
Kind string `json:"kind"`
// Target is the path or object the fix applies to (e.g. "/etc/docker/daemon.json").
Target string `json:"target"`
// Snippet is the concrete change (a JSON fragment, a flag, a mode string).
Snippet string `json:"snippet"`
// DryRun describes the effect in one line for a human-readable diff preview.
DryRun string `json:"dry_run,omitempty"`
}
Fix is a structured remediation an automation agent can propose or apply with a dry-run diff. It is intentionally declarative (what to set, where) rather than an executable script, so a human or agent reviews before acting.
type Framework ¶
type Framework string
Framework identifies a compliance regime a control can be mapped onto. The point of the abstraction is one scan → many audits: a single control failure simultaneously satisfies the evidence needs of CIS, NIST 800-190, DISA STIG, and the higher-level regimes those roll up into.
const ( FrameworkCIS Framework = "CIS" // the native benchmark FrameworkNIST190 Framework = "NIST-800-190" // Application Container Security Guide FrameworkNIST53 Framework = "NIST-800-53" // security & privacy controls FrameworkSTIG Framework = "DISA-STIG" // DoD Security Technical Implementation Guide FrameworkPCI Framework = "PCI-DSS-4.0" // payment card industry FrameworkNSACISA Framework = "NSA-CISA-K8s" // Kubernetes Hardening Guidance )
func SortedFrameworks ¶
SortedFrameworks returns the frameworks in a coverage map in a stable order.
type FrameworkCoverageStat ¶
type FrameworkCoverageStat struct {
Framework string `json:"framework"`
Total int `json:"total"`
Satisfied int `json:"satisfied"`
Failed int `json:"failed"`
Waived int `json:"waived"`
NotApplicable int `json:"not_applicable"`
Manual int `json:"manual"` // unresolved manual (needs attestation)
Unknown int `json:"unknown"`
Resolved int `json:"resolved"` // any state but Unknown/unresolved-Manual
CoveragePct float64 `json:"coverage_pct"`
AutomatedPct float64 `json:"automated_pct"`
}
FrameworkCoverageStat summarizes how completely one framework is satisfied.
func Coverage ¶
func Coverage(rep *ComplianceReport) []FrameworkCoverageStat
Coverage aggregates a ComplianceReport into per-framework KPIs (COMPLIANCE_PLAN §5/§8): Coverage = resolved ÷ total; Automation rate = automatically-satisfied ÷ total. Frameworks are returned sorted for deterministic output.
type FrameworkLine ¶
FrameworkLine is one row of framework coverage for dashboards.
type FrameworkRef ¶
FrameworkRef is a single mapping: "this control corresponds to <ID> in <Framework>". A control may carry several. Kept as an explicit slice (not a map) so ordering is authored and deterministic.
func Ref ¶
func Ref(fw Framework, id string) FrameworkRef
Ref is a terse constructor used when authoring benchmark catalogues in the dockerbench/kubebench packages.
type Level ¶
type Level int
Level is the CIS benchmark profile a control belongs to. Level 1 is the baseline every environment should meet; Level 2 is defense-in-depth that can impose operational cost. Selecting a profile lets an auditor scope a run.
func (Level) MarshalJSON ¶
MarshalJSON renders levels as "L1"/"L2" so JSON exports read like a CIS profile column rather than an opaque integer.
type Narrative ¶
type Narrative struct {
Benchmark string `json:"benchmark"`
Version string `json:"version"`
Profile string `json:"profile,omitempty"`
GeneratedAt string `json:"generated_at"`
Score int `json:"score"`
Counts map[string]int `json:"counts"`
Frameworks []FrameworkLine `json:"frameworks"`
Drift *Drift `json:"drift,omitempty"`
TopFailures []NarrativeItem `json:"top_failures,omitempty"`
Expiring []Waiver `json:"expiring_waivers,omitempty"`
}
Narrative is the structured "state of compliance" summary. It marshals to JSON for machine consumers and renders to prose via Text() for humans.
func BuildNarrative ¶
func BuildNarrative(rep *Report, opts NarrativeOptions) *Narrative
BuildNarrative assembles the narrative from a report. It performs no I/O and reads no ambient state, so identical inputs yield an identical narrative.
type NarrativeItem ¶
type NarrativeItem struct {
ControlID string `json:"control_id"`
Title string `json:"title"`
Status Status `json:"status"`
Section string `json:"section,omitempty"`
Remediation string `json:"remediation,omitempty"`
Frameworks []FrameworkRef `json:"frameworks,omitempty"`
Fix *Fix `json:"fix,omitempty"`
}
NarrativeItem is one highlighted control (a failure or a fix) with the machine-consumable context an agent needs to reason about or act on it.
type NarrativeOptions ¶
type NarrativeOptions struct {
Now time.Time // injected timestamp (required for determinism)
Baseline *Report // prior run, for the since-last-run drift section
Waivers *Waivers // to surface soon-to-expire exceptions
MaxFailures int // cap on TopFailures (0 ⇒ default of 5)
}
NarrativeOptions parameterizes narrative generation. Now is mandatory and is the injected clock that keeps output deterministic in tests and reproducible in production.
type Pack ¶
type Pack struct {
Framework string `json:"framework"` // e.g. "cis-docker", "pci-dss-4.0.1"
Version string `json:"version"`
Title string `json:"title"`
SourceURL string `json:"source_url"`
Controls []PackControl `json:"controls"`
}
Pack is one framework version as data. Adding or updating a framework is a reviewed data change, never a code change.
type PackControl ¶
type PackControl struct {
ID string `json:"id"`
Title string `json:"title"`
Statement string `json:"statement,omitempty"`
Level int `json:"level,omitempty"`
Assessment string `json:"assessment"` // automated|manual|inherited|hybrid
Module string `json:"module,omitempty"` // engine module that owns Check
Check string `json:"check,omitempty"` // engine finding RuleID that evaluates it
// PresentMeans flips check polarity: for most controls a matching finding is
// a violation ("fail", the default); for evidence controls (e.g. "an SBOM was
// generated") the finding's presence proves satisfaction ("pass").
PresentMeans string `json:"present_means,omitempty"`
Expected string `json:"expected,omitempty"`
Remediation string `json:"remediation,omitempty"`
MapsTo map[string][]string `json:"maps_to,omitempty"` // framework -> control ids
References []string `json:"references,omitempty"`
}
PackControl is a single control: how it is assessed, which automated check evaluates it, and the crosswalk to every other framework it maps to.
type RegisterEntry ¶
type RegisterEntry struct {
Kind string `json:"kind"` // "waiver" | "attestation"
Framework string `json:"framework"`
ControlID string `json:"control_id"`
Owner string `json:"owner"`
Justification string `json:"justification,omitempty"` // waivers
Evidence string `json:"evidence,omitempty"` // attestations (policy link, SOC report)
Expires time.Time `json:"expires"`
}
RegisterEntry is one waiver or attestation for a specific control.
type Report ¶
type Report struct {
Benchmark string `json:"benchmark"`
Version string `json:"version"`
Profile string `json:"profile,omitempty"`
Results []Result `json:"results"`
}
Report is the aggregated outcome of running a benchmark. It is safe to JSON-marshal directly as an auditor-ready, control-by-control evidence export.
func (*Report) Counts ¶
Counts tallies results by effective status (waivers demoted). The map always contains all five statuses so callers need not check for missing keys.
func (*Report) Failing ¶
Failing returns the results that still count against the score (FAIL, not waived), in report order — the actionable worklist.
func (*Report) FailsAt ¶
FailsAt reports whether the report should fail a CI/CD gate: any effective FAIL fails; if warnAlso is set, effective WARN fails too. Waived controls never trip the gate.
func (*Report) Score ¶
Score is the compliance pass rate over *scorable* results — those that landed on PASS or FAIL (effective). WARN and INFO are excluded from the denominator because they are advisory, not a hard pass/fail. Returns 0..100; a report with nothing scorable scores 100 (nothing failed).
type Result ¶
type Result struct {
Control Control `json:"control"`
Status Status `json:"status"`
Evidence string `json:"evidence,omitempty"`
Actual string `json:"actual,omitempty"`
// Waived is set when a matching, unexpired waiver suppressed a WARN/FAIL.
// The original Status is preserved above; consumers gate on !Waived.
Waived bool `json:"waived,omitempty"`
WaiverReason string `json:"waiver_reason,omitempty"`
}
Result pairs a control with the outcome of assessing it. The full Control is embedded (not just its id) so an exported report is self-describing evidence: an auditor reading the JSON sees the requirement, the mappings, the observed value, and the verdict together, with no external lookup.
type RunOptions ¶
type RunOptions struct {
Now time.Time
ToolVersion string
Target string
Register *AttestationRegister
}
RunOptions carries the injected clock (for determinism), tool version, scanned target, and the waiver/attestation register.
type Status ¶
type Status int
Status is the outcome of evaluating one control against collected evidence. It mirrors the PASS/WARN/FAIL/INFO vocabulary auditors expect from CIS tools.
const ( // StatusUnknown means the control was never evaluated (a programming error // if it reaches a report). It sorts first so it is easy to spot. StatusUnknown Status = iota // StatusInfo covers manual-review controls and inputs we could not read. // Per the contract, an unreadable input degrades to INFO, never a crash. StatusInfo // StatusPass means the control's requirement is satisfied. StatusPass // StatusWarn means partially satisfied or best-practice-but-not-required. StatusWarn // StatusFail means the requirement is violated. StatusFail )
func (Status) MarshalJSON ¶
MarshalJSON renders statuses as their names so JSON exports read like an auditor's control sheet rather than opaque integers.
type Waiver ¶
type Waiver struct {
// Control is the native control id to suppress (e.g. "2.13"). Matching is
// exact; a benchmark code may be given to scope cross-benchmark files.
Control string `json:"control"`
// Benchmark optionally scopes the waiver to one benchmark code ("docker",
// "k8s"). Empty applies to any benchmark carrying the control id.
Benchmark string `json:"benchmark,omitempty"`
// Reason is the mandatory human 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 the date the waiver stops applying (RFC3339). A zero/empty
// value is treated as already expired — waivers must have an end date.
Expires string `json:"expires,omitempty"`
}
Waiver records an accepted risk: a specific control is knowingly suppressed until a date, with a justification and an owner. Waivers are the auditable escape hatch demanded by every serious compliance regime — an exception that expires and re-surfaces beats a check silently disabled in code forever.
type Waivers ¶
type Waivers struct {
// contains filtered or unexported fields
}
Waivers is a set of waivers with an evaluation clock. Construct with the benchmark code you are scoring so Apply can honor per-benchmark scoping.
func (*Waivers) Apply ¶
Apply marks results as waived in place when a matching, unexpired waiver exists at time now. It returns the report for chaining. The original Status is preserved; only Waived/WaiverReason are set, so gating and scoring use the demoted status while the export still shows the true verdict.