Documentation
¶
Overview ¶
Package compliancekit is the v1.0+ public API surface of compliancekit. Anything exported from this package is covered by SemVer 2.0 — breaking changes require a major-version bump and the last two minor versions receive security patches for at least two years after release.
The contract was established at the v1.0 release by promoting the load-bearing types that survived four iterations (v0.1, v0.6, v0.12, v0.18) out of internal/ and into this package. The full surface is machine-checked: cmd/genapi enumerates every exported identifier and diffs against api.txt in this directory; the CI gate fails the build when an identifier is added, renamed, or removed without an explicit api.txt update so the maintainer cannot widen the contract by accident.
Embedding compliancekit ¶
The canonical embedding shape is "build a ResourceGraph, register checks, run an Evaluator, render Findings through a Reporter." Operators who only run the CLI never touch this package; tool authors who want to compose compliancekit into a larger product import it directly:
import "github.com/darpanzope/compliancekit/pkg/compliancekit"
Subpackages and internal/ are NOT covered by the SemVer promise. Anything under internal/ may change in any release; consumers who reach across that boundary do so at their own risk.
What is NOT in pkg/compliancekit ¶
- Vulnerability databases. compliancekit ingests Trivy / Grype output; it does not reimplement a CVE store. See ADR-009.
- Auto-remediation execution. Remediation snippets are generated and rendered through this package, but applying them is an opt-in CLI-only action that requires per-run reaffirmation.
- The serve daemon. CLI parity is a hard invariant; daemon mode is permanently optional and lives under internal/.
- Telemetry. There is none, and there will never be any.
Versioning ¶
The Go module path stays github.com/darpanzope/compliancekit for the entire v1.x line. A hypothetical v2.0 would live under /v2/ per Go module conventions and would only ship if a real contract break is unavoidable.
Index ¶
- Constants
- func Register(check Check, fn CheckFunc)
- func RegisteredCount() int
- func RegisteredIDs() []string
- func Unregister(id string) bool
- type Check
- type CheckFunc
- type Collector
- type Comment
- type Control
- type Evaluator
- type EvidencePtr
- type Finding
- type Framework
- type Package
- type Registry
- func (r *Registry) Check(id string) (Check, bool)
- func (r *Registry) Checks() []Check
- func (r *Registry) Count() int
- func (r *Registry) Get(id string) (CheckFunc, bool)
- func (r *Registry) IDs() []string
- func (r *Registry) Register(check Check, fn CheckFunc)
- func (r *Registry) Unregister(id string) bool
- type Reporter
- type ResolvedControl
- type Resource
- type ResourceGraph
- func (g *ResourceGraph) Add(r Resource)
- func (g *ResourceGraph) All() []Resource
- func (g *ResourceGraph) ByID(id string) (Resource, bool)
- func (g *ResourceGraph) ByType(t string) []Resource
- func (g *ResourceGraph) Count() int
- func (g *ResourceGraph) Query(expr string) ([]Resource, error)
- func (g *ResourceGraph) Related(r Resource, edge string) []Resource
- type ResourceRef
- type SearchResponse
- type SearchResult
- type SearchType
- type Secret
- type Severity
- type Source
- type Status
- type Tactic
- type User
- type Vulnerability
- type WaiverRef
Constants ¶
const ( CategoryCompliance = "compliance" CategoryThreatModel = "threat_model" )
CategoryCompliance / CategoryThreatModel classify a framework. Most frameworks are audit-shaped: a finite control catalog that a check either reaches or does not. MITRE ATT&CK is different — it maps to tactics + techniques along the kill chain, so the reporter renders it as a matrix view rather than a control checklist. Frameworks default to compliance when Category is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisteredCount ¶
func RegisteredCount() int
RegisteredCount returns the count of checks in the default registry.
func RegisteredIDs ¶
func RegisteredIDs() []string
RegisteredIDs returns sorted IDs from the default registry.
func Unregister ¶
Unregister removes id from the default registry. Returns true if something was actually removed. See Registry.Unregister for the "tests only" caveat.
Types ¶
type Check ¶
type Check struct {
// ID is the stable identifier, kebab-case. Convention:
// "<provider>-<service>-<concise-rule>", e.g. "do-spaces-public-acl".
// Never rename an ID; deprecate via the Deprecated field instead.
ID string `yaml:"id" json:"id"`
// Title is the short human-readable summary shown in reports.
Title string `yaml:"title" json:"title"`
// Severity is the impact classification. See severity.go.
Severity Severity `yaml:"severity" json:"severity"`
// Provider names the provider this check targets, e.g. "digitalocean".
Provider string `yaml:"provider" json:"provider"`
// Service narrows the provider area, e.g. "spaces", "droplets".
Service string `yaml:"service,omitempty" json:"service,omitempty"`
// ResourceType identifies the resource kind the check examines.
ResourceType string `yaml:"resource_type,omitempty" json:"resource_type,omitempty"`
// Description explains the check in prose. Shown in `checks show` and
// the HTML report.
Description string `yaml:"description" json:"description"`
// Rationale optionally captures the "why this is bad" -- typically
// citing an incident or threat model.
Rationale string `yaml:"rationale,omitempty" json:"rationale,omitempty"`
// Remediation is the human-readable fix. Specific commands beat
// generic advice; CHECKS.md spells this out.
Remediation string `yaml:"remediation" json:"remediation"`
// Frameworks maps framework ID to the control IDs it satisfies,
// e.g. {"soc2": ["CC6.1", "CC6.6"], "cis-v8": ["3.3"]}.
Frameworks map[string][]string `yaml:"frameworks" json:"frameworks"`
// Tags are filter labels propagated to Findings.
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"`
// References link to authoritative external sources (docs, CWEs, etc.).
References []string `yaml:"references,omitempty" json:"references,omitempty"`
// Scanner names the Go CheckFunc registered in the registry under
// this ID. Mutually exclusive with Policy.
Scanner string `yaml:"scanner,omitempty" json:"scanner,omitempty"`
// Policy points at a Rego file. Available from v0.13 once the Rego
// evaluator lands. Mutually exclusive with Scanner.
Policy string `yaml:"policy,omitempty" json:"policy,omitempty"`
// Deprecated marks the check as scheduled for removal.
Deprecated bool `yaml:"deprecated,omitempty" json:"deprecated,omitempty"`
// DeprecatedIn records the version in which deprecation was announced,
// e.g. "v0.7".
DeprecatedIn string `yaml:"deprecated_in,omitempty" json:"deprecated_in,omitempty"`
// ReplacedBy is the ID of the check that supersedes this one.
ReplacedBy string `yaml:"replaced_by,omitempty" json:"replaced_by,omitempty"`
}
Check is the parsed metadata of a single check from the YAML catalog.
At runtime, a Check is paired with a CheckFunc registered in the check registry; together they form a complete check ready to run. The YAML lives at internal/checks/<provider>/*.yaml; the schema is documented in CHECKS.md.
func LookupCheck ¶
LookupCheck returns the Check metadata for an ID from the default registry.
func RegisteredChecks ¶
func RegisteredChecks() []Check
RegisteredChecks returns every Check, sorted by ID.
type CheckFunc ¶
type CheckFunc func(ctx context.Context, graph *ResourceGraph) ([]Finding, error)
CheckFunc is the signature every check evaluator must satisfy.
Implementers read from the ResourceGraph and emit Findings. They MUST NOT perform I/O -- collectors do all data fetching; scanners only evaluate. The returned error signals "the check could not run" (data missing, ambiguous), not "the resource is non-compliant" (which is a Finding with StatusFail).
At v0.1 implementers are plain Go functions registered via Register. At v0.13 Rego policies will be wrapped into CheckFuncs by the Rego evaluator; the signature was chosen so this addition requires no change to existing checks (see DECISIONS.md ADR-002).
type Collector ¶
type Collector interface {
// Name returns the provider identifier, e.g. "digitalocean" or "linux".
// Used in logs, error messages, and the resource ID prefix.
Name() string
// Collect fetches resources from the provider. The returned slice is
// added to the engine's ResourceGraph; the engine handles ordering
// and de-duplication across collectors.
Collect(ctx context.Context) ([]Resource, error)
}
Collector fetches data from a provider and emits typed Resources for the ResourceGraph.
A Collector is constructed with its provider-specific configuration (DO token, SSH inventory, kubeconfig path); Collect performs the actual fetch and is the only method the engine invokes. Splitting construction from execution lets the engine validate every collector's config (and surface friendly errors) before spending any time on network I/O.
Collectors MUST honor ctx.Done() -- long-running provider scans get canceled on user interrupt (Ctrl-C) and on engine-side timeouts.
See DECISIONS.md ADR-001 for the reasoning behind splitting collection from evaluation.
type Comment ¶ added in v1.8.0
type Comment struct {
// ID is the daemon's opaque identifier for this comment.
ID string `json:"id"`
// Author is the operator who wrote the comment. May be a zero
// User if the original author was deleted from the daemon (the
// comment text is preserved; Author.ID is empty in that case).
Author User `json:"author"`
// Body is the markdown source the author wrote.
Body string `json:"body"`
// BodyHTML is the rendered, sanitized HTML. Cached at write time
// and refreshed only on Edit. Empty for embedders that don't
// populate it.
BodyHTML string `json:"body_html,omitempty"`
// CreatedAt is the timestamp the comment was first persisted.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt tracks the most recent write, including edits.
UpdatedAt time.Time `json:"updated_at"`
// EditedAt is non-nil when the comment has been edited at least
// once after creation. The UI renders "edited 3m ago" when set.
EditedAt *time.Time `json:"edited_at,omitempty"`
// Source records how the comment entered compliancekit: 'ui',
// 'slack', 'teams', 'github-pr', 'jira', 'linear'. Empty string
// is treated as 'ui' for backwards compatibility.
Source string `json:"source,omitempty"`
// ExternalID is the originating sink's native id (Slack thread
// timestamp, GitHub comment id, Jira/Linear comment id) — populated
// for non-UI comments so two-way sync can dedup re-delivery.
ExternalID string `json:"external_id,omitempty"`
}
Comment is one operator-authored message attached to a Finding. Comments thread by Finding.Fingerprint() — across scans, the same fingerprint surfaces the same comment history. v1.8 introduces the type; v1.9+ may extend it (reactions, replies) within the additive- only ADR-014 contract.
Body is the markdown source as the author typed it. BodyHTML is the goldmark-rendered, sanitized HTML cached for fast list rendering; embedders that re-render markdown themselves can ignore it.
type Control ¶
type Control struct {
ID string `yaml:"-"`
Name string `yaml:"name"`
Description string `yaml:"description,omitempty"`
Family string `yaml:"family,omitempty"`
Tags []string `yaml:"tags,omitempty"`
References []string `yaml:"references,omitempty"`
}
Control is a single named requirement within a framework. The ID is the map key from YAML; bundle loaders backfill it into the struct so callers iterating Controls don't have to track the key separately.
Optional fields support framework-specific metadata that the reporter surfaces conditionally:
- Family (NIST 800-53): "AC" for Access Control, "AU" for Audit, etc.
- Tags (CIS v8): "ig1"/"ig2"/"ig3" Implementation Group; (HIPAA): "required" / "addressable" implementation specification class; (ATT&CK technique): tactic ID(s) the technique belongs to.
- References: pointer back to authoritative paragraph numbers.
type Evaluator ¶
type Evaluator interface {
// Evaluate runs all configured checks against the graph and returns
// every Finding produced. Findings of any Status are returned;
// filtering by status, severity, or check ID is the caller's
// responsibility.
Evaluate(ctx context.Context, graph *ResourceGraph) ([]Finding, error)
}
Evaluator runs a set of checks against a ResourceGraph and emits the produced Findings.
At v0.1 the only implementation is the function-backed evaluator in internal/engine: it iterates the check registry and invokes each registered CheckFunc. At v0.13 a Rego-backed evaluator joins, sourcing CheckFuncs from compiled Rego policies. The interface was shaped on day 1 so adding the second implementation requires no change to existing checks (see DECISIONS.md ADR-002).
Implementations must honor ctx.Done() -- a long-running evaluator gets canceled on user interrupt (Ctrl-C) and on engine-side timeouts.
type EvidencePtr ¶
type EvidencePtr struct {
Path string `json:"path,omitempty"`
}
EvidencePtr is a pointer to raw evidence captured during collection. The evidence pack reporter (v0.4) uses Path to copy the underlying file into the audit-ready output folder.
type Finding ¶
type Finding struct {
// CheckID identifies the originating check; matches Check.ID exactly.
CheckID string `json:"check_id"`
// Status is the evaluation outcome (pass / fail / skip / error).
Status Status `json:"status"`
// Severity is denormalized from the originating Check so reporters
// and CLI filters can act on it without joining against the catalog.
Severity Severity `json:"severity"`
// Resource identifies the resource the finding is about. A scan
// targeting account-level configuration may use a synthetic
// account resource here.
Resource ResourceRef `json:"resource"`
// Message is a short human-readable description specific to this
// finding, typically including the offending attribute value.
// Example: `bucket "assets" has acl=public-read`.
Message string `json:"message,omitempty"`
// Evidence optionally points at captured raw data for the evidence pack.
// Empty for findings derived from cross-resource logic with no single
// underlying API response.
Evidence EvidencePtr `json:"evidence,omitempty"`
// Tags propagate filter labels from the check or resource. CLI flags
// (--tags) match against this slice.
Tags []string `json:"tags,omitempty"`
// Timestamp is when the finding was produced (engine end-of-scan time).
// All findings from a single scan share the same Timestamp.
Timestamp time.Time `json:"timestamp"`
// Source records the provenance of this finding: native scan, or
// ingested from an external tool (Trivy, AWS Security Hub, OSCAL
// assessment results, …). nil for legacy findings written before
// v0.13; reporters and the evidence pack treat absence as native
// for backwards compatibility.
//
// v0.13+. Populated by either the engine (native) or an
// internal/ingest adapter (external).
Source *Source `json:"source,omitempty"`
// Vulnerability is the v0.14+ typed metadata block populated when
// the finding represents a CVE / GHSA / vendor advisory rather
// than a posture issue. Trivy / Grype / Snyk / Dependabot ingest
// adapters populate it; native checks leave it nil. Reporters
// render CVE IDs natively when present.
Vulnerability *Vulnerability `json:"vulnerability,omitempty"`
// Secret is the v0.14+ typed metadata block populated when the
// finding represents a leaked credential discovered by a secret
// scanner (gitleaks, TruffleHog). Always carries a redacted
// fingerprint, never the raw secret value — ADR-010 codifies
// this hard rule.
Secret *Secret `json:"secret,omitempty"`
// Waiver is the v0.18+ typed metadata block populated when a
// matching waiver muted this finding. Auditor-visible by
// design: the Finding flows through every reporter as
// StatusSkip with this block populated so the auditor sees
// the acknowledgement + reason + approver. A nil Waiver means
// the finding ran through the normal status machinery, NOT
// that it was waived for an unrelated reason. See ADR-013.
Waiver *WaiverRef `json:"waiver,omitempty"`
// Comments is the v1.8+ ordered conversation attached to this
// finding by Fingerprint(). Empty for CLI-only scans; populated
// only when the finding is loaded from the serve daemon or
// re-rendered through `compliancekit render --server=...`. ADR-014
// — additive, backwards-compatible.
Comments []Comment `json:"comments,omitempty"`
// Assignee is the v1.8+ operator currently accountable for the
// finding. nil when no assignee is set. Resolves to the same
// fingerprint-keyed row across scans.
Assignee *User `json:"assignee,omitempty"`
// Followers is the v1.8+ set of operators who opted in to receive
// notifications about findings on this Resource. Populated from
// the resource_follower table when the finding is loaded from the
// daemon. nil/empty for CLI-only scans.
Followers []User `json:"followers,omitempty"`
}
Finding is the atomic unit of compliancekit output: one statement about one resource from one check.
Findings are produced by check evaluators, accumulated by the engine, and consumed by reporters. A scan with N resources and M checks may produce up to N*M findings, though most carry StatusPass and are dropped by the default min_report filter.
func (Finding) Fingerprint ¶
Fingerprint returns a stable hex hash over the (check_id, resource.id, status) triple. The diff engine at v0.6+ uses this to correlate findings across scans and classify them as new / existing / resolved.
Severity, Message, Tags, Evidence, and Timestamp are deliberately excluded: a finding whose wording changes between runs should still be recognized as the same finding.
type Framework ¶
type Framework struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
Version string `yaml:"version,omitempty"`
Description string `yaml:"description,omitempty"`
URL string `yaml:"url,omitempty"`
// Category distinguishes audit-shaped compliance frameworks
// (default) from threat-model frameworks like MITRE ATT&CK.
// Empty == compliance for back-compat with v0.3-era yamls.
Category string `yaml:"category,omitempty"`
// Source cites the authoritative document the catalog was
// derived from, for auditor transparency.
Source string `yaml:"source,omitempty"`
// Tactics is populated only for threat-model frameworks
// (Category=threat_model). Ordered along the kill chain.
Tactics []Tactic `yaml:"tactics,omitempty"`
Controls map[string]Control `yaml:"controls"`
}
Framework is a compliance standard with a named set of controls.
The wire shape (YAML/JSON tags) is the v1.0 contract — frameworks loaded from third-party YAMLs or assembled in code by embedders must serialize compatibly. The bundled catalog loader lives in internal/frameworks and is not part of the public API; embedders who want their own catalog instantiate Framework values directly.
func (*Framework) IsThreatModel ¶
IsThreatModel reports whether this framework renders as an ATT&CK- style matrix rather than a control checklist.
type Package ¶
type Package struct {
Name string `json:"name"`
Version string `json:"version,omitempty"`
Ecosystem string `json:"ecosystem,omitempty"` // "apk", "deb", "pypi", "npm", "go", "maven", …
// PURL is the canonical Package URL (pkg:apk/alpine/openssl@3.0.7?...).
// Trivy and Grype both emit it; older tools may not.
PURL string `json:"purl,omitempty"`
}
Package describes one software package. Version is the installed version; FixedVersion lives on the enclosing Vulnerability so a single Package value can be reused across multiple advisories.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps check IDs to both their Check metadata and their CheckFunc implementation.
Checks register themselves at package init time via Register; the engine looks up the function at scan time, and reporters / the `checks list` command look up the metadata. The registry is the single source of truth for "which checks exist and what they are."
A test may construct an isolated *Registry; the package-level functions operate on a default global registry intended for production use.
func DefaultRegistry ¶
func DefaultRegistry() *Registry
DefaultRegistry returns the package-level registry that production checks register themselves into via init(). The engine accepts a *Registry so tests can substitute an isolated one.
func (*Registry) IDs ¶
IDs returns the registered check IDs in sorted order. Stable ordering matters for `checks list` output and for deterministic test fixtures.
func (*Registry) Register ¶
Register associates check.ID with both the metadata and the implementation function. Re-registering the same id panics: duplicate check IDs indicate a programming error (two checks claiming the same identity), not a recoverable runtime condition.
func (*Registry) Unregister ¶
Unregister removes a check from the registry. Returns true if a check was actually removed, false if id was unknown. Primarily useful in tests where a per-test Register call needs a paired cleanup so suite ordering doesn't leak state across tests. Production code should never need this — registrations happen once at init time and stay for the process lifetime.
type Reporter ¶
type Reporter interface {
// Format returns the lowercase identifier matching the config value
// (e.g. "json", "html"). The scan command compares against this.
Format() string
// Render writes findings in this reporter's format. The graph is
// passed so reporters that need raw resource detail (the evidence
// pack reporter at v0.4, primarily) can access it without forcing
// every check to denormalize attributes into Findings.
//
// Implementations must honor ctx.Done() -- a large evidence pack
// can take meaningful wall time to write.
Render(ctx context.Context, findings []Finding, graph *ResourceGraph, w io.Writer) error
}
Reporter renders a set of Findings to a writer in a specific format.
One Reporter exists per output format -- "json", "html", "sarif", "json-ocsf", "markdown", "evidence-pack" -- and lives in its own subpackage under internal/reporters/. The scan command builds the reporter set from config.Output.Format and runs each against the final findings list.
Reporters are stateless: a single Reporter value may be reused across scans. They must not mutate their Findings or graph inputs.
type ResolvedControl ¶
ResolvedControl pairs a Control with the Framework it belongs to, useful when iterating across the controls a single Check references.
type Resource ¶
type Resource struct {
// ID is globally unique across providers, e.g. "digitalocean.droplet.123456".
// Convention: "<provider>.<type>.<provider-native-id>".
ID string `json:"id"`
// Type is the resource type, e.g. "digitalocean.droplet".
// Convention: "<provider>.<resource-kind>".
Type string `json:"type"`
// Name is the human-friendly identifier, typically the name set in the
// cloud console. Surfaced in finding messages and the HTML report.
Name string `json:"name"`
// Provider is the source, e.g. "digitalocean" or "linux".
Provider string `json:"provider"`
// Region is the cloud region or, for hosts, a logical zone.
// Empty for resources without a regional concept (e.g. accounts).
Region string `json:"region,omitempty"`
// Attributes carries provider-specific fields. Access via the typed
// helpers (Attr / AttrInt / AttrBool) so scanners stay agnostic of
// how a particular value was decoded.
Attributes map[string]any `json:"attributes,omitempty"`
// Relations maps an edge name to the IDs of related resources.
// Example: a droplet may have Relations["firewall"] = []string{"do-fw-1"}.
// Collectors populate edges; scanners traverse them via ResourceGraph.Related.
Relations map[string][]string `json:"relations,omitempty"`
// Tags are arbitrary string labels assigned to the resource in the cloud.
Tags []string `json:"tags,omitempty"`
// RawPath points to the captured raw API response on disk, for the
// evidence pack to copy into the audit-ready output. Empty when the
// collector did not persist raw evidence (e.g. in tests).
RawPath string `json:"raw_path,omitempty"`
}
Resource is the typed, normalized node in the resource graph.
A Collector emits Resources; an Evaluator queries them. Edges between resources (a droplet's firewall, a bucket's CDN) live in Relations, keyed by edge name. The graph is the single source of truth during evaluation -- scanners read from it instead of re-fetching from the cloud API. See DECISIONS.md ADR-001 for the reasoning.
func (Resource) Attr ¶
Attr returns the string value of an attribute, or "" if missing or non-string. Scanners use this instead of map access so the type-switch lives in one place.
func (Resource) AttrBool ¶
AttrBool returns the bool value of an attribute, or false if missing or non-bool.
func (Resource) AttrInt ¶
AttrInt returns the int value of an attribute, or 0 if missing or non-numeric. JSON unmarshaling yields float64 for numbers, so we accept that case too.
func (Resource) Ref ¶
func (r Resource) Ref() ResourceRef
Ref returns a lightweight reference suitable for embedding in a Finding. Findings carry refs rather than full Resources so finding payloads stay small and serialize cheaply.
type ResourceGraph ¶
type ResourceGraph struct {
// contains filtered or unexported fields
}
ResourceGraph is the in-memory store of collected Resources.
Collectors call Add to register resources; Evaluators read them via ByType / ByID / Related. The graph is built once per scan and is effectively immutable from the moment evaluation begins.
ResourceGraph is NOT safe for concurrent writes -- collectors finish before evaluators start, and the engine enforces this ordering. Concurrent reads after the build phase are safe (maps are read-only at that point).
At v1.1 (`serve` mode), the in-memory graph remains the working set while a separate StateStore persists snapshots between scans.
func NewResourceGraph ¶
func NewResourceGraph() *ResourceGraph
NewResourceGraph returns an empty graph ready to be populated.
func (*ResourceGraph) Add ¶
func (g *ResourceGraph) Add(r Resource)
Add inserts a resource. If a resource with the same ID already exists, it is replaced; the type index is not duplicated.
func (*ResourceGraph) All ¶
func (g *ResourceGraph) All() []Resource
All returns every resource in the graph, ordered first by type (in insertion order of types) and then by insertion order within each type.
func (*ResourceGraph) ByID ¶
func (g *ResourceGraph) ByID(id string) (Resource, bool)
ByID returns the resource with the given ID and whether it exists.
func (*ResourceGraph) ByType ¶
func (g *ResourceGraph) ByType(t string) []Resource
ByType returns all resources of the given type, in insertion order. Stable ordering matters because findings are produced in graph order and we want diffs across runs to be readable.
func (*ResourceGraph) Count ¶
func (g *ResourceGraph) Count() int
Count returns the total number of resources in the graph.
func (*ResourceGraph) Query ¶
func (g *ResourceGraph) Query(expr string) ([]Resource, error)
Query filters the graph and returns the resources matching expr. The expression is a small DSL designed to express the "give me resources of type X with attribute Y" filters that check scanners repeat by hand today.
Supported syntax:
identifier OP value a single comparison expr AND expr both must hold expr OR expr either holds NOT expr negation ( expr ) grouping
Identifiers:
type matches Resource.Type provider matches Resource.Provider region matches Resource.Region name matches Resource.Name id matches Resource.ID tag special: matches any value in Resource.Tags <any other ident> matches Resource.Attributes[ident]
Operators:
= string or bool equality != negation of = CONTAINS substring match (strings only)
Values are double-quoted strings, the literal true / false, or bare integers. Identifiers are case-sensitive (matching the attribute map's case); operators and keywords are case-insensitive.
Example queries:
type = "digitalocean.droplet" type = "digitalocean.droplet" AND tag CONTAINS "prod" provider = "linux" AND reachable = true NOT (region = "nyc1" OR region = "nyc3")
Errors: a malformed expression returns nil and a parse error. Scanners are expected to fail loudly on parse errors -- a typo in a check's query should fail tests, not silently produce zero matches.
type ResourceRef ¶
type ResourceRef struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Provider string `json:"provider"`
Region string `json:"region,omitempty"`
AccountID string `json:"account_id,omitempty"`
}
ResourceRef is a lightweight pointer to a Resource. Findings carry refs rather than full resources to keep finding payloads small. Consumers can look up the full Resource via ResourceGraph.ByID(ref.ID).
AccountID + Region land on the Ref (rather than just on the parent Resource's Attributes) so the evidence pack's control-mapping.csv can surface them as columns without re-joining against the graph. Empty for resources without a cloud account / region concept (e.g. linux hosts).
type SearchResponse ¶ added in v1.19.0
type SearchResponse struct {
Query string `json:"query"`
Results []SearchResult `json:"results"`
NextCursor string `json:"next_cursor,omitempty"`
}
SearchResponse is the envelope the daemon's GET /api/v1/search returns: the page of results + an opaque cursor for the next page ("" when the result set is exhausted).
type SearchResult ¶ added in v1.19.0
type SearchResult struct {
Type SearchType `json:"type"`
ID string `json:"id"`
Title string `json:"title"`
Subtitle string `json:"subtitle,omitempty"`
Href string `json:"href"`
Score float64 `json:"score"`
}
SearchResult is one hit from the daemon's global search. Score is a relative rank (higher = better) combining fuzzy-match closeness with a recency weight; callers should treat it as an ordering hint, not an absolute measure. Href is the in-app path that opens the entity.
type SearchType ¶ added in v1.19.0
type SearchType string
SearchType enumerates the kinds of entities the daemon's global search index can return. v1.19 phase 5 — added to the public surface (ADR-014 additive) so external embedders + the daemon's API share one result shape.
const ( SearchTypeFinding SearchType = "finding" SearchTypeResource SearchType = "resource" SearchTypeScan SearchType = "scan" SearchTypeUser SearchType = "user" SearchTypeWaiver SearchType = "waiver" SearchTypeSetting SearchType = "setting" SearchTypeDoc SearchType = "doc" )
type Secret ¶
type Secret struct {
// RuleID is the producing scanner's rule identifier
// ("aws-access-key-id", "github-pat", "stripe-secret-key", …).
RuleID string `json:"rule_id"`
// RuleName is the human-readable rule description.
RuleName string `json:"rule_name,omitempty"`
// Fingerprint is a redacted derivation of the secret — typically
// the first 4 + last 4 characters of the captured string with the
// middle replaced by "..." (e.g. "AKIA...A2X9"). Adapters MUST
// NOT emit the full secret value. The full secret never leaves
// the producing tool's report; we read only the fingerprint.
Fingerprint string `json:"fingerprint,omitempty"`
// File / Line locate the secret within the scanned repo.
File string `json:"file,omitempty"`
Line int `json:"line,omitempty"`
// Commit (optional) is the git commit SHA where the secret was
// introduced; gitleaks ships this when it scans the full history.
Commit string `json:"commit,omitempty"`
// Author / Email / Date describe the commit metadata the secret
// was introduced in. Useful for revocation routing.
Author string `json:"author,omitempty"`
Email string `json:"email,omitempty"`
Date string `json:"date,omitempty"`
}
Secret is the v0.14+ typed metadata block carried on Findings produced by a secret scanner (gitleaks, TruffleHog). Reporters render the secret's rule name + file location; the actual secret value is NEVER stored — ADR-010 codifies this — only a truncated fingerprint that lets operators correlate findings without leaking the credential into logs.
type Severity ¶
type Severity int
Severity classifies the impact of a finding.
Ordering is meaningful: a Severity value compares correctly with the usual relational operators. The CLI severity filter (--fail-on=high) relies on this ordering, so adding a new level requires preserving the ascending-impact order.
const ( // SeverityUnknown is the zero value. It signals "not set yet" rather // than a real severity; checks must populate a real level. SeverityUnknown Severity = iota SeverityInfo // observation, no action required SeverityLow // hygiene, recommended setting SeverityMedium // best-practice gap, hardening miss SeverityHigh // meaningful exposure, audit-failing SeverityCritical // exploitable now, data at immediate risk )
func ParseSeverity ¶
ParseSeverity is the inverse of String. It is case-insensitive and tolerates surrounding whitespace, so values from CLI flags or YAML parse without preprocessing.
func (Severity) MarshalJSON ¶
MarshalJSON emits the canonical lowercase string. Findings are routinely serialized to JSON for CI consumption, so this matters for stability.
func (*Severity) UnmarshalJSON ¶
UnmarshalJSON accepts the canonical lowercase string and rejects unknown values.
type Source ¶
type Source struct {
// Type is "native" for engine-produced findings or "ingest" for
// findings projected from an external tool's output.
Type string `json:"type"`
// Tool identifies the producer when Type=="ingest", e.g. "trivy",
// "checkov", "aws-security-hub", "gcp-scc", "defender". Empty
// when Type=="native".
Tool string `json:"tool,omitempty"`
// ToolVersion (optional) records the producing tool's version,
// e.g. "v0.50.2". Useful for audit-trail reproducibility.
ToolVersion string `json:"tool_version,omitempty"`
// Format names the wire format the finding was decoded from
// ("sarif", "ocsf", "oscal-ar"). Empty for native findings.
Format string `json:"format,omitempty"`
// File (optional) is the path of the source file the ingest
// adapter read. Aids reproducibility but never relied on for
// correctness — the finding is fully described without it.
File string `json:"file,omitempty"`
}
Source describes where a Finding came from. Native findings produced by the scan engine set Type="native" and leave Tool/ToolVersion/Format empty. Findings produced by an internal/ingest adapter set Type="ingest" plus the tool identifier and the wire format that carried them in.
Source travels with the finding through every reporter, the diff engine, and the evidence pack — operators and auditors can see "this control is flagged by both compliancekit's native check and Trivy v0.50.2" without losing either side of the trail.
type Status ¶
type Status string
Status is the outcome of evaluating a check against a resource.
A single check can produce findings of mixed status — for a graph of 10 buckets a Spaces check might emit 7 StatusPass, 2 StatusFail, and 1 StatusSkip. Only StatusFail and StatusError count against severity gates; StatusPass and StatusSkip are recorded for evidence-pack completeness but never cause a non-zero exit code.
const ( // StatusPass means the resource is compliant with the check. StatusPass Status = "pass" // StatusFail means the resource is non-compliant with the check. StatusFail Status = "fail" // StatusSkip means the check did not apply (resource type mismatch, // feature not enabled, etc.). Skipped checks still appear in the // evidence pack so an auditor sees the full coverage matrix. StatusSkip Status = "skip" // StatusError means the check could not be evaluated due to missing // or ambiguous data. An error is not the same as a failing resource; // it signals "we don't know" rather than "we know it's bad." StatusError Status = "error" )
func ParseStatus ¶
ParseStatus parses the textual status name (case-insensitive) produced by external callers — Rego policies, ingest adapters, the CLI's --status flag. Returns the typed Status or an error listing the four valid values.
Unlike Severity which uses an int enum, Status is already a typed string so this helper is mostly an "unknown value" guard.
func (Status) IsActionable ¶
IsActionable returns true for statuses that warrant operator attention. The CLI uses this to decide whether a finding contributes to the --fail-on threshold.
type Tactic ¶
type Tactic struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
Description string `yaml:"description,omitempty"`
Techniques []string `yaml:"techniques,omitempty"`
}
Tactic is a phase in a threat-model kill chain (MITRE ATT&CK uses "Initial Access", "Execution", ..., "Impact"). Each tactic has an ID like TA0001 and references the techniques mapped to it. Tactics are ignored for compliance-category frameworks.
type User ¶ added in v1.8.0
type User struct {
// ID is the daemon's internal opaque user id. Embedders that need
// to correlate against an external IdP should join on Email.
ID string `json:"id"`
// Email is the user's primary contact address. Required.
Email string `json:"email"`
// DisplayName is the operator-visible label ("Alex Lee"). Falls
// back to the local-part of Email when unset.
DisplayName string `json:"display_name,omitempty"`
}
User is the v1.8+ identity reference exposed on Finding for collaboration metadata. It is a deliberately thin shape — just enough for a Finding consumer to render "who commented" or "who is assigned" without forcing embedders to model an entire auth system.
User values flow through reporters as part of Finding.Comments, Finding.Assignee, and Finding.Followers when the finding is loaded from the serve daemon. CLI-only embedders who never run the daemon will see zero-value users (the embedder may still construct them directly to plumb attribution through their own pipeline).
Added in v1.8 alongside per-finding markdown comments and resource ownership. ADR-014 — additive-only changes preserve the v1.x API contract.
type Vulnerability ¶
type Vulnerability struct {
// ID is the canonical advisory identifier. CVE IDs ("CVE-2026-XXXXX")
// are preferred; GHSA IDs are accepted when the upstream tool only
// emits those. Other vendor-specific IDs flow into Aliases.
ID string `json:"id"`
// Aliases lists any equivalent identifiers the producing tool
// reported alongside ID (RHSA, USN, GHSA when ID is CVE,
// vendor-specific tickets).
Aliases []string `json:"aliases,omitempty"`
// CVSSScore is the CVSS base score (0.0-10.0) the producing tool
// attributed to the advisory. Zero means "unknown" — reporters
// should fall through to compliancekit's severity scale rather
// than rendering "0.0".
CVSSScore float64 `json:"cvss_score,omitempty"`
// CVSSVector is the full vector string ("CVSS:3.1/AV:N/AC:L/...")
// when the tool ships it. Useful for downstream consumers that
// want to re-score with different temporal/environmental modifiers.
CVSSVector string `json:"cvss_vector,omitempty"`
// Package identifies the vulnerable package: name, version,
// ecosystem, and the canonical PURL (Package URL) where known.
Package Package `json:"package,omitempty"`
// FixedVersion is the smallest version that addresses the CVE.
// Empty when no fix is yet available; reporters render "unpatched"
// or similar in that case.
FixedVersion string `json:"fixed_version,omitempty"`
// Description is a short prose summary of the advisory, typically
// 1-2 sentences. Used as the Finding.Message when the producing
// adapter doesn't synthesize one.
Description string `json:"description,omitempty"`
// PrimaryURL is the canonical URL pointing at the upstream
// advisory (NVD / GHSA / vendor advisory page). Reporters render
// it as a clickable reference.
PrimaryURL string `json:"primary_url,omitempty"`
// PublishedDate / LastModifiedDate are the advisory dates as
// reported by the producing tool. Useful for age-based filters
// ("CVEs published in the last 30 days"). Stored as RFC3339
// strings rather than time.Time to preserve whatever precision
// the tool emitted (some tools omit time-of-day).
PublishedDate string `json:"published_date,omitempty"`
LastModifiedDate string `json:"last_modified_date,omitempty"`
// Image identifies the container image when the finding came from
// an image scan (Trivy image, Grype container, Defender image
// scan). Empty for filesystem / package scans.
Image string `json:"image,omitempty"`
}
Vulnerability is the v0.14+ typed metadata block carried on Findings that originated from a vulnerability scanner (Trivy / Grype / Snyk / Dependabot / GHSA). Reporters render CVE IDs natively; the evidence pack's vulnerabilities.csv pivots on this block to produce one row per (CVE, resource) pair.
Per ADR-009 (vulnerability scanning is composed, not native), compliancekit never produces these blocks from its own database; every Vulnerability arrives via the v0.14 ingest pipeline.
type WaiverRef ¶
type WaiverRef struct {
// CheckID is the catalog ID the waiver matched on. Always
// non-empty; helps round-tripping when a reporter drops the
// surrounding Finding.CheckID for some reason.
CheckID string `json:"check_id"`
// ResourceID is the resource the waiver matched. Always
// non-empty; the matcher requires both CheckID + ResourceID
// in v0.18 (broader scopes deferred — see ADR-013).
ResourceID string `json:"resource_id"`
// Reason is the operator-supplied justification. Required by
// the loader; an empty Reason fails waiver validation. The
// auditor reads this to understand WHY the deviation is
// acceptable.
Reason string `json:"reason"`
// Approver is the human who signed off. Required by the loader.
// Convention: email address or "@github-username"; the auditor
// uses this to chase up if context is needed.
Approver string `json:"approver"`
// Expires is the date the waiver lapses. Expired waivers stop
// muting and emit their own info-level finding ("waiver X
// expired N days ago") so the auditor sees the lapse instead
// of silent re-coverage. UTC date; loader parses YYYY-MM-DD.
Expires time.Time `json:"expires"`
// Source identifies where the waiver was declared. Three values
// at v0.18: "file" (waivers.yaml entry), "annotation" (in-code
// // compliancekit:waive ... comment), or "" (legacy / unknown).
Source string `json:"source,omitempty"`
// SourcePath optionally points at the file the waiver was
// declared in. waivers.yaml path for Source=="file"; the
// annotated source file for Source=="annotation".
SourcePath string `json:"source_path,omitempty"`
}
WaiverRef is the v0.18+ typed metadata block populated on core.Finding when a matching waiver muted the underlying check. Lives in core (not internal/waivers) so adding it to Finding does not create an import cycle — same pattern as Vulnerability + Secret.
Auditor-visible by design: a waived finding is NOT hidden, it flows through every reporter (markdown, sarif, ocsf, html, evidence pack) as StatusSkip with this block populated so the auditor sees the waiver acknowledgement plus the reason + approver that justified it.
func (*WaiverRef) DaysUntilExpiry ¶
DaysUntilExpiry returns the number of whole days between t and the waiver's Expires date. Negative when expired. Used by the expiry-warning pipeline to surface "expiring in 30 days" alerts.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package plugin is the v1.13+ public surface for compliancekit plugins.
|
Package plugin is the v1.13+ public surface for compliancekit plugins. |
|
Package rbac is the v1.12+ public surface for the daemon's role-based access control model.
|
Package rbac is the v1.12+ public surface for the daemon's role-based access control model. |
|
Package rules is the v1.9+ public surface for the workflow automation rules engine.
|
Package rules is the v1.9+ public surface for the workflow automation rules engine. |