policy

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 13, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package policy defines DevProof's verification result model and, in later phases, the verification policy document and its evaluator.

The central idea here is that verification answers three separate questions, and collapsing them into one boolean is what makes supply-chain tooling misleading. An artifact can be perfectly intact and signed by someone you have never heard of; it can be signed by exactly the right identity and contain nonsense. Each dimension is reported on its own terms.

Index

Constants

View Source
const (
	FindingIntegrityFailed   = "integrity-failed"
	FindingDigestMismatch    = "digest-mismatch"
	FindingInvalidArtifact   = "invalid-artifact"
	FindingFormatNotAllowed  = "format-not-allowed"
	FindingResourceLimit     = "resource-limit-exceeded"
	FindingPolicyNotSupplied = "policy-not-supplied"

	FindingDigestReferenceRequired = "digest-reference-required"
	FindingSignatureThreshold      = "signature-threshold-not-met"
	FindingSignerNotAllowed        = "signer-identity-not-allowed"
	FindingTransparencyProof       = "transparency-proof-required"
	FindingProvenanceRequired      = "provenance-required"
	// FindingProvenanceInconsistent marks a signed statement whose claims
	// contradict what verification established. A signature proves authorship,
	// not truth.
	FindingProvenanceInconsistent  = "provenance-inconsistent"
	FindingPredicateNotAllowed     = "predicate-not-allowed"
	FindingBuilderNotAllowed       = "builder-not-allowed"
	FindingLockDigestRequired      = "lock-digest-required"
	FindingSourceTypeNotAllowed    = "source-type-not-allowed"
	FindingSourceHostNotAllowed    = "source-host-not-allowed"
	FindingImmutableResolution     = "immutable-resolution-required"
	FindingSubjectMismatch         = "evidence-subject-mismatch"
	FindingEvidenceExpired         = "evidence-expired"
	FindingMatchingEvidenceInvalid = "matching-evidence-invalid"
	FindingIgnoredEvidence         = "evidence-ignored"
	FindingTagFallbackNotAllowed   = "evidence-tag-fallback-not-allowed"
)

Stable finding codes.

Codes are a compatibility surface; messages are not. A caller branches on these, and a message may be reworded at any time.

Variables

This section is empty.

Functions

This section is empty.

Types

type AcceptedEvidence added in v0.4.0

type AcceptedEvidence struct {
	// Digest identifies the evidence object.
	Digest string `json:"digest"`
	// PredicateType is what the statement claims to be.
	PredicateType string `json:"predicateType,omitempty"`
	// TransparencyLogVerified reports whether an inclusion proof was checked.
	TransparencyLogVerified bool `json:"transparencyLogVerified,omitempty"`
	// IntegratedTime is the authenticated signing time, when one exists.
	IntegratedTime string `json:"integratedTime,omitempty"`
	// ViaTagFallback reports that the evidence was found under the fallback
	// tag scheme, which cannot express a set (DP-028).
	ViaTagFallback bool `json:"viaTagFallback,omitempty"`
}

AcceptedEvidence is one verified statement the result relied on.

type Document

type Document struct {
	APIVersion string   `json:"apiVersion" yaml:"apiVersion"`
	Kind       string   `json:"kind" yaml:"kind"`
	Metadata   Metadata `json:"metadata" yaml:"metadata"`
	Spec       Spec     `json:"spec" yaml:"spec"`
}

Document is a verification policy.

A policy is trusted configuration supplied by the caller. It is never read from the artifact being evaluated, and nothing in an artifact can change how it is interpreted — otherwise an attacker who controls a bundle would control the rules it is judged by.

func ParseDocument

func ParseDocument(data []byte) (*Document, error)

ParseDocument decodes and validates a policy from YAML or JSON.

Decoding is strict. An unknown field in a policy is the dangerous case: a rule this build does not understand is a rule it would not enforce, and silently ignoring one turns a strict policy into a permissive one.

func (*Document) EffectiveThreshold

func (d *Document) EffectiveThreshold() int

EffectiveThreshold returns how many distinct identities are required.

func (*Document) RequiresSignatures

func (d *Document) RequiresSignatures() bool

RequiresSignatures reports whether the policy demands any signature.

func (*Document) Validate

func (d *Document) Validate() error

Validate checks a policy's structure and rejects contradictions.

type EvidenceRules

type EvidenceRules struct {
	// RejectInvalidMatchingEvidence makes malformed evidence that claims a
	// required type fatal on its own.
	//
	// A repository is an open attachment surface: anyone with write access
	// can attach anything. With this false, junk is ignored and the policy
	// still fails if what remains cannot satisfy it. With it true, the
	// presence of malformed evidence is itself a signal worth failing on.
	RejectInvalidMatchingEvidence bool `json:"rejectInvalidMatchingEvidence,omitempty" yaml:"rejectInvalidMatchingEvidence,omitempty"`
	// AllowTagFallback permits evidence stored under the fallback tag scheme
	// on registries without the referrers API (DP-028). Off by default,
	// because that mode cannot express a set and silently replaces.
	AllowTagFallback bool `json:"allowTagFallback,omitempty" yaml:"allowTagFallback,omitempty"`
	// MaxAge bounds how old accepted evidence may be, as a Go duration such
	// as "720h".
	//
	// Measured from an authenticated signing time, never from something the
	// evidence asserts about itself. Evidence with no authenticated time
	// cannot satisfy this rule and is refused rather than waved through: a
	// missing trusted time source is exactly the case an age rule exists to
	// catch, and treating "unknown" as "recent" would make the rule useless
	// against the only adversary who cares about it.
	//
	// Unset means age is not considered. A signature does not expire on its
	// own, and for a build attestation that is often right -- this is for the
	// facts whose truth decays even though their bytes do not.
	MaxAge string `json:"maxAge,omitempty" yaml:"maxAge,omitempty"`
}

EvidenceRules govern how candidate evidence is treated.

type Finding

type Finding struct {
	Code     string   `json:"code"`
	Rule     string   `json:"rule,omitempty"`
	Severity Severity `json:"severity"`
	Subject  string   `json:"subject,omitempty"`
	Message  string   `json:"message"`
}

Finding is one thing verification observed.

Code is a stable machine identifier and Message is not. Callers branch on the code; the message is for the human deciding what to do.

func (Finding) String

func (f Finding) String() string

type IdentityRule

type IdentityRule struct {
	// KeyID accepts a bare public key by identifier.
	KeyID string `json:"keyId,omitempty" yaml:"keyId,omitempty"`
	// Issuer is the OIDC issuer, matched exactly.
	Issuer string `json:"issuer,omitempty" yaml:"issuer,omitempty"`
	// Subject is the OIDC subject, matched exactly.
	Subject string `json:"subject,omitempty" yaml:"subject,omitempty"`
	// SubjectPattern is an anchored regular expression alternative to
	// Subject. It is compiled at load time so a malformed pattern fails
	// where it can be fixed rather than mid-verification.
	SubjectPattern string `json:"subjectPattern,omitempty" yaml:"subjectPattern,omitempty"`
	// contains filtered or unexported fields
}

IdentityRule matches one accepted signer.

Exactly one form may be given. A rule that named both a key and an OIDC identity would be ambiguous about which had to match, and "either" is spelled by writing two rules.

func (*IdentityRule) Matches

func (r *IdentityRule) Matches(keyID, issuer, subject string) bool

Matches reports whether a verified identity satisfies this rule.

The identity passed here must already have been established cryptographically. A rule never sees an envelope's self-declared fields.

type Input

type Input struct {
	// SubjectDigest is what is being verified.
	SubjectDigest string
	// TreeDigest is the payload identity that was recomputed while verifying.
	//
	// Present so that provenance claiming a different payload can be caught.
	// A statement bound to this subject can still describe a tree that is not
	// this one, and a signature says who wrote that claim rather than whether
	// it is true.
	TreeDigest string
	// Format is the bundle format that was read.
	Format bundle.Format
	// SuppliedDigestReference reports whether the caller named a digest
	// rather than a tag.
	SuppliedDigestReference bool
	// FileCount and TotalBytes describe the payload.
	FileCount  int64
	TotalBytes int64

	// Evidence is the cryptographically verified evidence. Candidates that
	// failed verification are not here; they are in Rejected.
	Evidence []VerifiedEvidence
	// Rejected are candidates that did not verify.
	Rejected []RejectedEvidence

	// EvaluatedAt is the single time used by every time-dependent rule,
	// captured once at the start of verification and recorded in the result.
	EvaluatedAt time.Time
}

Input is everything an evaluation considers.

type Limit added in v0.2.0

type Limit struct {
	// Name is the bound's stable name, matching the policy field that sets
	// it.
	Name string `json:"name"`
	// Value is the effective bound: the smallest anybody asked for.
	Value int64 `json:"value"`
	// Origin is which input supplied that value: default, client, request,
	// or policy.
	Origin string `json:"origin"`
}

Limit is one resource bound as it applied to a verification.

type Metadata

type Metadata struct {
	Name string `json:"name" yaml:"name"`
}

Metadata names a policy for diagnostics.

type ProvenanceRules

type ProvenanceRules struct {
	// Required demands provenance evidence.
	Required bool `json:"required,omitempty" yaml:"required,omitempty"`
	// PredicateTypes restricts acceptable predicate types.
	PredicateTypes []string `json:"predicateTypes,omitempty" yaml:"predicateTypes,omitempty"`
	// RequireLockDigest demands that provenance records a lock digest, which
	// is what makes a build's inputs auditable after the fact.
	RequireLockDigest bool `json:"requireLockDigest,omitempty" yaml:"requireLockDigest,omitempty"`
	// AllowedBuilders restricts the builder identity.
	AllowedBuilders []string `json:"allowedBuilders,omitempty" yaml:"allowedBuilders,omitempty"`
	// Sources constrains where material came from.
	Sources SourceRules `json:"sources,omitzero" yaml:"sources,omitempty"`
}

ProvenanceRules constrain what the evidence says.

type RejectedEvidence

type RejectedEvidence struct {
	Digest string
	Reason string
	// MatchedRequiredType reports whether the candidate claimed a type the
	// policy requires. Junk of an unrelated type is noise; junk claiming to
	// be the thing you asked for may be an attack.
	MatchedRequiredType bool
}

RejectedEvidence is a candidate that did not become evidence.

Rejected candidates are reported separately rather than merged into the findings, so that "the policy was not satisfied" and "somebody attached junk to this repository" remain distinguishable.

type Report

type Report struct {
	// Integrity reports whether the artifact's own parts agree. It is always
	// evaluated; there is no flag that disables it.
	Integrity Status `json:"integrity"`
	// Trust reports whether evidence satisfied the supplied policy.
	Trust Status `json:"trust"`
	// Semantics reports whether a caller-supplied validator accepted the
	// payload.
	Semantics Status `json:"semantics"`

	// SubjectDigest identifies what was verified. A result always names its
	// subject by digest, never by the tag it may have been reached through
	// (DP-007).
	SubjectDigest string `json:"subjectDigest"`
	// TreeDigest identifies the payload independently of its encoding.
	TreeDigest string `json:"treeDigest,omitempty"`
	// Format is the bundle format version that was read.
	Format string `json:"format,omitempty"`

	FileCount  int64 `json:"fileCount"`
	TotalBytes int64 `json:"totalBytes"`

	// AcceptedIdentities are the signers whose signatures verified and whose
	// identities a policy rule accepted. A report records who was believed,
	// not merely that somebody was.
	AcceptedIdentities []string `json:"acceptedIdentities,omitempty"`
	// PolicyDigest identifies the policy that was applied, so a result can
	// be re-checked against the rules that produced it.
	PolicyDigest string `json:"policyDigest,omitempty"`
	// PolicyName is the policy's metadata name, for diagnostics.
	PolicyName string `json:"policyName,omitempty"`
	// EvaluatedAt is the single time every time-dependent rule used.
	EvaluatedAt string `json:"evaluatedAt,omitempty"`
	// EvidenceStorage reports how evidence was found: "referrers" or
	// "tag-fallback". The fallback cannot express a set, so a consumer needs
	// to know which mode produced the answer (DP-028).
	EvidenceStorage string `json:"evidenceStorage,omitempty"`
	// AcceptedEvidence describes the evidence the conclusion rests on.
	//
	// Recording who signed is not enough. Two statements from the same trusted
	// signer can say entirely different things about an artifact, and a report
	// that named the signer but not the statement could not answer "what was
	// this verified against" after the fact.
	AcceptedEvidence []AcceptedEvidence `json:"acceptedEvidence,omitempty"`
	// RejectedEvidence summarizes candidates that did not verify, kept
	// separate from findings so "the policy was not satisfied" and "somebody
	// attached junk" stay distinguishable.
	RejectedEvidence []string `json:"rejectedEvidence,omitempty"`
	// TrustRoots identifies the trust material by digest.
	//
	// The same policy reaches different conclusions under different roots, so
	// a result that named only the policy did not identify the rules that
	// produced it. Digests rather than paths: a path is a fact about one
	// machine, and trust material is not a secret but its location can be.
	TrustRoots []string `json:"trustRoots,omitempty"`

	// Limits records the bounds this verification actually ran under and
	// where each came from (DP-021).
	//
	// A limit is only meaningful if a consumer can tell which one applied. A
	// policy that tightened maxExpandedBytes and a client that did are the
	// same number in the result and very different facts about who decided
	// it, and "the policy asked for a bound nothing applied" was previously
	// indistinguishable from "the policy's bound held".
	Limits []Limit `json:"limits,omitempty"`

	Findings []Finding `json:"findings,omitempty"`
}

Report is the outcome of verifying one subject: the DevProof proof report.

It records what was checked, not merely whether it passed, so that a consumer can tell a verification that proved a great deal from one that proved very little.

func Evaluate

func Evaluate(doc *Document, input Input) *Report

Evaluate applies a policy to verified facts.

It reports findings rather than stopping at the first failure, because a consumer fixing a policy mismatch wants to know everything that is wrong, not to discover it one run at a time.

func (*Report) AddFinding

func (r *Report) AddFinding(f Finding)

AddFinding appends a finding.

func (*Report) HasErrors

func (r *Report) HasErrors() bool

HasErrors reports whether any finding is an error.

func (*Report) OK

func (r *Report) OK() bool

OK reports whether the operation should be considered successful.

Integrity must pass, and any dimension that was evaluated must pass. A dimension that was not evaluated cannot fail the result — but it cannot satisfy a requirement either, which is why callers that need trust must ask for it explicitly rather than reading OK.

type Severity

type Severity string

Severity classifies a finding.

const (
	// SeverityError means the policy was not satisfied. One is enough to
	// fail a report.
	SeverityError Severity = "error"
	// SeverityWarning is worth reporting but does not fail verification, such
	// as evidence that was ignored because nothing required it.
	SeverityWarning Severity = "warning"
)

type SignatureRules

type SignatureRules struct {
	// Threshold is the number of distinct accepted identities required.
	// Zero with no identities means signatures are not required.
	Threshold int `json:"threshold,omitempty" yaml:"threshold,omitempty"`
	// Identities are the accepted signers. Any one of them satisfies a
	// single unit of the threshold.
	Identities []IdentityRule `json:"identities,omitempty" yaml:"identities,omitempty"`
	// RequireTransparencyLog demands a proven transparency-log inclusion.
	RequireTransparencyLog bool `json:"requireTransparencyLog,omitempty" yaml:"requireTransparencyLog,omitempty"`
}

SignatureRules constrain who signed.

type SourceRules

type SourceRules struct {
	// AllowedTypes restricts source types, such as "git" or "path".
	AllowedTypes []string `json:"allowedTypes,omitempty" yaml:"allowedTypes,omitempty"`
	// AllowedHosts restricts the hosts remote sources may come from.
	AllowedHosts []string `json:"allowedHosts,omitempty" yaml:"allowedHosts,omitempty"`
	// RequireImmutableResolution demands that every source resolved to
	// something immutable.
	RequireImmutableResolution bool `json:"requireImmutableResolution,omitempty" yaml:"requireImmutableResolution,omitempty"`
}

SourceRules constrain a build's inputs.

These apply to what the provenance claims, not to the payload's identity. Two attestations may truthfully describe different source histories for one subject; policy chooses which history it is willing to trust.

type Spec

type Spec struct {
	Subject    SubjectRules    `json:"subject,omitzero" yaml:"subject,omitempty"`
	Signatures SignatureRules  `json:"signatures,omitzero" yaml:"signatures,omitempty"`
	Provenance ProvenanceRules `json:"provenance,omitzero" yaml:"provenance,omitempty"`
	Evidence   EvidenceRules   `json:"evidence,omitzero" yaml:"evidence,omitempty"`
	Limits     bundle.Limits   `json:"limits,omitzero" yaml:"limits,omitempty"`
}

Spec is a policy's rules.

type Status

type Status string

Status is the outcome of one verification dimension.

const (
	// StatusPass means the dimension was evaluated and satisfied.
	StatusPass Status = "pass"
	// StatusFail means the dimension was evaluated and not satisfied.
	StatusFail Status = "fail"
	// StatusNotEvaluated means the dimension was not assessed.
	//
	// It is deliberately not a synonym for pass. "No policy was supplied, so
	// nothing was checked" and "the policy was satisfied" are different
	// facts, and a consumer that cannot tell them apart has no way to notice
	// that its trust configuration never took effect (DP-010).
	StatusNotEvaluated Status = "not-evaluated"
)

func (Status) String

func (s Status) String() string

type SubjectRules

type SubjectRules struct {
	// RequireDigestReference rejects a tag before anything is fetched.
	RequireDigestReference bool `json:"requireDigestReference,omitempty" yaml:"requireDigestReference,omitempty"`
	// AllowedFormats restricts which bundle format versions are acceptable.
	// Empty means any supported format.
	AllowedFormats []string `json:"allowedFormats,omitempty" yaml:"allowedFormats,omitempty"`
	// MaxFiles and MaxExpandedBytes bound the payload. Zero means the
	// effective limits apply unchanged.
	MaxFiles         int64 `json:"maxFiles,omitempty" yaml:"maxFiles,omitempty"`
	MaxExpandedBytes int64 `json:"maxExpandedBytes,omitempty" yaml:"maxExpandedBytes,omitempty"`
}

SubjectRules constrain the artifact itself.

type VerifiedEvidence

type VerifiedEvidence struct {
	// Descriptor identifies the evidence blob.
	Digest string
	// Statement is the verified statement.
	Statement *evidence.Statement
	// Identities are the signers whose signatures verified.
	Identities []evidence.Identity
	// TransparencyLogVerified reports a proven log inclusion.
	TransparencyLogVerified bool
	// IntegratedTime is an authenticated signing time, when one exists.
	IntegratedTime *time.Time
	// ViaTagFallback reports that this evidence was found under the
	// fallback tag scheme rather than the referrers API (DP-028).
	ViaTagFallback bool
}

VerifiedEvidence is one evidence object whose signatures have been checked.

Nothing reaches this type without a verifier having established its identities. That is the whole structural guarantee of DP-014: the evaluator has no access to candidate evidence, so it cannot accidentally treat a parsed claim as an established one.

Jump to

Keyboard shortcuts

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