//go:build !windows // Uses setupBox / driveOp / writePairingState / signTerms / grantTerms / // tAnchor / tMember from anchor_test.go, which are unix-only. buildtags_test.go // enforces the tag. package main // The caps ceiling cannot be reached by an op nobody classified. // // capsAllow used to return "" — allowed — for any op opFamily did not know, so // a member pairing holding any verified grant could be asked for it whatever // the anchor had granted. The tests here are the standing guard against that // returning: one for the DEFAULT (an unclassified op is refused), and one for // the CLASS (no op on this lane is unclassified in the first place). import ( "encoding/base64" "encoding/json" "strings" "testing" "time" ) // EVERY op this box will answer must map to a family. This is the gate that // stops the next op added to either table from silently failing open: without // it, a new handler is reachable by any pairing holding any grant, and nothing // anywhere says so out loud. // // Both tables, because they are two different doors into the same lane — // handlerOps is the no-exec handlers, p8AllowedOps the shell allowlist — and // handleMgmtConnFor puts the same ceiling in front of both. func TestEveryOpOnTheMgmtLaneHasAFamily(t *testing.T) { check := func(table string, ops []string) { t.Helper() var unclassified []string for _, op := range ops { if opFamily(op) == "" { unclassified = append(unclassified, op) } } if len(unclassified) > 0 { t.Fatalf("these %s ops are in NO capability family:\n %s\n"+ "capsAllowIn default-denies an unclassified op, so each of these is refused for\n"+ "every member pairing on an anchored box — including ones the anchor meant to\n"+ "grant. Give each a family in opFamily (anchor.go). If the op genuinely\n"+ "authorizes nothing, say so explicitly with capsFamilyNone at its gate rather\n"+ "than by leaving it unclassified.", table, strings.Join(unclassified, "\n ")) } } handlers := make([]string, 0, len(handlerOps)) for k := range handlerOps { handlers = append(handlers, k) } check("handlerOps", handlers) p8 := make([]string, 0, len(p8AllowedOps)) for k := range p8AllowedOps { p8 = append(p8, k) } check("p8AllowedOps", p8) check("sessionOps", sessionOps) // And the check must not have gone vacuous: an op nobody has classified // still has no family, or the assertion above proves nothing. if opFamily("some_future_op") != "" { t.Fatal("opFamily now answers for an unknown op — the check above asserts nothing") } } // The DEFAULT itself: an op in no family is refused, and the refusal says so // rather than quoting a family it does not have. func TestUnclassifiedOpIsDefaultDenied(t *testing.T) { priv := setupBox(t) writePairingState(t, tMember, pairingPending) future := time.Now().Add(time.Hour).UTC().Format(time.RFC3339) terms, sig := signTerms(t, priv, grantTerms(tMember, []string{"observe", "dcn", "session"}, future)) if _, errOut, code := driveOp(t, tAnchor, "pairing_grant_sync", []string{terms, sig}); code != 0 { t.Fatalf("grant: %d %s", code, errOut) } // A broadly-granted member is still refused an op in no family. why := capsAllow(tMember, "some_op_in_no_family") if why == "" { t.Fatal("an op in no capability family must be refused on an anchored box") } if !strings.Contains(why, "no capability family") { t.Fatalf("the refusal must say the op is in no family, got %q", why) } // The ANCHOR pairing is unaffected: it returns before the family check, // because its user is the admin of this machine. if why := capsAllow(tAnchor, "some_op_in_no_family"); why != "" { t.Fatalf("the anchor pairing must stay unconstrained: %s", why) } // And a LOCAL box gates nothing at all — capsAllow returns on a nil anchor // long before the family check, which is what keeps every local // bastion unchanged by this. t.Setenv("XDG_CONFIG_HOME", t.TempDir()) if boxMode() != modeLocal { t.Fatalf("a box with no mode file must be local, got %s", boxMode()) } if why := capsAllow(tMember, "some_op_in_no_family"); why != "" { t.Fatalf("a LOCAL bastion must not be gated by this: %s", why) } } // stepupDetails builds the b64 details argument for one request kind. func stepupDetails(t *testing.T, d map[string]any) string { t.Helper() raw, err := json.Marshal(d) if err != nil { t.Fatal(err) } return base64.StdEncoding.EncodeToString(raw) } // A step-up is gated on the family of the thing being APPROVED. // // The bypass this closes: a pairing granted only "observe" could raise a // step-up for any of the four kinds that DO authorize a capability of this // machine — a DCN store grant or move plan, a root shell, a model download — // with a purpose and details of its own choosing, putting the prompt in front // of the user. It could not self-approve. The fifth kind, critical_action, // stays reachable by any pairing holding a verified grant and that is // deliberate, not a leftover: the box is not the enforcer there (the platform // executes once it sees an approval), the second factor is per pairing dir, and // approving one consumes no capability of this machine — so there is no family // that could honestly be named for it. See stepupKindFamily. // // The regression it avoids: gating all five as "dcn" would stop a session-only // pairing raising the step-up session:root requires. func TestStepupIsGatedOnTheApprovedFamily(t *testing.T) { // observe only: every kind refused, including the one it might be thought // to be entitled to by holding a grant at all. priv := setupBox(t) writePairingState(t, tMember, pairingPending) future := time.Now().Add(time.Hour).UTC().Format(time.RFC3339) terms, sig := signTerms(t, priv, grantTerms(tMember, []string{"observe"}, future)) if _, errOut, code := driveOp(t, tAnchor, "pairing_grant_sync", []string{terms, sig}); code != 0 { t.Fatalf("grant: %d %s", code, errOut) } for _, kind := range []string{"dcn_store_grant", "dcn_move_plan", "session_root", "model_pull"} { args := []string{"p", stepupDetails(t, map[string]any{"kind": kind})} _, errOut, code := driveOp(t, tMember, "stepup_request", args) if code != 126 || !strings.Contains(errOut, "caps") { t.Fatalf("an observe-only pairing must be refused a %s step-up: %d %s", kind, code, errOut) } } // session only: the root-shell step-up is its own lane's and must pass the // ceiling, while a dcn one is still refused. priv = setupBox(t) writePairingState(t, tMember, pairingPending) future = time.Now().Add(time.Hour).UTC().Format(time.RFC3339) terms, sig = signTerms(t, priv, grantTerms(tMember, []string{"session"}, future)) if _, errOut, code := driveOp(t, tAnchor, "pairing_grant_sync", []string{terms, sig}); code != 0 { t.Fatalf("grant: %d %s", code, errOut) } args := []string{"p", stepupDetails(t, map[string]any{"kind": "session_root"})} if _, errOut, _ := driveOp(t, tMember, "stepup_request", args); strings.Contains(errOut, "caps") { t.Fatalf("a session pairing must pass the ceiling for a session_root step-up: %s", errOut) } args = []string{"p", stepupDetails(t, map[string]any{"kind": "dcn_store_grant"})} if _, errOut, code := driveOp(t, tMember, "stepup_request", args); code != 126 || !strings.Contains(errOut, "caps") { t.Fatalf("a session pairing must still be refused a dcn step-up: %d %s", code, errOut) } // models only: the model_pull step-up passes, a root shell does not. priv = setupBox(t) writePairingState(t, tMember, pairingPending) future = time.Now().Add(time.Hour).UTC().Format(time.RFC3339) terms, sig = signTerms(t, priv, grantTerms(tMember, []string{modelGateOpFamily}, future)) if _, errOut, code := driveOp(t, tAnchor, "pairing_grant_sync", []string{terms, sig}); code != 0 { t.Fatalf("grant: %d %s", code, errOut) } args = []string{"p", stepupDetails(t, map[string]any{"kind": "model_pull"})} if _, errOut, _ := driveOp(t, tMember, "stepup_request", args); strings.Contains(errOut, "caps") { t.Fatalf("a models pairing must pass the ceiling for a model_pull step-up: %s", errOut) } args = []string{"p", stepupDetails(t, map[string]any{"kind": "session_root"})} if _, errOut, code := driveOp(t, tMember, "stepup_request", args); code != 126 || !strings.Contains(errOut, "caps") { t.Fatalf("a models pairing must be refused a root-shell step-up: %d %s", code, errOut) } } // The kind→family map is the whole gate, so it is asserted directly: a wrong // entry here is a bypass that no amount of frame-level testing would name. func TestStepupKindFamilies(t *testing.T) { for kind, want := range map[string]string{ "dcn_store_grant": "dcn", "dcn_move_plan": "dcn", "session_root": "session", "model_pull": modelGateOpFamily, // The box is not the enforcer for a platform critical action, so no // capability of THIS machine is being authorized and there is no family // to name. Holding a verified grant is the ceiling. "critical_action": capsFamilyNone, // Anything purposeFromDetails would refuse anyway. "": capsFamilyNone, "made_up_kind": capsFamilyNone, } { if got := stepupKindFamily(kind); got != want { t.Errorf("stepupKindFamily(%q) = %q, want %q", kind, got, want) } } // Every kind purposeFromDetails accepts must be mapped here, or a real // approval flow is refused for a pairing that should hold it. The list is // the switch in purposeFromDetails (stepup.go). for _, kind := range []string{"dcn_store_grant", "dcn_move_plan", "session_root", "critical_action", "model_pull"} { fam := stepupKindFamily(kind) if fam == "" { t.Fatalf("kind %q maps to the empty family, which default-denies every "+ "pairing including ones that should hold it", kind) } if fam == capsFamilyNone { continue } found := false for _, c := range allCaps { found = found || c == fam } if !found { t.Fatalf("kind %q maps to family %q, which is not in allCaps %v — ungrantable, "+ "so this approval flow could never be opened for any pairing", kind, fam, allCaps) } } } // The local UI decides whether to show a family as HELD by probing capsAllow // with one representative op (uigiven.go:484). Under default-deny an // unclassified probe reads as "not held" for every pairing, so the machine's // own admin page would quietly show a family the pairing does have as absent. // The probes are all classified today; this keeps a rename from changing that. func TestEveryUIProbeIsClassified(t *testing.T) { for _, f := range uiGivenFamilies { fam := opFamily(f.Probe) if fam == "" { t.Fatalf("the %q family probes with %q, which is in no capability family — "+ "capsAllow default-denies it, so this family would read as NOT HELD on the "+ "local UI for every pairing that actually holds it", f.Name, f.Probe) } if fam != f.Name { t.Fatalf("the %q family probes with %q, which resolves to family %q — the probe "+ "must test its OWN family or the page reports the wrong one", f.Name, f.Probe, fam) } } }