attest

package
v3.100.3 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: AGPL-3.0 Imports: 17 Imported by: 0

Documentation

Overview

Package attest verifies signatures and provenance on artefacts this CLI consumes.

internal/cdxsign is the inverse: it signs a CycloneDX document with the machine's own ambient OIDC identity, so the result verifies with stock cosign. This package reads those signatures — and anyone else's — back.

What "verified" means here, exactly

Two rules, and they pull in opposite directions. A verifier must not report success for a check it did not perform — a green tick over a skipped chain validation converts an unknown into a false assurance. But a verifier that answers every question with "you did not tell me what to trust" is not being careful either; it is making the user do its job and calling that honesty.

So: do the work by default, and be exact about what was done.

The Sigstore public-good root is embedded, so chain validation runs without being asked, exactly as cosign does it. --trusted-root overrides it for a private deployment. Signature validity and the certificate chain are always checked. The signer's identity is always *read* and reported as a fact — it is not a check, and listing it as "skipped" implied a gap where none existed. Comparing it against an expectation is a check, and runs when the caller states one.

What genuinely cannot be done from the material at hand is said once, in Suggestions, with the command that would do it. Rekor inclusion is the only such item: the sidecars carry no log entry, so proving it means querying the log and verifying its signed tree head.

Verified() is true when every check that ran passed. Options.Require turns "this did not run" into a failure, for a caller who needs a specific assurance rather than the default set.

Index

Constants

View Source
const ProjectTrustRootPath = ".vulnetix/trusted-root.pem"

ProjectTrustRootPath is where a repository keeps its own trust root.

A team running a private Sigstore commits this once and never types --trusted-root again. It is the same shape as .vulnetix/license-policy.yaml and .vulnetix/ai-firewall.yaml: the project states its own defaults in the repository, so the knowledge lives with the code rather than in whoever remembers the flag.

Variables

This section is empty.

Functions

func ExpandIssuer added in v3.98.0

func ExpandIssuer(name string) string

ExpandIssuer resolves a shortcut name to its issuer URL, or returns the input.

func IssuerShortcutNames added in v3.98.0

func IssuerShortcutNames() []string

IssuerShortcutNames lists the accepted shortcuts, for help text.

Types

type Check

type Check struct {
	// Name identifies the check, e.g. "signature", "certificate-chain".
	Name string `json:"name"`
	// Status is what happened.
	Status CheckStatus `json:"status"`
	// Detail explains a failure, or why a check was skipped.
	Detail string `json:"detail,omitempty"`
}

Check is one verification step and its outcome.

type CheckStatus

type CheckStatus string

CheckStatus is the outcome of one verification check.

const (
	// CheckPassed — the check ran and succeeded.
	CheckPassed CheckStatus = "passed"
	// CheckFailed — the check ran and failed.
	CheckFailed CheckStatus = "failed"
	// CheckSkipped — the check did not run. Detail says why. A skipped check is
	// never counted as a pass.
	CheckSkipped CheckStatus = "skipped"
)

type Identity

type Identity struct {
	// Subject is the SAN — a workflow reference in CI, an email for a person,
	// a SPIFFE ID for a workload.
	Subject string `json:"subject,omitempty"`
	// Issuer is the OIDC issuer that authenticated them.
	Issuer string `json:"issuer,omitempty"`
	// NotBefore and NotAfter are the certificate's validity window. Fulcio
	// certificates live for ten minutes, so this is also roughly when the
	// signature was made.
	NotBefore time.Time `json:"notBefore,omitzero"`
	NotAfter  time.Time `json:"notAfter,omitzero"`
	// BuildTrigger, SourceRepository and SourceRevision are the Fulcio CI
	// extensions, present when the signer was a pipeline rather than a person.
	BuildTrigger     string `json:"buildTrigger,omitempty"`
	SourceRepository string `json:"sourceRepository,omitempty"`
	SourceRevision   string `json:"sourceRevision,omitempty"`
}

Identity is who a certificate says signed something.

type Options

type Options struct {
	// ArtifactPath is the file being verified, for labelling.
	ArtifactPath string
	// Artifact is the bytes the signature should cover. Required for a cosign
	// detached signature; for DSSE the payload is inside the envelope.
	Artifact []byte
	// SignaturePath and CertificatePath are the cosign detached sidecars.
	SignaturePath, CertificatePath string
	// EnvelopePath is a DSSE envelope (.intoto.jsonl).
	EnvelopePath string
	// Identity, when set, is the exact certificate subject required.
	//
	// Exact, not a pattern. Every keyless subject is a URL, so a pattern is
	// full of dots, and a user pasting "https://github.com/acme/repo" as a
	// regex gets a match on "https://githubXcom/acme/repo" too. The safe
	// comparison is the default and the pattern is opt-in, which is also the
	// split cosign uses (--certificate-identity vs -regexp).
	Identity string
	// IdentityRegex, when set, is a pattern the subject must match. For
	// deliberately matching a set — every workflow in one repository, say.
	IdentityRegex string
	// Issuer, when set, is the OIDC issuer the certificate must name. Accepts a
	// shortcut name (github, gitlab, google, microsoft) as well as a URL.
	Issuer string
	// TrustedRootPath is a PEM bundle to validate the certificate chain
	// against. Empty resolves through SIGSTORE_ROOT_FILE, then the project's
	// own root, then the embedded public-good one — see resolveAnchor.
	TrustedRootPath string
	// ProjectRoot is the repository the artefact belongs to. Used to discover
	// .vulnetix/trusted-root.pem, and to notice when the signer is that
	// repository's own CI.
	ProjectRoot string
	// RepoFullName is the "owner/repo" of ProjectRoot, when it is known.
	// Purely informational: a signature from the repository being scanned is
	// worth pointing out, and one from somewhere else is worth pointing out
	// more.
	RepoFullName string
	// Strict requires the verification to be fully pinned: an identity and an
	// issuer expectation, and a chain that verified. It fails with the exact
	// flags to add rather than a lecture about what strict means.
	Strict bool
	// Require names checks that must have been performed. A required check that
	// was skipped becomes a failure.
	Require []string
	// Now overrides the clock, for tests.
	Now time.Time
}

Options controls verification.

type Predicate

type Predicate struct {
	// Type is the predicateType URI, verbatim.
	Type string `json:"type"`
	// Kind is what this CLI made of it.
	Kind PredicateKind `json:"kind"`
	// Subjects are the artefacts the statement is about.
	Subjects []Subject `json:"subjects,omitempty"`

	// Builder is who claims to have built the artefact, e.g. a GitHub Actions
	// workflow reference. Provenance only.
	Builder string `json:"builder,omitempty"`
	// BuildType is the build definition the builder followed.
	BuildType string `json:"buildType,omitempty"`
	// SourceURI and SourceRevision are where the inputs came from.
	SourceURI      string `json:"sourceUri,omitempty"`
	SourceRevision string `json:"sourceRevision,omitempty"`
	// Invocation is the entry point the build was started at.
	Invocation string `json:"invocation,omitempty"`

	// SLSAVersion is the provenance schema version: "v0.2" or "v1".
	SLSAVersion string `json:"slsaVersion,omitempty"`
}

Predicate is the interpreted content of an in-toto statement.

func (*Predicate) Claim

func (p *Predicate) Claim() SLSALevelClaim

Claim summarises what a predicate's fields support.

type PredicateKind

type PredicateKind string

PredicateKind classifies a predicate by what it asserts.

const (
	// KindProvenance — how an artefact was built (SLSA provenance).
	KindProvenance PredicateKind = "provenance"
	// KindSBOM — what an artefact contains.
	KindSBOM PredicateKind = "sbom"
	// KindOther — a predicate this CLI does not interpret.
	KindOther PredicateKind = "other"
)

type Result

type Result struct {
	// Artifact is the file that was verified.
	Artifact string `json:"artifact"`
	// Digest is the SHA-256 of the artefact's bytes.
	Digest string `json:"digest"`
	// Envelope is the signature form that was read: "dsse" or "cosign".
	Envelope string `json:"envelope,omitempty"`
	// Identity is who the certificate says signed it.
	Identity Identity `json:"identity,omitzero"`
	// TrustAnchor names what the chain was verified against.
	TrustAnchor string `json:"trustAnchor,omitempty"`
	// SignerIsThisRepo reports that the signer is the repository being scanned.
	SignerIsThisRepo bool `json:"signerIsThisRepo,omitempty"`
	// SignerRepo names the signer's repository when it is NOT this one.
	SignerRepo string `json:"signerRepo,omitempty"`
	// Checks is every verification step and its outcome. This, not a boolean,
	// is the honest answer.
	Checks []Check `json:"checks"`
	// Suggestions are the ways this verification could be made stricter, each
	// with the exact command to do it.
	//
	// This is the difference between a report that is honest and one that is
	// useful. Telling a reader that nothing pinned the signer's identity is
	// only half an answer; the other half is the flag that would pin it, filled
	// in with the identity actually found.
	Suggestions []Suggestion `json:"suggestions,omitempty"`
	// Predicate is the in-toto predicate when one was found.
	Predicate *Predicate `json:"predicate,omitempty"`
}

Result is a full verification outcome.

func Verify

func Verify(opts Options) (*Result, error)

Verify checks the signatures on an artefact.

func (*Result) Failures

func (r *Result) Failures() []Check

Failures lists the checks that ran and failed.

func (*Result) Missing

func (r *Result) Missing() []Check

Missing lists the checks that did not run.

func (*Result) PerformedChain

func (r *Result) PerformedChain() bool

PerformedChain reports whether the certificate chain was actually validated.

func (*Result) Verified

func (r *Result) Verified() bool

Verified reports whether every performed check passed and none failed.

A skipped check does not make this false — it means the caller did not ask for it. Use Missing to find out what was not checked, and Options.Require to turn a skip into a failure.

type SLSALevelClaim

type SLSALevelClaim struct {
	// HasProvenance reports whether a provenance predicate was found at all.
	HasProvenance bool `json:"hasProvenance"`
	// HasBuilder, HasSource and HasSubjectDigest report the fields that make
	// provenance actionable.
	HasBuilder       bool `json:"hasBuilder"`
	HasSource        bool `json:"hasSource"`
	HasSubjectDigest bool `json:"hasSubjectDigest"`
	// Missing names the absent fields, so a shortfall is actionable.
	Missing []string `json:"missing,omitempty"`
}

SLSALevelClaim describes what a predicate's own content supports.

This is deliberately not "the SLSA level of this artefact". A level is a property of the build platform and its controls, which no consumer can determine by reading a document the build produced. What can be said is which fields are present, and that is what this reports — so a caller gating on provenance gates on facts rather than on a number the artefact asserted about itself.

func (SLSALevelClaim) Complete

func (c SLSALevelClaim) Complete() bool

Complete reports whether every field is present.

type Subject

type Subject struct {
	Name   string            `json:"name,omitempty"`
	Digest map[string]string `json:"digest,omitempty"`
}

Subject is an artefact a statement is about.

type Suggestion

type Suggestion struct {
	// Why says what is currently unconstrained, in plain terms.
	Why string `json:"why"`
	// Flags is the argument to add to this command, ready to paste. Empty when
	// the suggestion is to run a different tool entirely.
	Flags string `json:"flags,omitempty"`
	// Command is a complete command line, for a suggestion this CLI cannot
	// satisfy itself. Never a fragment with an ellipsis in it: a suggestion the
	// reader has to fill in is one they have to already know the answer to,
	// which defeats the point of suggesting it.
	Command string `json:"command,omitempty"`
}

Suggestion is a way to make a verification stricter.

type TrustAnchor

type TrustAnchor struct {
	// Name identifies the anchor in output, e.g. "Sigstore public-good".
	Name string
	// Pool is what certificates chain to.
	Pool *x509.CertPool
	// Roots are the parsed certificates, for reporting expiry.
	Roots []*x509.Certificate
}

TrustAnchor is a named set of root certificates.

func LoadTrustAnchor

func LoadTrustAnchor(path string) (*TrustAnchor, error)

LoadTrustAnchor reads a PEM bundle as a trust anchor.

func PublicGood

func PublicGood() (*TrustAnchor, error)

PublicGood returns the embedded Sigstore public-good trust anchor.

func (*TrustAnchor) EarliestExpiry

func (a *TrustAnchor) EarliestExpiry() (name string, notAfter string)

EarliestExpiry is when the first certificate in the anchor expires.

A pinned root that has quietly expired would make every verification fail with an unhelpful chain error, so the expiry is surfaced as its own thing.

Jump to

Keyboard shortcuts

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