Files
Privatae 1176f7fd0b
ci / personal-data (push) Successful in 8s
ci / test (push) Successful in 7m17s
ci / fuzz (push) Successful in 6m33s
bastion 0.61.0
2026-11-02 06:20:42 +00:00

1119 lines
48 KiB
Go

//go:build !windows
package main
// hostops_test.go — the write gate on an anchored box (writes.go, device.go,
// quarantine.go): every change needs an approval this box verified, reads
// never do, a local box is untouched, and the mutations the design names —
// unsigned / expired / uses-exhausted / wrong-key / no-nonce / replayed /
// wrong-purpose approvals — are each refused.
import (
"context"
"crypto/ed25519"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
"github.com/gorilla/websocket"
)
// testApproval signs a device approval for one run of a write on an anchored
// test box, from a device key it creates and pins in the test's config dir.
// "" when the gate does not apply (local box, a read, an op with its own
// approval), so drivers can pass it unconditionally.
func testApproval(t *testing.T, op string, args []string) string {
t.Helper()
if currentAnchor() == nil || !opWrites(op) || ownApproval(op) {
return ""
}
pub, err := deviceInit()
if err != nil {
t.Fatalf("device-init: %v", err)
}
if err := pinDeviceKey(pub); err != nil {
t.Fatalf("pin-device: %v", err)
}
hn, _ := os.Hostname()
a, err := deviceSign(mgmtOpPurpose(op, hn, args), time.Minute)
if err != nil {
t.Fatalf("device-sign: %v", err)
}
return a
}
// testDirective attaches the person's approval for one heartbeat directive.
// `key` is the approvals map key ("self-update", "contain:<pid>", "mirror");
// the purpose is bound to the directive name before any ":".
func testDirective(t *testing.T, key string, args []string, body string) []byte {
t.Helper()
var m map[string]any
if err := json.Unmarshal([]byte(body), &m); err != nil {
t.Fatal(err)
}
what, _, _ := strings.Cut(key, ":")
if a := testApproval(t, what, args); a != "" {
m["approvals"] = map[string]string{key: a}
}
out, _ := json.Marshal(m)
return out
}
// driveJob sends a job AS IS — no approval is added — with a configured
// context root, so the reply is the gate's and not "context store not
// configured".
func driveJob(t *testing.T, self string, job mgmtJob, ctxRoot, dcnDir string) (string, string, int) {
t.Helper()
up := websocket.Upgrader{}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ws, err := up.Upgrade(w, r, nil)
if err != nil {
return
}
defer ws.Close()
handleMgmtConnFor(ws, self, ctxRoot, dcnDir, "")
}))
defer srv.Close()
c, _, err := websocket.DefaultDialer.Dial("ws"+strings.TrimPrefix(srv.URL, "http"), nil)
if err != nil {
t.Fatalf("dial: %v", err)
}
defer c.Close()
raw, _ := json.Marshal(job)
if err := c.WriteMessage(websocket.BinaryMessage, append([]byte("C"), raw...)); err != nil {
t.Fatalf("write: %v", err)
}
var out, errOut string
for {
_, data, err := c.ReadMessage()
if err != nil {
t.Fatalf("read: %v", err)
}
switch data[0] {
case 'O':
out += string(data[1:])
case 'R':
errOut += string(data[1:])
case 'X':
var code int
fmt.Sscanf(string(data[1:]), "%d", &code)
return out, errOut, code
}
}
}
// putJob is the gated write the gate tests drive: ctx_extract with an
// argument its handler will refuse (an unknown tool), so a run that passes
// the gate answers with the HANDLER's exit (1) — the gate's refusals are 126.
// (ctx_put is standing work under the configured store and is not gated.)
func putJob(approval string) mgmtJob {
return mgmtJob{Op: "ctx_extract", Args: []string{`{"tool":"no-such-tool","mime":"text/plain","b64":"aGk="}`},
Approval: approval}
}
const putOp = "ctx_extract"
// Every op this box answers is a read or a write, and the read set is exactly
// the one the design names. A new op lands as a write until someone says
// otherwise.
func TestEveryOpIsReadOrWrite(t *testing.T) {
reads := map[string]bool{}
for _, op := range []string{"uptime", mgmtAuditFetchOp, "dcn_list", "dcn_stat", "dcn_read_range",
"dcn_search", "dcn_catalog", "ctx_get", "backup_list", "model:list", "pairing_list", "stepup_request",
"stepup_status", "device_request", "device_status", "op_list", "op_run_status", "op_plan_status"} {
reads[op] = true
}
for _, op := range mgmtOpVocabulary() {
isRead := reads[op] || strings.HasPrefix(op, "observe:") || strings.HasPrefix(op, "computer:")
if opWrites(op) == isRead {
t.Errorf("%s: opWrites=%v, the design says read=%v", op, opWrites(op), isRead)
}
}
for _, op := range []string{"harness:run", "session:open", "contain", "self-update", "mirror", "backup_fetch",
"op_sync", "op_dry_run", "op_apply", "op_grant_run", "brand_new_op"} {
if !opWrites(op) {
t.Errorf("%s must be a write", op)
}
}
// egress and inference USE the machine and change nothing on it.
for _, op := range []string{"egress", "inference"} {
if opWrites(op) {
t.Errorf("%s uses this machine and changes nothing on it — not gated per run", op)
}
}
for _, op := range []string{"dcn_grant_drop", "pairing_grant_drop"} {
if !opTightens(op) || !ownApproval(op) {
t.Errorf("%s only removes reach and is never held back", op)
}
}
for _, op := range []string{"dcn_grant_sync", "dcn_plan_apply", "model:pull", "pairing_grant_sync", "backup_fetch", "ctx_put", "ctx_del"} {
if !ownApproval(op) {
t.Errorf("%s carries its own approval and must not be gated twice", op)
}
}
if ownApproval("ctx_extract") || ownApproval("model:drop") {
t.Error("ctx_extract / model:drop carry no approval of their own")
}
for _, op := range []string{"backup_fetch", "ctx_put", "ctx_del"} {
if !ownApproval(op) {
t.Errorf("%s is standing work under a store the person configured at the machine", op)
}
}
}
// A local box gates nothing: the pairing is the authorization there.
func TestALocalBoxRunsAWriteOnThePairingAlone(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
writePairing(t, tAnchor)
if _, errOut, code := driveJob(t, tAnchor, putJob(""), t.TempDir(), ""); code == 126 {
t.Fatalf("a local box must run a write with no approval (the handler answers, not the gate): %d %s", code, errOut)
}
}
// On an anchored box: a read answers; a write with no approval is refused and
// names the fix; a write with a device approval runs exactly once.
func TestAnAnchoredBoxRefusesAWriteWithoutApprovalAndRunsItOnceWithOne(t *testing.T) {
setupBox(t)
ctx := t.TempDir()
if _, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "pairing_list"}, ctx, ""); code != 0 {
t.Fatalf("a read must answer on the anchor's word: %d %s", code, errOut)
}
_, errOut, code := driveJob(t, tAnchor, putJob(""), ctx, "")
if code != 126 || !strings.Contains(errOut, "holds no way to take one") || !strings.Contains(errOut, "pin-device") {
t.Fatalf("no factor, no device: the refusal must name the fix: %d %q", code, errOut)
}
if !quarantinedUntil(tAnchor, time.Now()).IsZero() {
t.Fatal("'no way to approve' is the box's own state and must not count as a strike")
}
if refusalCounts(putOp, "refused: op 'zzz' not permitted on this host") || refusalCounts("uptime", "refused: x") {
t.Fatal("an unknown op or a read never counts")
}
if !refusalCounts(putOp, "refused: op 'ctx_extract' changes this machine and approval expired") {
t.Fatal("a bad approval on a known write counts")
}
// Device pinned, no approval given.
pub, _ := deviceInit()
if err := pinDeviceKey(pub); err != nil {
t.Fatal(err)
}
if !devicePinned() || !secondFactorAvailable("") {
t.Fatal("the pin must count as a second factor")
}
_, errOut, code = driveJob(t, tAnchor, putJob(""), ctx, "")
if code != 126 || !strings.Contains(errOut, "none was given") {
t.Fatalf("a write with no approval is refused: %d %q", code, errOut)
}
// Approved: runs once.
hn, _ := os.Hostname()
approval, err := deviceSign(mgmtOpPurpose(putOp, hn, putJob("").Args), time.Minute)
if err != nil {
t.Fatal(err)
}
if _, errOut, code := driveJob(t, tAnchor, putJob(approval), ctx, ""); code == 126 {
t.Fatalf("an approved write must reach the handler: %d %s", code, errOut)
}
// Replay: the nonce is burned.
if _, errOut, code := driveJob(t, tAnchor, putJob(approval), ctx, ""); code != 126 || !strings.Contains(errOut, "already used") {
t.Fatalf("a replayed approval must be refused: %d %q", code, errOut)
}
// The audit line names how it was approved and binds the args.
recs := readMgmtAudit(t)
var ran map[string]any
for _, r := range recs {
if r["verdict"] == auditAllowed && r["op"] == putOp {
ran = r
}
}
if ran == nil || !strings.HasPrefix(fmt.Sprint(ran["approval"]), "device:") || ran["args_sha256"] != argsDigest(putJob("").Args) {
t.Fatalf("the allowed line must name the approval spent and the args digest: %v", ran)
}
}
// The mutation suite: each broken approval is refused for its own reason.
func TestEveryBrokenDeviceApprovalIsRefused(t *testing.T) {
setupBox(t)
ctx := t.TempDir()
pub, _ := deviceInit()
if err := pinDeviceKey(pub); err != nil {
t.Fatal(err)
}
hn, _ := os.Hostname()
purpose := mgmtOpPurpose(putOp, hn, putJob("").Args)
good, _ := deviceSign(purpose, time.Minute)
var a deviceApproval
_ = json.Unmarshal([]byte(good), &a)
mutate := func(f func(x *deviceApproval)) string {
x := a
f(&x)
raw, _ := json.Marshal(&x)
return string(raw)
}
cases := []struct{ name, approval, want string }{
{"wrong purpose (other args)", mutate(func(x *deviceApproval) { x.Purpose = mgmtOpPurpose(putOp, hn, []string{"other"}) }), "signature does not verify"},
{"tampered signature", mutate(func(x *deviceApproval) { x.Sig = base64.StdEncoding.EncodeToString(make([]byte, 64)) }), "signature does not verify"},
{"expired", mutate(func(x *deviceApproval) { x.Expires = time.Now().Add(-time.Minute).UTC().Format(time.RFC3339) }), "signature does not verify"},
{"two uses", mutate(func(x *deviceApproval) { x.Uses = 2 }), "signature does not verify"},
{"no nonce", mutate(func(x *deviceApproval) { x.Nonce = "" }), "signature does not verify"},
{"other version", mutate(func(x *deviceApproval) { x.V = 2 }), "not one this box reads"},
{"unreadable", `{"sig": not json`, "not readable"},
{"not an approval at all", "{not json", "none was given"},
{"console id that does not exist", "deadbeef", "none was given"},
}
// Every refused write here is a strike (quarantine.go); the hold is lifted
// between cases so each case meets the gate and not the hold.
for _, c := range cases {
_, errOut, code := driveJob(t, tAnchor, putJob(c.approval), ctx, "")
if code != 126 || !strings.Contains(errOut, c.want) {
t.Errorf("%s: want refusal containing %q, got %d %q", c.name, c.want, code, errOut)
}
quarantineClear(tAnchor)
}
// Properly RE-SIGNED mutations: the signature is fine, the content is not.
resign := func(f func(x *deviceApproval)) string {
x := a
f(&x)
f2, _ := deviceKeyLoad()
priv, _ := base64.StdEncoding.DecodeString(f2.Priv)
x.Sig = base64.StdEncoding.EncodeToString(ed25519.Sign(ed25519.PrivateKey(priv), x.canonical()))
raw, _ := json.Marshal(&x)
return string(raw)
}
resigned := []struct{ name, approval, want string }{
{"wrong purpose", resign(func(x *deviceApproval) { x.Purpose = mgmtOpPurpose(putOp, hn, []string{"other"}) }), "purpose mismatch"},
{"expired", resign(func(x *deviceApproval) { x.Expires = time.Now().Add(-time.Minute).UTC().Format(time.RFC3339) }), "expired"},
{"too long-lived", resign(func(x *deviceApproval) { x.Expires = time.Now().Add(48 * time.Hour).UTC().Format(time.RFC3339) }), "longer than"},
{"two uses", resign(func(x *deviceApproval) { x.Uses = 2 }), "one run"},
{"short nonce", resign(func(x *deviceApproval) { x.Nonce = "abc" }), "nonce too short"},
}
for _, c := range resigned {
_, errOut, code := driveJob(t, tAnchor, putJob(c.approval), ctx, "")
if code != 126 || !strings.Contains(errOut, c.want) {
t.Errorf("%s: want refusal containing %q, got %d %q", c.name, c.want, code, errOut)
}
quarantineClear(tAnchor)
}
// And the good one still runs after all of that: no mutation spent it.
if _, errOut, code := driveJob(t, tAnchor, putJob(good), ctx, ""); code == 126 {
t.Fatalf("the untouched approval must still run: %d %s", code, errOut)
}
}
// A device the box never pinned signs nothing that runs here, and unpinning
// withdraws a device.
func TestAnUnpinnedDeviceIsRefusedAndUnpinWithdrawsOne(t *testing.T) {
// Device A on its own machine.
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
pubA, _ := deviceInit()
hn, _ := os.Hostname()
fromA, _ := deviceSign(mgmtOpPurpose(putOp, hn, putJob("").Args), time.Minute)
// The anchored box, with device B pinned.
setupBox(t)
ctx := t.TempDir()
pubB, _ := deviceInit()
if err := pinDeviceKey(pubB); err != nil {
t.Fatal(err)
}
if _, errOut, code := driveJob(t, tAnchor, putJob(fromA), ctx, ""); code != 126 || !strings.Contains(errOut, "not pinned") {
t.Fatalf("an unpinned device's approval must be refused: %d %q", code, errOut)
}
if err := pinDeviceKey(pubA); err != nil {
t.Fatal(err)
}
if _, errOut, code := driveJob(t, tAnchor, putJob(fromA), ctx, ""); code == 126 {
t.Fatalf("once pinned, A's approval runs: %d %s", code, errOut)
}
if err := unpinDeviceKey(pubA); err != nil {
t.Fatal(err)
}
if err := unpinDeviceKey(pubB); err != nil {
t.Fatal(err)
}
if devicePinned() {
t.Fatal("no device is pinned now")
}
if err := unpinDeviceKey(pubB); err == nil {
t.Fatal("unpinning a key that is not pinned must say so")
}
if err := pinDeviceKey("not-a-key"); err == nil {
t.Fatal("a malformed key must be refused")
}
}
// The console path: a factor enrolled here, a mgmt_op request the host files,
// the person approves with their code, and the approval runs the op once.
func TestAConsoleApprovalRunsAWriteOnce(t *testing.T) {
setupBox(t)
ctx, dcn := t.TempDir(), t.TempDir()
enrollFactor(t, dcn)
hn, _ := os.Hostname()
args := putJob("").Args
details := map[string]any{"kind": "mgmt_op", "op": putOp, "server": hn,
"args": []any{args[0]}, "note": "save the meeting notes"}
purpose, err := purposeFromDetails(details)
if err != nil {
t.Fatal(err)
}
if purpose != mgmtOpPurpose(putOp, hn, args) {
t.Fatalf("the purpose must be derived from the details: %s", purpose)
}
if stepupDetailsFamily(details) != "dcn" {
t.Fatalf("a mgmt_op ask is gated on the op's family: %s", stepupDetailsFamily(details))
}
if stepupDetailsFamily(map[string]any{"kind": "mgmt_op", "op": "contain", "server": hn}) != capsFamilyAnchor {
t.Fatal("a directive's approval is the anchor's alone")
}
elsewhere := map[string]any{"kind": "mgmt_op", "op": "ctx_put", "server": "elsewhere", "args": []any{"a"}}
ep, _ := purposeFromDetails(elsewhere)
if _, err := stepupNewRequest(dcn, ep, elsewhere); err == nil || !strings.Contains(err.Error(), "elsewhere") {
t.Fatalf("a request for another machine must be refused at THIS box's ask: %v", err)
}
// The host files the ask on the mgmt lane.
detailsRaw, _ := json.Marshal(details)
detailsB64 := base64.StdEncoding.EncodeToString(detailsRaw)
out, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "stepup_request", Args: []string{purpose, detailsB64}}, ctx, dcn)
if code != 0 {
t.Fatalf("stepup_request: %d %s", code, errOut)
}
var reply struct{ Request string }
_ = json.Unmarshal([]byte(out), &reply)
if words := describeRequest(stepupRequest{Details: details}); !strings.Contains(words, putOp) ||
!strings.Contains(words, "on "+hn) || !strings.Contains(words, "arg 1: "+args[0]) || strings.Contains(words, "meeting notes") {
t.Fatalf("the console shows the op, the machine and every argument — never the pod's note: %q", words)
}
if long := argWords(strings.Repeat("x", 3000)); !strings.Contains(long, "3000 bytes in all") {
t.Fatalf("a long argument is named by its length and digest: %q", long[len(long)-60:])
}
raw, _ := b32decode(tSecret)
approval, err := stepupApproveRequest(dcn, reply.Request, hotp(raw, time.Now().Unix()/stepupPeriod))
if err != nil {
t.Fatal(err)
}
if _, errOut, code := driveJob(t, tAnchor, putJob(approval), ctx, dcn); code == 126 {
t.Fatalf("the console approval must run the op: %d %s", code, errOut)
}
if _, errOut, code := driveJob(t, tAnchor, putJob(approval), ctx, dcn); code != 126 || !strings.Contains(errOut, "already used") {
t.Fatalf("single use: %d %q", code, errOut)
}
// The console path hands a missed tap back on a re-file, once. The first
// request above was approved directly (never read through stepup_status),
// so it is still undelivered: a re-file returns IT, approved.
same, err := stepupNewRequest(dcn, purpose, details)
if err != nil || same != reply.Request {
t.Fatalf("an approved, undelivered ask is the same request: %s %v", same, err)
}
if _, ok := stepupHandOut(dcn, reply.Request); !ok {
t.Fatal("handed out once")
}
id2, err := stepupNewRequest(dcn, purpose, details)
if err != nil || id2 == reply.Request {
t.Fatalf("after delivery the same ask is a new request: %s %v", id2, err)
}
if _, err := stepupApproveRequest(dcn, id2, hotp(raw, time.Now().Unix()/stepupPeriod+1)); err != nil {
t.Fatal(err)
}
out2, _, code := driveJob(t, tAnchor, mgmtJob{Op: "stepup_request", Args: []string{purpose, detailsB64}}, ctx, dcn)
if code != 0 || !strings.Contains(out2, `"approved"`) || !strings.Contains(out2, id2) {
t.Fatalf("a re-filed approved ask is answered approved with its id: %d %s", code, out2)
}
st, _, _ := driveJob(t, tAnchor, mgmtJob{Op: "stepup_status", Args: []string{id2}}, ctx, dcn)
if !strings.Contains(st, `"delivered"`) {
t.Fatalf("handed out once: %s", st)
}
}
// Five refused writes inside ten minutes close a pairing's write lanes; reads
// stay open; the console lifts it.
func TestRepeatedRefusedWritesQuarantineThePairing(t *testing.T) {
setupBox(t)
ctx := t.TempDir()
// A device is pinned, so every refusal below is the host's fault (no
// approval given), the kind that counts.
pub, _ := deviceInit()
_ = pinDeviceKey(pub)
for i := 0; i < quarantineStrikes; i++ {
if _, errOut, code := driveJob(t, tAnchor, putJob(""), ctx, ""); code != 126 || strings.Contains(errOut, "quarantined") {
t.Fatalf("refusal %d must be the gate's, not a hold yet: %d %q", i+1, code, errOut)
}
}
if _, errOut, code := driveJob(t, tAnchor, putJob(""), ctx, ""); code != 126 || !strings.Contains(errOut, "quarantined") {
t.Fatalf("the sixth write must meet the hold: %d %q", code, errOut)
}
// Even a GOOD approval is refused during the hold, and not spent — and a
// revoke is never held back.
hn, _ := os.Hostname()
if _, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "dcn_grant_drop", Args: []string{"7"}}, ctx, t.TempDir()); code == 126 && strings.Contains(errOut, "quarantined") {
t.Fatalf("a tighten must pass a hold: %d %q", code, errOut)
}
approval, _ := deviceSign(mgmtOpPurpose(putOp, hn, putJob("").Args), time.Minute)
if _, errOut, code := driveJob(t, tAnchor, putJob(approval), ctx, ""); code != 126 || !strings.Contains(errOut, "quarantined") {
t.Fatalf("a held pairing's approved write is still refused: %d %q", code, errOut)
}
if _, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "pairing_list"}, ctx, ""); code != 0 {
t.Fatalf("reads stay open under a hold: %d %s", code, errOut)
}
rep := buildEnforcingReport("", nil)
if rep["write_gate"] != true || rep["device"] != true {
t.Fatalf("the report must say the gate is on and a device is pinned: %v", rep)
}
held, _ := rep["quarantined"].([]string)
if len(held) != 1 || held[0] != tAnchor {
t.Fatalf("the report must name the held pairing: %v", rep["quarantined"])
}
if ring, _ := rep["refusals"].([]refusalRecord); len(ring) < quarantineStrikes {
t.Fatalf("the report must carry the refusal ring: %v", rep["refusals"])
}
quarantineClear(tAnchor)
if _, errOut, code := driveJob(t, tAnchor, putJob(approval), ctx, ""); code == 126 {
t.Fatalf("after the console lifts the hold, the approval (never spent) runs: %d %s", code, errOut)
}
// A second hold doubles.
for i := 0; i < quarantineStrikes; i++ {
driveJob(t, tAnchor, putJob(""), ctx, "")
}
until := quarantinedUntil(tAnchor, time.Now())
if until.IsZero() {
t.Fatal("second hold")
}
quarantineClear(tAnchor) // forgiven: strikes reset
for i := 0; i < quarantineStrikes; i++ {
driveJob(t, tAnchor, putJob(""), ctx, "")
}
if u := quarantinedUntil(tAnchor, time.Now()); u.IsZero() || time.Until(u) > quarantineBase+time.Minute {
t.Fatalf("a cleared hold is forgiven — the next one is the base length again: %v", u)
}
// Strikes double when the hold lapses on its own.
f := quarantineLoad()
f.Pairing[tAnchor].Until = time.Now().Add(-time.Second).UTC().Format(time.RFC3339)
_ = quarantineSave(f)
for i := 0; i < quarantineStrikes; i++ {
driveJob(t, tAnchor, putJob(""), ctx, "")
}
if u := quarantinedUntil(tAnchor, time.Now()); u.IsZero() || time.Until(u) < 2*quarantineBase-time.Minute {
t.Fatalf("a repeat hold doubles: %v", u)
}
}
// The person at the machine switches the directives off, and the anchor's
// approved directive is refused anyway.
// The args digest is what the pod derives too: a fixed vector, framed
// "<len>:<bytes>\n" per argument, never JSON.
func TestArgsDigestIsTheFramedSHA256(t *testing.T) {
if got := argsDigest(nil); got != "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
t.Fatalf("no args: %s", got)
}
// sha256 of "1:a\n3:b<c\n" — the host pins the same vector on its side.
if got := argsDigest([]string{"a", "b<c"}); got != "922c052b1872b6644665fa394e99c19aa26037ee0b81b5c4bc22ed4fd34ddd0b" {
t.Fatalf("fixed vector: %s", got)
}
if argsDigest([]string{"a\nb"}) == argsDigest([]string{"a", "b"}) {
t.Fatal("the length prefix must keep one argument apart from two")
}
}
// A directive the box already approved is not re-asked on the next beat, and
// a mirror placement stands until the host disables it.
func TestARepeatedDirectiveRunsOnTheApprovalAlreadyGiven(t *testing.T) {
setupBox(t)
// Both targets below are older than the version this box runs, so each
// needs an approval (a newer one would not: claims_update_forward_test.go).
freshVersionState(t)
clientVersion = "100.0.0"
fetches := 0
man := httptest.NewServer(signedStub(releaseKindBastion, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fetches++
_, _ = w.Write([]byte(`{"version":"0.0.0-test","binaries":{}}`))
})))
defer man.Close()
oldURL := manifestURL
manifestURL = man.URL + "/version.json"
t.Cleanup(func() { manifestURL = oldURL })
maybeSelfUpdate(tAnchor, "", testDirective(t, directiveSelfUpdate, []string{"99.0.0"}, `{"update":{"version":"99.0.0"}}`))
maybeSelfUpdate(tAnchor, "", []byte(`{"update":{"version":"99.0.0"}}`)) // the next beat, no approval carried
if fetches != 2 {
t.Fatalf("the same directive inside the window runs on the approval already given: %d", fetches)
}
maybeSelfUpdate(tAnchor, "", []byte(`{"update":{"version":"98.0.0"}}`))
if fetches != 2 {
t.Fatal("a different target is a different act")
}
if directiveApproved(directiveSelfUpdate, argsDigest([]string{"99.0.0"}), "", time.Now().Add(directiveRepeatWindow+time.Second)) {
t.Fatal("the window lapses")
}
rememberDirective(directiveMirror, "d1", "", true, time.Now())
if !directiveApproved(directiveMirror, "d1", "", time.Now().Add(365*24*time.Hour)) {
t.Fatal("a mirror placement stands")
}
forgetDirective(directiveMirror)
if directiveApproved(directiveMirror, "d1", "", time.Now()) {
t.Fatal("disabled: forgotten")
}
}
func TestTheLocalSwitchCoversTheDirectives(t *testing.T) {
setupBox(t)
// Both targets below are older than the version this box runs, so each
// needs an approval (a newer one would not: claims_update_forward_test.go).
freshVersionState(t)
clientVersion = "100.0.0"
if err := localDenySet(tAnchor, localFamilyDirectives, true); err != nil {
t.Fatalf("directives must be switchable: %v", err)
}
if err := localDenySet(tAnchor, "no-such-family", true); err == nil {
t.Fatal("an unknown family is still refused")
}
fetches := 0
man := httptest.NewServer(signedStub(releaseKindBastion, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fetches++
_, _ = w.Write([]byte(`{"version":"0.0.0-test","binaries":{}}`))
})))
defer man.Close()
oldURL := manifestURL
manifestURL = man.URL + "/version.json"
t.Cleanup(func() { manifestURL = oldURL })
maybeSelfUpdate(tAnchor, "", testDirective(t, directiveSelfUpdate, []string{"99.0.0"}, `{"update":{"version":"99.0.0"}}`))
if fetches != 0 {
t.Fatal("switched off here: even an approved self-update must not fetch")
}
if r := directiveRefusalReport(); r == nil || !strings.Contains(fmt.Sprint(r), "switched off") {
t.Fatalf("the host is told why: %v", r)
}
if err := localDenySet(tAnchor, localFamilyDirectives, false); err != nil {
t.Fatal(err)
}
maybeSelfUpdate(tAnchor, "", testDirective(t, directiveSelfUpdate, []string{"99.0.0"}, `{"update":{"version":"99.0.0"}}`))
if fetches != 1 {
t.Fatal("switched back on: the approved directive proceeds")
}
// The approval is bound to the target: one for 99.0.0 does not roll 98.0.0.
body := testDirective(t, directiveSelfUpdate, []string{"99.0.0"}, `{"update":{"version":"98.0.0"}}`)
maybeSelfUpdate(tAnchor, "", body)
if fetches != 1 {
t.Fatal("an approval for another version must not fetch")
}
}
// No open-ended terms: a DCN store grant or a session grant with no expiry
// does not verify.
func TestOpenEndedTermsAreRefused(t *testing.T) {
priv := setupBox(t) // the anchor's key, pinned at anchoring
pubB64 := anchorVerifyKey()
hn, _ := os.Hostname()
terms := map[string]any{"kind": "dcn_store_grant", "class": "machine", "server": hn, "root": "/srv/x",
"mask": []string{"read"}, "expires": ""}
sign := func() dcnGrantRec {
canonical, _ := json.Marshal(terms)
return dcnGrantRec{TermsCanonical: base64.StdEncoding.EncodeToString(canonical),
Signature: base64.StdEncoding.EncodeToString(ed25519.Sign(priv, canonical))}
}
if _, err := dcnVerifyRec(pubB64, sign()); err == nil || !strings.Contains(err.Error(), "no expiry") {
t.Fatalf("an open-ended store grant must be refused: %v", err)
}
terms["expires"] = time.Now().Add(time.Hour).UTC().Format(time.RFC3339)
if _, err := dcnVerifyRec(pubB64, sign()); err != nil {
t.Fatalf("the same grant with an expiry verifies: %v", err)
}
// The session lane's grant, same rule.
dir := t.TempDir()
st := map[string]any{"entity": 9001, "server": hn, "ops": []string{sessionOpOpen}, "deny": []string{}, "expires": ""}
canonical, _ := json.Marshal(st)
sig := base64.StdEncoding.EncodeToString(ed25519.Sign(priv, canonical))
if _, err := sessionVerifyGrant(dir, tAnchor, hn, sessionOpOpen, base64.StdEncoding.EncodeToString(canonical), sig, ""); err == nil ||
!strings.Contains(err.Error(), "no expiry") {
t.Fatalf("an open-ended session grant must be refused: %v", err)
}
}
// On an anchored box the key a grant is verified under comes from anchoring,
// never from what the host offers on the lane.
func TestAnAnchoredBoxNeverPinsAnOfferedKey(t *testing.T) {
priv := setupBox(t)
want := anchorVerifyKey()
other := base64.StdEncoding.EncodeToString(make([]byte, 32))
f := &dcnGrantFile{Stores: map[string]dcnGrantRec{}}
// The anchor pairing: pinned from anchor.json, an offer that differs is refused.
if pinned, why := pinVerifyKey(f, tAnchor, other); why == "" || pinned {
t.Fatalf("an offered key that is not the anchor's must be refused: %q", why)
}
f = &dcnGrantFile{Stores: map[string]dcnGrantRec{}}
if pinned, why := pinVerifyKey(f, tAnchor, ""); why != "" || !pinned || f.VerifyPubkey != want {
t.Fatalf("the anchor's key is pinned from anchoring: %v %q %s", pinned, why, f.VerifyPubkey)
}
// A member with no verify_key in its grant and nothing pinned keeps
// first-use pinning (the anchor host holds no other host's key yet); the
// anchor's own offer above was never taken.
writePairingState(t, tMember, pairingActive)
future := time.Now().Add(time.Hour).UTC().Format(time.RFC3339)
terms, sig := signTerms(t, priv, grantTerms(tMember, allCaps, future))
if _, errOut, code := driveOp(t, tAnchor, "pairing_grant_sync", []string{terms, sig}); code != 0 {
t.Fatalf("grant: %d %s", code, errOut)
}
f = &dcnGrantFile{Stores: map[string]dcnGrantRec{}}
if pinned, why := pinVerifyKey(f, tMember, other); why != "" || !pinned || f.VerifyPubkey != other {
t.Fatalf("a member with no key from anchoring pins its first offer: %v %q", pinned, why)
}
f = &dcnGrantFile{Stores: map[string]dcnGrantRec{}}
if _, why := pinVerifyKey(f, tMember, ""); why == "" {
t.Fatal("a member with no key from anchoring and no offer is default-deny")
}
// The same member, re-granted WITH verify_key: pinned from the grant.
memberKey := base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...))
gt := grantTerms(tMember, allCaps, future)
gt["verify_key"] = memberKey
terms, sig = signTerms(t, priv, gt)
if _, errOut, code := driveOp(t, tAnchor, "pairing_grant_sync", []string{terms, sig}); code != 0 {
t.Fatalf("re-grant: %d %s", code, errOut)
}
f = &dcnGrantFile{Stores: map[string]dcnGrantRec{}}
if pinned, why := pinVerifyKey(f, tMember, memberKey); why != "" || !pinned || f.VerifyPubkey != memberKey {
t.Fatalf("a member's key is pinned from the anchor-signed grant: %v %q", pinned, why)
}
if _, why := pinVerifyKey(f, tMember, other); why == "" {
t.Fatal("an offer that disagrees with the grant's key is refused")
}
// A local box keeps TOFU.
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
f = &dcnGrantFile{Stores: map[string]dcnGrantRec{}}
if pinned, why := pinVerifyKey(f, tAnchor, other); why != "" || !pinned || f.VerifyPubkey != other {
t.Fatalf("a local box pins the first offer: %v %q", pinned, why)
}
}
// The device derives the purpose it signs and the words it shows from the
// same details; root is never signed there; only the person's own host may
// use the device lane.
func TestTheDeviceSignsOnlyWhatItDerivedItself(t *testing.T) {
setupBox(t) // the device is an anchored box too, anchored to tAnchor
if _, err := deviceInit(); err != nil {
t.Fatal(err)
}
hn, _ := os.Hostname()
details := map[string]any{"kind": "mgmt_op", "op": "ctx_put", "server": "box-7",
"args": []any{"notes/a.txt", "aGk="}, "note": "save the notes"}
raw, _ := json.Marshal(details)
b64 := base64.StdEncoding.EncodeToString(raw)
// A member pairing cannot file an ask on the person's device.
if _, errOut, code := driveJob(t, tMember, mgmtJob{Op: "device_request", Args: []string{b64}}, "", ""); code != 126 || !strings.Contains(errOut, "anchor") {
t.Fatalf("a member must be refused the device lane: %d %q", code, errOut)
}
out, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "device_request", Args: []string{b64}}, "", "")
if code != 0 {
t.Fatalf("device_request: %d %s", code, errOut)
}
var reply struct{ ID string }
_ = json.Unmarshal([]byte(out), &reply)
deviceMu.Lock()
r := deviceRequestsLoad()[reply.ID]
deviceMu.Unlock()
if r.Purpose != mgmtOpPurpose("ctx_put", "box-7", []string{"notes/a.txt", "aGk="}) {
t.Fatalf("the device must derive the purpose from the details: %s", r.Purpose)
}
if !strings.Contains(r.Words, "ctx_put") || !strings.Contains(r.Words, "on box-7") ||
!strings.Contains(r.Words, "notes/a.txt") || strings.Contains(r.Words, "save the notes") {
t.Fatalf("the words name the machine and every argument, and never the pod's free text: %q", r.Words)
}
if r.Server != "box-7" {
t.Fatalf("the request's machine is the details' machine: %q", r.Server)
}
// A kind whose purpose names no machine is not signed here.
pull := map[string]any{"kind": "model_pull", "model": "m", "role": "embed", "max_bytes": float64(1), "replaces": ""}
praw, _ := json.Marshal(pull)
if _, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "device_request", Args: []string{base64.StdEncoding.EncodeToString(praw)}}, "", ""); code != 126 || !strings.Contains(errOut, "console") {
t.Fatalf("a model_pull ask names no machine and is refused on the device: %d %q", code, errOut)
}
// The same ask again is the same request.
out2, _, _ := driveJob(t, tAnchor, mgmtJob{Op: "device_request", Args: []string{b64}}, "", "")
if !strings.Contains(out2, reply.ID) || strings.Contains(out2, "approval") {
t.Fatalf("a repeat of a pending ask returns the same id and no approval: %s", out2)
}
// Status before the tap: pending, no approval.
st, _, _ := driveJob(t, tAnchor, mgmtJob{Op: "device_status", Args: []string{reply.ID}}, "", "")
if !strings.Contains(st, `"pending"`) || strings.Contains(st, "approval") {
t.Fatalf("pending, and no approval leaks: %s", st)
}
if _, err := deviceApprove(reply.ID); err != nil {
t.Fatal(err)
}
st, _, _ = driveJob(t, tAnchor, mgmtJob{Op: "device_status", Args: []string{reply.ID}}, "", "")
var stv struct{ State, Approval string }
_ = json.Unmarshal([]byte(st), &stv)
if stv.State != "approved" || !isDeviceApproval(stv.Approval) {
t.Fatalf("approved, with the signed approval: %s", st)
}
// device_status handed the approval out once; a second read says delivered,
// and the same ask filed again is a NEW request (a new act), never the
// spent approval handed back.
st2, _, _ := driveJob(t, tAnchor, mgmtJob{Op: "device_status", Args: []string{reply.ID}}, "", "")
if !strings.Contains(st2, `"delivered"`) || strings.Contains(st2, "approval") {
t.Fatalf("a second status read repeats nothing: %s", st2)
}
again, _, _ := driveJob(t, tAnchor, mgmtJob{Op: "device_request", Args: []string{b64}}, "", "")
var av struct{ ID, State, Approval string }
_ = json.Unmarshal([]byte(again), &av)
if av.ID == reply.ID || av.State != "pending" || av.Approval != "" {
t.Fatalf("after delivery the same ask is a new request: %s", again)
}
// The tap the pod MISSED: approve, then re-file without a status read —
// the approval is handed back on the file, once.
if _, err := deviceApprove(av.ID); err != nil {
t.Fatal(err)
}
again2, _, _ := driveJob(t, tAnchor, mgmtJob{Op: "device_request", Args: []string{b64}}, "", "")
var av2 struct{ ID, State, Approval string }
_ = json.Unmarshal([]byte(again2), &av2)
if av2.ID != av.ID || av2.State != "approved" || !isDeviceApproval(av2.Approval) {
t.Fatalf("a re-filed approved ask is handed back once: %s", again2)
}
again3, _, _ := driveJob(t, tAnchor, mgmtJob{Op: "device_request", Args: []string{b64}}, "", "")
if strings.Contains(again3, av.ID) {
t.Fatalf("and not twice: %s", again3)
}
// The approval verifies on the governed box (this one, playing box-7's part).
_ = pinDeviceKey(mustPub(t))
if _, err := verifyDeviceApproval(stv.Approval, r.Purpose, time.Now()); err != nil {
t.Fatalf("the signed approval must verify under the pinned key: %v", err)
}
// Root is never signed on a device.
rootDetails := map[string]any{"kind": "session_root", "server": hn, "session_id": "sess-0001"}
rraw, _ := json.Marshal(rootDetails)
if _, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "device_request",
Args: []string{base64.StdEncoding.EncodeToString(rraw)}}, "", ""); code != 126 || !strings.Contains(errOut, "code") {
t.Fatalf("a root ask must be refused on the device: %d %q", code, errOut)
}
// Deny: not asked again.
details2 := map[string]any{"kind": "mgmt_op", "op": "ctx_del", "server": "box-7", "args": []any{"x"}}
raw2, _ := json.Marshal(details2)
out3, _, _ := driveJob(t, tAnchor, mgmtJob{Op: "device_request", Args: []string{base64.StdEncoding.EncodeToString(raw2)}}, "", "")
_ = json.Unmarshal([]byte(out3), &reply)
if err := deviceDeny(reply.ID); err != nil {
t.Fatal(err)
}
if _, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "device_request", Args: []string{base64.StdEncoding.EncodeToString(raw2)}}, "", ""); code != 126 || !strings.Contains(errOut, "refused this") {
t.Fatalf("a denied ask is not asked again: %d %q", code, errOut)
}
}
func mustPub(t *testing.T) string {
t.Helper()
f, err := deviceKeyLoad()
if err != nil {
t.Fatal(err)
}
return f.Pub
}
// An approval is spent when accepted — two workers handed the same one
// cannot both run — and root never opens on a device tap.
func TestAnApprovalIsSpentWhenAcceptedNotAfterTheRun(t *testing.T) {
setupBox(t)
dcn := t.TempDir()
enrollFactor(t, dcn)
hn, _ := os.Hostname()
purpose := mgmtOpPurpose(putOp, hn, putJob("").Args)
raw, _ := b32decode(tSecret)
id, err := stepupIssueApproval(dcn, hotp(raw, time.Now().Unix()/stepupPeriod), purpose)
if err != nil {
t.Fatal(err)
}
if _, err := consumeApproval(dcn, id, purpose); err != nil {
t.Fatal(err)
}
if _, err := consumeApproval(dcn, id, purpose); err == nil || !strings.Contains(err.Error(), "already used") {
t.Fatalf("the second spend must fail: %v", err)
}
if err := approvalOK(dcn, id, purpose); err == nil {
t.Fatal("a spent approval no longer checks out")
}
// A device approval, likewise, and its identity is what the audit records.
pub, _ := deviceInit()
_ = pinDeviceKey(pub)
dev, _ := deviceSign(purpose, time.Minute)
got, err := consumeApproval(dcn, dev, purpose)
if err != nil || !strings.HasPrefix(got, "device:") {
t.Fatalf("device approval spent once: %q %v", got, err)
}
if _, err := consumeApproval(dcn, dev, purpose); err == nil {
t.Fatal("a device approval cannot be spent twice")
}
}
// The harness lane is gated on an anchored box: no approval, no container.
func TestTheHarnessLaneSpendsAnApprovalOnAnAnchoredBox(t *testing.T) {
setupBox(t)
drive := func(spec harnessSpec) (string, int) {
up := websocket.Upgrader{}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ws, err := up.Upgrade(w, r, nil)
if err != nil {
return
}
defer ws.Close()
handleHarnessConn(ws, tAnchor, "")
}))
defer srv.Close()
c, _, err := websocket.DefaultDialer.Dial("ws"+strings.TrimPrefix(srv.URL, "http"), nil)
if err != nil {
t.Fatal(err)
}
defer c.Close()
raw, _ := json.Marshal(spec)
_ = c.WriteMessage(websocket.BinaryMessage, append([]byte("H"), raw...))
var out string
for {
_, data, err := c.ReadMessage()
if err != nil {
t.Fatal(err)
}
switch data[0] {
case 'J':
out = string(data[1:])
case 'X':
var code int
fmt.Sscanf(string(data[1:]), "%d", &code)
return out, code
}
}
}
spec := harnessSpec{HarnessID: "h1", Mode: "echo", Prompt: "ping"}
if out, code := drive(spec); code != 126 || !strings.Contains(out, "needs your approval") {
t.Fatalf("the anchor's harness job without an approval must be refused: %d %s", code, out)
}
spec.Approval = testApproval(t, "harness:run", harnessArgs(spec))
if out, code := drive(spec); code != 0 || !strings.Contains(out, `"echo":"ping"`) {
t.Fatalf("with the approval it runs: %d %s", code, out)
}
if out, code := drive(spec); code != 126 || !strings.Contains(out, "already used") {
t.Fatalf("the same approval does not run a second job: %d %s", code, out)
}
other := spec
other.Image = "someone-elses-image"
if out, code := drive(other); code != 126 {
t.Fatalf("an approval for one image does not run another: %d %s", code, out)
}
}
// `device-approve all` signs only the asks the person was shown.
func TestDeviceApproveAllSignsOnlyWhatWasShown(t *testing.T) {
setupBox(t)
if _, err := deviceInit(); err != nil {
t.Fatal(err)
}
file := func(op string) string {
d := map[string]any{"kind": "mgmt_op", "op": op, "server": "box-7", "args": []any{}}
id, err := deviceNewRequest(d)
if err != nil {
t.Fatal(err)
}
return id
}
a := file("ctx_del")
if err := cmdDeviceApprove("all"); err == nil {
t.Fatal("nothing shown yet: all must refuse")
}
if err := cmdDeviceApprovals(nil); err != nil {
t.Fatal(err)
}
b := file("harness:run") // filed AFTER the person looked
if err := cmdDeviceApprove("all"); err != nil {
t.Fatal(err)
}
deviceMu.Lock()
m := deviceRequestsLoad()
deviceMu.Unlock()
if m[a].State != "approved" || m[b].State != "pending" {
t.Fatalf("shown %s must be approved, unseen %s must stay pending: %s / %s", a[:8], b[:8], m[a].State, m[b].State)
}
if err := cmdDeviceApprove("all"); err == nil {
t.Fatal("the snapshot is spent: all must refuse again until the list is re-read")
}
}
// The harness approval binds everything the run acts on, and the clone URL
// must name the approved repository.
func TestHarnessApprovalBindsTheWholeRunAndTheRepoName(t *testing.T) {
setupBox(t)
pub, _ := deviceInit()
_ = pinDeviceKey(pub)
hn, _ := os.Hostname()
spec := harnessSpec{HarnessID: "h1", Mode: "git", Image: "img", Repo: "git.example/owner/site", Branch: "main",
Prompt: "fix the header", RepoURL: "https://u:tok@git.example/owner/site.git"}
sign := func(sp harnessSpec) string {
a, _ := deviceSign(mgmtOpPurpose("harness:run", hn, harnessArgs(sp)), time.Minute)
return a
}
spec.Approval = sign(spec)
if why := harnessGate(tAnchor, "", spec); why != "" {
t.Fatalf("a matching run passes: %s", why)
}
other := spec
other.Approval = sign(spec)
other.RepoURL = "https://u:tok@git.example/someone/else.git"
if why := harnessGate(tAnchor, "", other); !strings.Contains(why, "not the one the approval names") {
t.Fatalf("a clone URL naming another repo is refused: %q", why)
}
changed := spec
changed.Approval = sign(spec)
changed.Prompt = "rm -rf everything"
if why := harnessGate(tAnchor, "", changed); why == "" {
t.Fatal("a changed prompt is a different act")
}
elsewhere := spec
elsewhere.Approval = sign(spec)
elsewhere.RepoURL = "https://u:tok@evil.example/owner/site.git"
if why := harnessGate(tAnchor, "", elsewhere); !strings.Contains(why, "not the one the approval names") {
t.Fatalf("the same path on another forge is another repository: %q", why)
}
if repoNameOf("https://u:t@H.example/owner/name.git") != "h.example/owner/name" || repoNameOf("nonsense") != "" {
t.Fatal("repoNameOf")
}
// A blank repository on both sides, or a local path as the URL, is refused.
blank := harnessSpec{HarnessID: "h2", Mode: "git", Image: "img", RepoURL: "/srv/app/.git"}
blank.Approval = sign(blank)
if why := harnessGate(tAnchor, "", blank); !strings.Contains(why, "not the one the approval names") {
t.Fatalf("a git run with no repository named is refused: %q", why)
}
local := spec
local.Approval = sign(spec)
local.RepoURL = "file:///srv/app"
if why := harnessGate(tAnchor, "", local); why == "" {
t.Fatal("a file: URL is not the approved forge repository")
}
if clean("a\u009bb\x1bc") != "a b c" {
t.Fatalf("C0 and C1 controls are blanked: %q", clean("a\u009bb\x1bc"))
}
}
// On an anchored box the egress lane refuses anything inward.
func TestAnchoredEgressRefusesLoopbackAndPrivateDestinations(t *testing.T) {
setupBox(t)
ctx := context.Background()
for _, d := range []string{"127.0.0.1:11434", "localhost:80", "10.0.0.5:443", "192.168.1.1:80",
"172.16.4.4:22", "[::1]:80", "[fd12:3456::e:3]:8443", "[fe80::1]:80", "100.64.0.1:80", "169.254.169.254:80"} {
if why := egressInternalRefusal(ctx, d); why == "" {
t.Errorf("%s must be refused on an anchored box", d)
}
}
for _, d := range []string{"8.8.8.8:53", "[2606:4700::1111]:443"} {
if why := egressInternalRefusal(ctx, d); why != "" {
t.Errorf("%s is the public internet: %s", d, why)
}
}
// The address vetted is the address dialled, never the name again.
if addr, why := egressVettedAddr(ctx, "8.8.8.8:53"); why != "" || addr != "8.8.8.8:53" {
t.Fatalf("vetted literal: %q %q", addr, why)
}
if addr, why := egressVettedAddr(ctx, "localhost:80"); why == "" || addr != "" {
t.Fatalf("a refused name yields no address: %q %q", addr, why)
}
// A local box refuses this machine too, and the network around it
// (egress_local_test.go and egress_network_test.go have the rest).
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
stubOwnAddrs(t, loopbackOnly, localRange())
if why := egressInternalRefusal(ctx, "127.0.0.1:11434"); why == "" {
t.Fatal("a local box must refuse its own ollama on the egress lane")
}
if addr, why := egressVettedAddr(ctx, "192.168.1.1:80"); why == "" || addr != "" {
t.Fatalf("a local box refuses its LAN on the egress lane: %q %q", addr, why)
}
}
// A contain approval is remembered with the agent's signature: a reused pid
// is a new act.
func TestARememberedContainDoesNotKillAReusedPid(t *testing.T) {
setupBox(t)
if directiveApproved(directiveContain, argsDigest([]string{"4242"}), "sig-A", time.Now()) {
t.Fatal("nothing remembered yet")
}
rememberDirective(directiveContain, argsDigest([]string{"4242"}), "sig-A", false, time.Now())
if !directiveApproved(directiveContain, argsDigest([]string{"4242"}), "sig-A", time.Now()) {
t.Fatal("the same agent under the same pid is the remembered act")
}
if directiveApproved(directiveContain, argsDigest([]string{"4242"}), "sig-B", time.Now()) {
t.Fatal("another program under that pid is not")
}
}
// The standing context lane cannot touch this box's own record.
func TestTheContextLaneCannotTouchTheSessionRecord(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
writePairing(t, tAnchor)
ctx := t.TempDir()
for _, p := range []string{"sessions/sess-0001.log", "/sessions/x.log", "Sessions/x.log", "sessions", "Sessions",
"./sessions/x.log", "notes/../sessions/x.log", "a/b/../../sessions/x.log"} {
job := mgmtJob{Op: "ctx_put", Args: []string{p, "aGk="}}
if _, errOut, code := driveJob(t, tAnchor, job, ctx, ""); code == 0 || !strings.Contains(errOut, "path rejected") {
t.Fatalf("ctx_put %q must be refused: %d %q", p, code, errOut)
}
if _, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "ctx_del", Args: []string{p}}, ctx, ""); code == 0 || !strings.Contains(errOut, "path rejected") {
t.Fatalf("ctx_del %q must be refused: %d %q", p, code, errOut)
}
}
if _, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "ctx_put", Args: []string{"notes/sessions.txt", "aGk="}}, ctx, ""); code != 0 {
t.Fatalf("an ordinary blob path is fine: %d %s", code, errOut)
}
}
// The extract approval binds the tool, the mime and the host's name for the
// document — never the bytes, never the pull ticket.
func TestTheExtractApprovalBindsStableFields(t *testing.T) {
a := approvalArgs("ctx_extract", []string{`{"tool":"pdf-text","mime":"application/pdf","doc":"ctx:42","ticket":"t-1","b64":"aGk="}`})
if len(a) != 4 || a[0] != "pdf-text" || a[1] != "application/pdf" || a[2] != "ctx:42" || len(a[3]) != 64 {
t.Fatalf("stable fields, and the inline bytes' digest: %v", a)
}
other := approvalArgs("ctx_extract", []string{`{"tool":"pdf-text","mime":"application/pdf","doc":"ctx:42","b64":"b3RoZXI="}`})
if other[3] == a[3] {
t.Fatal("another document under the same label is another act")
}
b := approvalArgs("ctx_extract", []string{`{"tool":"pdf-text","mime":"application/pdf","ticket":"t-9"}`})
if len(b) != 4 || b[2] != "ticket:t-9" || b[3] != "" {
t.Fatalf("pull path with no doc name: the ticket, no bytes: %v", b)
}
if got := approvalArgs("ctx_put", []string{"x", "y"}); len(got) != 2 {
t.Fatalf("other ops bind their args as sent: %v", got)
}
// And the purpose for two calls that differ only in the ticket is the same.
p1 := mgmtOpPurpose("ctx_extract", "box", []string{`{"tool":"t","mime":"m","doc":"ctx:1","ticket":"a"}`})
p2 := mgmtOpPurpose("ctx_extract", "box", []string{`{"tool":"t","mime":"m","doc":"ctx:1","ticket":"b"}`})
if p1 != p2 {
t.Fatal("a re-minted ticket must not change the purpose")
}
}
// The Host's door itself drops the fields only this machine writes: a
// stepup_request that names a local account as its asker is filed without
// it, so nothing downstream (the audit, a deny's strike) ever sees it.
func TestAHostsDoorDropsTheLocalAskerFields(t *testing.T) {
setupBox(t)
ctx, dcn := t.TempDir(), t.TempDir()
enrollFactor(t, dcn)
hn, _ := os.Hostname()
details := map[string]any{"kind": "mgmt_op", "op": putOp, "server": hn, "args": []any{putJob("").Args[0]},
"asked_by_account": "u", "asker": map[string]any{"account": "u"}}
purpose, err := purposeFromDetails(details)
if err != nil {
t.Fatal(err)
}
raw, _ := json.Marshal(details)
out, errOut, code := driveJob(t, tAnchor, mgmtJob{Op: "stepup_request",
Args: []string{purpose, base64.StdEncoding.EncodeToString(raw)}}, ctx, dcn)
if code != 0 {
t.Fatalf("stepup_request: %d %s %s", code, out, errOut)
}
var reply struct{ Request string }
_ = json.Unmarshal([]byte(out), &reply)
r, ok := requestsLoad(dcn)[reply.Request]
if !ok {
t.Fatalf("no request filed: %s", out)
}
for _, k := range []string{"asked_by_account", "asker"} {
if _, ok := r.Details[k]; ok {
t.Fatalf("a Host's request kept %q: %v", k, r.Details)
}
}
}