evidence

package
v0.3.6 Latest Latest
Warning

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

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

Documentation

Overview

Package evidence implements `canary evidence`: the commands that produce and accumulate the passing-test records `canary verify` consumes.

  • `evidence run-go-test` runs the configured Go toolchain's own `go test` itself -- the caller supplies only `go test` arguments, never the executable -- and emits evidence records for the tokens that declare the tests it observed pass. It is the ONLY producer allowed to label its output origin "executed", the trusted level `canary verify` requires by default (C5-01): a fixed, canary-resolved executable cannot be a fake event generator.
  • `evidence run` executes an arbitrary caller-supplied command and emits evidence records the same way, but because the executable is not canary's own, every record's origin is "imported" -- the same trust level as from-go-test.
  • `evidence from-go-test` turns a `go test -json` stream already on stdin into evidence records -- origin "imported", since the stream was produced by something other than this command.
  • `evidence ingest` strictly validates a produced file and merges it into the project's evidence store.

None of these commands ever invents a record: run, run-go-test, and from-go-test only emit records for tests that actually reported Action="pass", and ingest refuses any file that does not satisfy the record grammar in pkg/evidence.

Index

Constants

View Source
const DefaultStorePath = ".canary/evidence.json"

DefaultStorePath is the evidence store's path relative to the project root.

View Source
const MaxStreamBytes = 512 << 20

MaxStreamBytes bounds the total go-test event stream. Beyond this the stream is rejected before any evidence is derived (resource exhaustion and truncation are indistinguishable from tampering at this layer).

Variables

View Source
var EvidenceCmd = &cobra.Command{
	Use:   "evidence",
	Short: "Produce and accumulate passing-test evidence records",
	Long: `Produce and accumulate the evidence records that 'canary verify' consumes.

Subcommands:
  run-go-test    Run 'go test' itself; emit evidence (stdout), origin "executed"
  run            Execute an arbitrary command itself; emit evidence (stdout), origin "imported"
  from-go-test   Map a 'go test -json' stream to evidence records (stdout), origin "imported"
  ingest         Validate an evidence file and merge it into the store`,

	PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return nil },
}

EvidenceCmd is the `canary evidence` command group.

Functions

func Merge

func Merge(current, incoming []ev.Record) []ev.Record

Merge appends the records of incoming that the store does not already hold verbatim, preserving the store's existing order. Two records are the same record only when every field matches: a differing digest, timestamp, runner, or argv vector describes a different observation and is kept. Record carries an Argv slice, so it is no longer comparable; dedup keys on string(json.Marshal(rec)) instead, which is deterministic because struct fields always encode in declaration order.

func RunExec added in v0.3.5

func RunExec(opts FromGoTestOptions, argv []string, stdout, stderr io.Writer) int

RunExec executes argv itself (Dir=opts.Root), captures its stdout bounded by MaxStreamBytes, lets its stderr flow through to stderr, derives HEAD/dirty state exactly like RunFromGoTest, then emits evidence records. argv is entirely caller-controlled -- a shell, a fake event generator, anything -- so canary having run it proves nothing about what actually happened; every emitted record's origin is therefore "imported" and no artifact is retained (C5-01). Only 'canary evidence run-go-test' (RunGoTest below), whose executable is fixed to canary's own resolved Go toolchain, may label output "executed".

func RunFromGoTest

func RunFromGoTest(opts FromGoTestOptions, stdin io.Reader, stdout, stderr io.Writer) int

RunFromGoTest reads a `go test -json` stream from stdin and writes an evidence file to stdout, returning the process exit code. Every record's origin is "imported": the stream was produced by something other than this command, so it is trusted only as far as `canary verify --allow-imported` permits.

func RunGoTest added in v0.3.6

func RunGoTest(opts FromGoTestOptions, testArgs []string, stdout, stderr io.Writer) int

RunGoTest runs the configured Go toolchain's own 'go test' -- resolved via exec.LookPath("go"), never a caller-supplied executable -- and emits evidence records with origin "executed": canary itself ran the real test runner, not an arbitrary command that printed test-shaped output (C5-01).

The "executed" guarantee rests on three things, not just a fixed argv[0]:

  1. argv is fixed as []string{goBin, "test"} + ensureJSON(testArgs); the caller's testArgs can never occupy argv[0] or argv[1], so no shape of testArgs can substitute a different executable. When testArgs is empty it defaults to ["-count=1", "-json", "./..."]; otherwise "-json" is appended unless already present.
  2. testArgs is scanned and rejected (rejectsUntrustedExecFlags) for -exec/-toolexec/-overlay in any dash or `=`-value form before argv is even built: `go test -exec <wrapper>` runs a caller-chosen wrapper "in place of" the compiled test binary, and `go`'s own -json machinery turns that wrapper's stdout into legitimate-looking pass events for tests that never compiled or ran -- while argv[0]/argv[1] stay "go"/"test". -toolexec substitutes build tools the same way, and -overlay redirects source file paths to attacker-chosen backing content at build time: `go test` genuinely compiles and runs, but the code that ran need not match anything committed -- the same forgery class under a different flag. None of the three may reach `go test` from testArgs.
  3. the child's environment has GOFLAGS neutralized -- execCapture's clearGoflags builds the child env with every GOFLAGS= entry from this process's own environment removed and one explicit empty GOFLAGS= appended, which overrides even a value persisted via `go env -w GOFLAGS=...` -- so an ambient GOFLAGS=-exec=... that testArgs scanning alone cannot see can't smuggle the same substitution in through go test's own env handling.

The raw stream is retained as a digest-named artifact under <root>/.canary/artifacts/ BEFORE being parsed, and the retained artifact's own bytes -- read back from disk, not the in-memory buffer -- are the sole source parsed into records: what 'canary verify' can later re-derive from the artifact is exactly what produced this run's evidence, not a buffer that could have been mutated between capture and parse. Every record also carries the exact argv vector that ran (Argv) and the process's exit status (RunExitStatus).

func RunIngest

func RunIngest(in, out string, stderr io.Writer) int

RunIngest validates the evidence file at in and merges it into the store at out, returning the process exit code (0 accepted, 1 rejected). Nothing is written when the input is rejected.

func WriteStore

func WriteStore(path string, records []ev.Record) error

WriteStore writes records to path atomically (staged in the same directory, fsynced, then renamed), creating the directory when needed. A crash mid-write therefore leaves the previous store intact rather than a truncated one. The store is this command's own output, so replacing it is intended; only a partial write would be a defect.

Types

type FromGoTestOptions

type FromGoTestOptions struct {
	Root      string
	ProjectID string
	// Commit, when non-empty, is an optional cross-check against Root's
	// derived HEAD -- the actual commit every record binds to is always
	// derived, never taken from this field on trust.
	Commit string
	// AllowDirty accepts a working tree that does not match HEAD. Without
	// it, RunFromGoTest refuses: evidence stamped with HEAD would otherwise
	// describe a tree that exists at no commit.
	AllowDirty bool
	Runner     string
	ObservedAt string
}

FromGoTestOptions are the resolved inputs of one from-go-test run.

Jump to

Keyboard shortcuts

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