opa

package
v0.4.61 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2026 License: MPL-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package opa wraps the Open Policy Agent runtime for Plumber's rule engine. Each policy is a Rego module evaluated against an ir.NormalizedPipeline and emits violations through the shared "deny" rule.

This is the Phase 0 scaffold: it can load in-memory modules and return findings. Embedded policy discovery, user-policy overrides, and reporter integration land in later phases.

Index

Constants

This section is empty.

Variables

View Source
var FindingsObserver func([]Finding)

FindingsObserver, when non-nil, receives every finding slice Evaluate returns. It exists for the identity harness in policies_test, which must see every emission the test suite produces to prove each control keeps emitting the fields its declared identity depends on. Production code never sets it; the harness sets it in TestMain before m.Run and clears it after, so the write happens-before every read and no lock is needed. It is a settable var rather than a Set* function so the whole-program deadcode gate (make deadcode) does not read a test-only setter as unreachable.

Functions

func StampFingerprints added in v0.4.29

func StampFingerprints(findings []Finding, root string)

StampFingerprints sets Fingerprint on every finding in place. Along the way it also rewrites File in place to a repository-relative path (see repoRelative), because the fingerprint is computed from the (possibly just-rewritten) File — so both mutations are part of this call's contract, not just the one named in the function's own name. Call it once, after findings are finalized, so all output writers read the same File and Fingerprint values.

root is the repository root absolute File paths should be relativized against. Pass the collector's own root (e.g. conf.GitRepoRoot) rather than the process's current working directory: a collector that walks a configured repo root (the GitHub collector walks conf.GitRepoRoot) keeps recording paths relative to that root regardless of which subdirectory Plumber was invoked from, and relativizing against os.Getwd() instead would make the fingerprint depend on the invocation directory again — reopening the machine-dependence problem this function exists to close. When root is unknown, pass "" and StampFingerprints falls back to os.Getwd().

Types

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine evaluates Rego policies against an IR pipeline.

func New

func New() *Engine

New returns an Engine with no policies loaded.

func (*Engine) Evaluate

func (e *Engine) Evaluate(ctx context.Context, pipeline *ir.NormalizedPipeline, config map[string]any) ([]Finding, error)

Evaluate runs every loaded policy against pipeline and returns the aggregated findings. Policies see a two-field input:

input.pipeline  — the NormalizedPipeline
input.config    — an arbitrary map forwarded from .plumber.yaml

config may be nil. Pipeline must not be nil.

func (*Engine) LoadFromFSFiltered

func (e *Engine) LoadFromFSFiltered(fsys fs.FS, skip func(filename string, content []byte) bool) error

LoadFromFSFiltered loads every .rego file at the root of fsys. The module's logical name is the file's base name without its extension. Nested subdirectories are ignored.

skip is an optional predicate: when it is non-nil and returns true for a (filename, content) pair, that file is excluded from the engine — it never executes, never produces findings, never costs evaluation time. Used to gate dev-side benched policies out of production runs without touching the policy files themselves. Pass nil to load everything.

func (*Engine) LoadModule

func (e *Engine) LoadModule(name, source string)

LoadModule registers a Rego module under the given logical name. The name must match the module's package path (the "deny" rule is queried at data.<name>.deny).

type Finding

type Finding struct {
	Code     string `json:"-"`
	Severity string `json:"-"`
	Message  string `json:"-"`
	Job      string `json:"-"`
	File     string `json:"-"`
	Line     int    `json:"-"`
	// URL is a clickable pointer to the offending file/line, populated at
	// output time (not by Rego). In CI it is the remote blob URL on the
	// host forge; locally it is the absolute filesystem path with the
	// :line suffix that VS Code / iTerm recognise as a source reference.
	// Empty when no useful link can be built (missing file/line, etc.).
	URL string `json:"-"`
	// Fingerprint is a stable, line-independent identifier for this finding,
	// stamped once by StampFingerprints so every output format carries the same
	// value and a consumer can track the same finding across runs even as line
	// numbers drift. Empty until stamped, and for codeless findings.
	Fingerprint string `json:"-"`
	// Dismissed is set by control.MarkDismissed when the platform served this finding's identity
	// as dismissed (#447); never emitted by MarshalJSON (the platform hashes that object) and
	// never a status: pass|fail|not_evaluable is frozen.
	Dismissed bool `json:"-"`
	// Policies names the platform policies that reported this finding, set
	// only on the deduplicated union the security-report writers (SARIF, the
	// GitLab SAST report) build for a platform-mode run: one alert per
	// finding, tagged with every policy it belongs to. Empty on every
	// evaluated finding, and never serialised - MarshalJSON below enumerates
	// the fields it emits, so the finding object the platform hashes into an
	// identity (#467) is untouched by this.
	Policies []string       `json:"-"`
	Data     map[string]any `json:"-"`
}

Finding is a single rule violation emitted by a policy. File and Line, when populated, point at the exact location of the offending job in the source workflow/pipeline file so editors and terminals can render a clickable file:line link.

Data carries policy-specific structured payload (variable name, affected image link, location, …) emitted by the Rego rule next to the canonical fields. It serialises inline at the top level so downstream consumers can read both the human message and the machine-parseable evidence on the same finding object.

func (Finding) Identity added in v0.4.34

func (f Finding) Identity() (identity.Fields, bool)

Identity returns the finding's identity field set: the fields the shared recipe selects as identifying this one finding instance across runs, as data. ok is false for a codeless finding, which has no identity.

The selection lives in finding/identity because Plumber is not its only consumer: a platform grouping findings into long-lived issues needs the same answer, and a second list maintained alongside this one would drift.

func (Finding) IdentityInput added in v0.4.59

func (f Finding) IdentityInput() identity.Finding

IdentityInput is the exported form of identityInput, for a caller outside this package that needs the identity recipe's own view of the finding: the control package hashes it against the platform's served dismissed_issues (#447) without duplicating the field selection.

func (Finding) MarshalJSON

func (f Finding) MarshalJSON() ([]byte, error)

MarshalJSON flattens the canonical fields and the Data payload into a single object so structured keys appear at the top level (the shape pre-Rego consumers parsed). Empty canonical fields are omitted, mirroring the previous `omitempty` tags.

func (*Finding) UnmarshalJSON

func (f *Finding) UnmarshalJSON(b []byte) error

UnmarshalJSON splits an incoming flat object into the canonical fields and the Data bag. Unknown keys land in Data so they survive a round-trip even when added by future rules.

Jump to

Keyboard shortcuts

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