bastion 0.53.2
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s

This commit is contained in:
Privatae
2026-10-20 04:42:14 +00:00
commit e4c9b69b46
471 changed files with 159794 additions and 0 deletions
+229
View File
@@ -0,0 +1,229 @@
package main
import (
"errors"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
// THE PAGES, RENDERED. walkfail_test.go proves the view models carry the
// failure; these prove the templates do not draw the tick under the banner.
// Both summaries were gated on Blind and the registry error only, so a walk
// failure with a working enumerator rendered the red banner AND "This machine
// looked; it did not find anything" beneath it.
func walkFailedBox(t *testing.T) {
t.Helper()
uiTestConfigDir(t)
prev := observedAgents
t.Cleanup(func() { observedAgents = prev })
observedAgents = func() ([]agentObs, error) { return nil, errors.New("read /proc: permission denied") }
}
func TestTheAgentsPageDoesNotSayItLookedWhenTheWalkFailed(t *testing.T) {
walkFailedBox(t)
u := uiTestServer(t)
h := u.handler()
cookie := uiSignedIn(t, u)
for _, path := range []string{"/agents", "/fragments/agents"} {
w := httptest.NewRecorder()
r := uiReq(http.MethodGet, path)
r.Header.Set("Cookie", cookie)
h.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%s = %d — both routes exist (uisanction.go) and a renamed one must fail here, not pass silently", path, w.Code)
}
body := w.Body.String()
if !strings.Contains(body, "could not read its processes") || !strings.Contains(body, "permission denied") {
t.Fatalf("%s: the failed walk is not bannered: %s", path, firstLines(body))
}
if strings.Contains(body, "This machine looked") || strings.Contains(body, "Nothing automated is running") {
t.Fatalf("%s: the page drew the tick under the walk banner: %s", path, firstLines(body))
}
}
}
func TestThePolicyPageDoesNotSayItLookedWhenTheWalkFailed(t *testing.T) {
walkFailedBox(t)
u := uiTestServer(t)
h := u.handler()
cookie := uiSignedIn(t, u)
body := policyGet(t, h, "/policy", cookie)
if !strings.Contains(body, "could not read its processes") {
t.Fatalf("the failed walk is not bannered: %s", firstLines(body))
}
for _, tick := range []string{"Nothing automated is running", "would be stopped by this rule", "Nothing running here would be stopped"} {
if strings.Contains(body, tick) {
t.Fatalf("the policy page drew the tick %q under the walk banner: %s", tick, firstLines(body))
}
}
}
func TestMarkingDuringAFailedWalkNamesTheWalk(t *testing.T) {
walkFailedBox(t)
u := uiTestServer(t)
h := u.handler()
cookie := uiSignedIn(t, u)
r := httptest.NewRequest(http.MethodPost, "/agents/mark", strings.NewReader("id=deadbeef"))
r.Host = "127.0.0.1:" + uiTestPort
r.Header.Set("Origin", "http://127.0.0.1:"+uiTestPort)
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
r.Header.Set("Cookie", cookie)
w := httptest.NewRecorder()
h.ServeHTTP(w, r)
body := w.Body.String()
if w.Code == http.StatusSeeOther || w.Code == http.StatusFound {
// The flash rides the redirected page.
body = agentsGet(t, h, w.Header().Get("Location"), cookie).Body.String()
}
if !strings.Contains(body, "could not read its processes") {
t.Fatalf("the mark did not name the failed walk (status %d): %s", w.Code, firstLines(body))
}
if strings.Contains(body, "not running on this machine any more") {
t.Fatalf("the mark blamed the wrong cause: %s", firstLines(body))
}
}
// THE ROWS THAT NAME A PROGRAM. The summary tick was gated in round two; these
// two lists said "not running right now" about a NAMED program on a render
// where nothing looked — one row below the banner. Both take the same shape
// their unreadable-registry guard already has.
func TestThePolicyPastRowsCannotSayNotRunningWhenTheWalkFailed(t *testing.T) {
cfg := policyTestBox(t)
obs := policyCandidateObs("claude-code", "/usr/local/bin/claude", 4242)
id := sanctionIdentityOf(obs)
now := time.Now()
writeLedger(t, cfg, policyLedger{Since: now.Add(-time.Hour), LastScan: now,
Candidates: map[string]policyCandidate{id.fingerprint(): {
ID: id.fingerprint(), sanctionIdentity: id, FirstSeen: now.Add(-time.Hour), LastSeen: now}}})
observedAgents = func() ([]agentObs, error) { return nil, errors.New("read /proc: permission denied") }
u := uiTestServer(t)
body := policyGet(t, u.handler(), "/policy", uiSignedIn(t, u))
if strings.Contains(body, policyPastNotSeen) {
t.Fatalf("a failed walk called a candidate %q: %s", policyPastNotSeen, firstLines(body))
}
if !strings.Contains(body, policyPastUnwalked) {
t.Fatalf("the past row does not say the walk failed: %s", firstLines(body))
}
if strings.Contains(body, "Still a candidate right now: 0") {
t.Fatalf("a failed walk counted zero candidates still running: %s", firstLines(body))
}
}
func TestTheAgentsPageDoesNotListMarksAsNotRunningWhenTheWalkFailed(t *testing.T) {
mine := anObservedAgent("claude-code", "/usr/local/bin/claude", 4242)
agentsTestBox(t, mine)
if _, err := addSanction(mine, sanctionByLocalUI); err != nil {
t.Fatal(err)
}
observedAgents = func() ([]agentObs, error) { return nil, errors.New("read /proc: permission denied") }
u := uiTestServer(t)
h := u.handler()
body := agentsGet(t, h, "/agents", uiSignedIn(t, u)).Body.String()
if strings.Contains(body, "Marked as yours, but not running right now") || strings.Contains(body, "/usr/local/bin/claude") {
t.Fatalf("a failed walk listed a marked program as not running: %s", firstLines(body))
}
if !strings.Contains(body, "could not read its processes") {
t.Fatalf("the banner is missing: %s", firstLines(body))
}
}
// THE CENSUS BLOCK, one line above the rows. "Vouched for since" is read from
// the registry and stays real on a failed walk; "still a candidate right now"
// is read from the walk and is not known. The first version of the row switch
// let the walk take the vouch with it — the count the whole page waits on
// went to 0 and the box turned green.
func TestTheCensusBlockKeepsTheVouchAndWithholdsStillOnAFailedWalk(t *testing.T) {
cfg := policyTestBox(t)
obs := policyCandidateObs("claude-code", "/usr/local/bin/claude", 4242)
id := sanctionIdentityOf(obs)
now := time.Now()
// Two candidates: one the user vouched for, one nobody did — the second is
// the row that must wear no "nothing behind it" edge on a failed walk.
other := policyCandidateObs("aider", "/usr/local/bin/aider", 4243)
oid := sanctionIdentityOf(other)
writeLedger(t, cfg, policyLedger{Since: now.Add(-time.Hour), LastScan: now,
Candidates: map[string]policyCandidate{
id.fingerprint(): {ID: id.fingerprint(), sanctionIdentity: id, FirstSeen: now.Add(-time.Hour), LastSeen: now},
oid.fingerprint(): {ID: oid.fingerprint(), sanctionIdentity: oid, FirstSeen: now.Add(-time.Hour), LastSeen: now},
}})
if _, err := addSanction(obs, sanctionByLocalUI); err != nil {
t.Fatal(err)
}
observedAgents = func() ([]agentObs, error) { return nil, errors.New("read /proc: permission denied") }
u := uiTestServer(t)
body := policyGet(t, u.handler(), "/policy", uiSignedIn(t, u))
if !strings.Contains(body, "Vouched for since: 1") {
t.Fatalf("a failed walk zeroed the vouched count: %s", firstLines(body))
}
if strings.Contains(body, "Still a candidate right now: 0") {
t.Fatalf("a failed walk counted zero candidates still running: %s", firstLines(body))
}
if !strings.Contains(body, "Still a candidate right now: not known") {
t.Fatalf("the census block does not say the count is unknown: %s", firstLines(body))
}
if !strings.Contains(body, `class="state warn"`) || strings.Contains(body, `class="state ok"`) {
t.Fatalf("a vouched candidate on a failed walk must keep the box amber, never green: %s", firstLines(body))
}
if strings.Contains(body, `past dormant`) {
t.Fatalf("a row nothing looked for wore the 'nothing behind it' edge: %s", firstLines(body))
}
}
// A mark that no longer counts is a fact about the record, not the walk: it is
// still said on a failed walk. Only the dormant half (which needs `seen`) is
// withheld.
func TestAnExpiredMarkIsStillSaidOnAFailedWalk(t *testing.T) {
live := anObservedAgent("claude-code", "/usr/local/bin/claude", 4242)
agentsTestBox(t, live)
d, err := configDir()
if err != nil {
t.Fatal(err)
}
v1ID := v1Fingerprint(kindAgent, "claude-code", "/usr/local/bin/claude", "user")
registry := `{"` + v1ID + `": {"id": "` + v1ID + `", "kind": "agent",
"signature": "claude-code", "argv0": "/usr/local/bin/claude",
"user": "user", "at": "2026-09-01T10:00:00Z", "by": "local-ui"}}`
if err := os.WriteFile(filepath.Join(d, "sanctions.json"), []byte(registry), 0o600); err != nil {
t.Fatal(err)
}
observedAgents = func() ([]agentObs, error) { return nil, errors.New("read /proc: permission denied") }
u := uiTestServer(t)
body := agentsGet(t, u.handler(), "/agents", uiSignedIn(t, u)).Body.String()
if !strings.Contains(body, "Marks that no longer count") {
t.Fatalf("a failed walk silenced a mark that no longer counts: %s", firstLines(body))
}
if strings.Contains(body, "Marked as yours, but not running right now") {
t.Fatalf("a failed walk listed a mark as not running: %s", firstLines(body))
}
}
// A running candidate the registry cannot judge is unknowable too, and wears
// no "nothing behind it" edge — the live-verdict arm, pre-existing before
// this PR, set the status without the flag.
func TestARunningCandidateTheRegistryCannotJudgeWearsNoDormantEdge(t *testing.T) {
obs := policyCandidateObs("claude-code", "/usr/local/bin/claude", 4242)
cfg := policyTestBox(t, obs)
id := sanctionIdentityOf(obs)
now := time.Now()
writeLedger(t, cfg, policyLedger{Since: now.Add(-time.Hour), LastScan: now,
Candidates: map[string]policyCandidate{id.fingerprint(): {
ID: id.fingerprint(), sanctionIdentity: id, FirstSeen: now.Add(-time.Hour), LastSeen: now}}})
if err := os.WriteFile(filepath.Join(cfg, "sanctions.json"), []byte("{not json"), 0o600); err != nil {
t.Fatal(err)
}
u := uiTestServer(t)
body := policyGet(t, u.handler(), "/policy", uiSignedIn(t, u))
if !strings.Contains(body, policyPastUnknowable) {
t.Fatalf("an unreadable registry did not make the running row unknowable: %s", firstLines(body))
}
if strings.Contains(body, "past dormant") {
t.Fatalf("a running-but-unjudgeable row wore the dormant edge: %s", firstLines(body))
}
}