ZDDC/zddc/internal/policy/standing_config_test.go
ZDDC 88ef2dd921 docs(server): correct overstated WORM/config-edit comment; pin two-step demotion
The decider comment claimed standing config-edit "only ever grants VerbA, so it
can never write/delete/create WORM records." True for a single decision, but it
overstated the guarantee: a config-editor who administers a WORM zone can edit
that zone's .zddc (inherit:false drops the embedded worm:), after which ordinary
writes are no longer clamped. That two-step demotion is intended — owning a
subtree's policy includes its worm: marker, and the edit is access-logged — so
WORM is tamper-EVIDENT to its policy owner, not tamper-PROOF.

Rewrite the comment to say so (and note where to gate worm: relaxation behind
elevation if a deployment needs tamper-proof markers), and add
TestStandingConfigEdit_WormDemotionIsTwoStep pinning the boundary (direct WORM
write denied unelevated), the lever (config-edit allowed), and the consequence
(post-demotion write allowed). Surfaced by the deferred-findings triage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 19:57:13 -05:00

118 lines
5.4 KiB
Go

package policy
import (
"context"
"testing"
"codeberg.org/VARASYS/ZDDC/zddc/internal/zddc"
)
// TestStandingConfigEdit pins the elevation-independent config-edit model:
// a subtree admin (admins: cascade) or an `a`-verb holder may edit config
// (ActionAdmin → VerbA) WITHOUT elevating — including above a WORM clamp —
// while WORM *data* writes and the other escape hatches stay behind the
// elevated bypass. See policy.InternalDecider.Allow + zddc.IsConfigEditor.
func TestStandingConfigEdit(t *testing.T) {
d := &InternalDecider{}
dec := func(chain zddc.PolicyChain, p zddc.Principal, action string) bool {
ok, _ := AllowActionFromChainP(context.Background(), d, chain, p, "/proj/probe", action)
return ok
}
alice := func(elev bool) zddc.Principal { return zddc.Principal{Email: "alice@x", Elevated: elev} }
// admins: [alice] — subtree admin via the cascade.
adminChain := zddc.PolicyChain{
Levels: []zddc.ZddcFile{{Admins: []string{"alice@x"}}},
HasAnyFile: true,
}
// acl: alice holds ONLY the `a` verb (config-edit, no rwcd).
aVerbChain := zddc.PolicyChain{
Levels: []zddc.ZddcFile{{ACL: zddc.ACLRules{Permissions: map[string]string{"alice@x": "a"}}}},
HasAnyFile: true,
}
// acl: alice holds rw but NOT a.
rwChain := zddc.PolicyChain{
Levels: []zddc.ZddcFile{{ACL: zddc.ACLRules{Permissions: map[string]string{"alice@x": "rw"}}}},
HasAnyFile: true,
}
// admins: [alice] AND a WORM zone (a non-nil worm list marks the zone).
wormAdminChain := zddc.PolicyChain{
Levels: []zddc.ZddcFile{{Admins: []string{"alice@x"}, Worm: []string{}}},
HasAnyFile: true,
}
cases := []struct {
name string
chain zddc.PolicyChain
p zddc.Principal
action string
want bool
}{
// The headline: a subtree admin edits config without the toggle.
{"subtree admin edits .zddc unelevated", adminChain, alice(false), ActionAdmin, true},
// ...but standing config authority does NOT bleed into data writes.
{"subtree admin data-write still needs elevation", adminChain, alice(false), ActionWrite, false},
{"subtree admin data-write WHEN elevated (bypass)", adminChain, alice(true), ActionWrite, true},
// The `a` verb is standing config-edit on its own, independent of admins:.
{"a-verb holder edits .zddc unelevated", aVerbChain, alice(false), ActionAdmin, true},
{"a-verb holder cannot write data", aVerbChain, alice(false), ActionWrite, false},
// Plain write/read must NOT be able to rewrite policy (no self-escalation).
{"rw-but-not-a cannot edit .zddc", rwChain, alice(false), ActionAdmin, false},
{"rw user can still read", rwChain, alice(false), ActionRead, true},
// A stranger gets nothing.
{"stranger cannot edit .zddc", adminChain, zddc.Principal{Email: "mallory@x"}, ActionAdmin, false},
// Config-edit transcends the WORM clamp (you can fix the policy that
// governs a WORM zone), but WORM data is still protected.
{"config-edit transcends WORM clamp unelevated", wormAdminChain, alice(false), ActionAdmin, true},
{"WORM data write denied to admin unelevated", wormAdminChain, alice(false), ActionWrite, false},
}
for _, tc := range cases {
if got := dec(tc.chain, tc.p, tc.action); got != tc.want {
t.Errorf("%s: got %v, want %v", tc.name, got, tc.want)
}
}
}
// TestStandingConfigEdit_WormDemotionIsTwoStep documents the (intended)
// composition that a single-action view of the decider hides: a config-editor
// who administers a WORM zone cannot write a WORM record directly, but CAN
// demote the zone by editing its own .zddc, after which an ordinary write is
// no longer clamped. WORM is thus tamper-evident to its policy owner, not
// tamper-proof. Pinned so the behavior is an explicit, tested decision — if a
// deployment ever needs WORM markers immutable except under elevation, this is
// the test that must change alongside gating worm: relaxation behind
// IsActiveAdmin in policy.InternalDecider.Allow.
func TestStandingConfigEdit_WormDemotionIsTwoStep(t *testing.T) {
d := &InternalDecider{}
dec := func(chain zddc.PolicyChain, p zddc.Principal, action string) bool {
ok, _ := AllowActionFromChainP(context.Background(), d, chain, p, "/proj/probe", action)
return ok
}
alice := zddc.Principal{Email: "alice@x", Elevated: false} // config-editor, NOT elevated
// Before — alice administers a WORM zone (admins: + a non-nil worm list).
worm := zddc.PolicyChain{
Levels: []zddc.ZddcFile{{Admins: []string{"alice@x"}, Worm: []string{}}},
HasAnyFile: true,
}
// The boundary holds: a direct WORM record write is denied unelevated...
if dec(worm, alice, ActionWrite) {
t.Error("unelevated config-editor must NOT directly write a WORM record")
}
// ...but she CAN edit the zone's policy (VerbA) — the lever for demotion.
if !dec(worm, alice, ActionAdmin) {
t.Error("config-editor should be able to edit the WORM zone's .zddc unelevated")
}
// After — alice has rewritten that .zddc: inherit:false dropped the
// embedded worm: and her acl now grants rwcd (the post-edit cascade the
// file API persists). The subtree is no longer WORM, so her write lands —
// still unelevated. This is step two of the intended demotion.
demoted := zddc.PolicyChain{
Levels: []zddc.ZddcFile{{ACL: zddc.ACLRules{Permissions: map[string]string{"alice@x": "rwcd"}}}},
HasAnyFile: true,
}
if !dec(demoted, alice, ActionWrite) {
t.Error("after the config-editor demotes the zone, the ordinary write should be allowed")
}
}