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
- Variables
- func Merge(current, incoming []ev.Record) []ev.Record
- func RunExec(opts FromGoTestOptions, argv []string, stdout, stderr io.Writer) int
- func RunFromGoTest(opts FromGoTestOptions, stdin io.Reader, stdout, stderr io.Writer) int
- func RunGoTest(opts FromGoTestOptions, testArgs []string, stdout, stderr io.Writer) int
- func RunIngest(in, out string, stderr io.Writer) int
- func WriteStore(path string, records []ev.Record) error
- type FromGoTestOptions
Constants ¶
const DefaultStorePath = ".canary/evidence.json"
DefaultStorePath is the evidence store's path relative to the project root.
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 ¶
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 ¶
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 ¶
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]:
- 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.
- 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.
- 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 ¶
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 ¶
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.