FLEX-WP-0040-T07: compact-sitting v14 admits present-existing requester
v14 (proposed, sha256:8911458f36f1a60598aa19592a2d752a74dffe2617fd86238407ac87bc300fde) keeps every v13 decision and admits approval requester service:nk-bind-executor only from submitter nk-bind-executor, memo class and approval action bounded-execution, an aal2 human reviewer, and never the approval actor itself (GH-DEC-2026-026 class C2). v13 stays unchanged as rollback reference. The admission generator skips present_existing registry rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 5502@bnt-lap001 Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
This commit is contained in:
parent
18db517e00
commit
2de0f862f9
10 changed files with 1414 additions and 2 deletions
131
internal/policy/informed_decision_present_existing_test.go
Normal file
131
internal/policy/informed_decision_present_existing_test.go
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package policy_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/netkingdom/access-engine/internal/policy"
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
)
|
||||
|
||||
// FLEX-WP-0040-T07: compact-sitting v14 admits the present-existing requester
|
||||
// service:nk-bind-executor (INFD-WP-0010, GH-DEC-2026-026 class C2). v13 stays
|
||||
// in examples/informed-decision-lifecycle-review as the rollback reference.
|
||||
func TestInformedDecisionPresentExistingV14(t *testing.T) {
|
||||
dir := filepath.Join(repoRoot(t), "examples", "informed-decision-present-existing-review")
|
||||
pkg, err := policy.LoadAndValidateFile(context.Background(), filepath.Join(dir, "policy.md"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !pkg.Valid {
|
||||
t.Fatalf("invalid package: %+v", pkg.Validation)
|
||||
}
|
||||
if pkg.Metadata.ID != "informed-decision.compact-sitting" || pkg.Metadata.Version != "v14" {
|
||||
t.Fatalf("package %s %s", pkg.Metadata.ID, pkg.Metadata.Version)
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(dir, "pin.json"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var pin map[string]string
|
||||
if err := json.Unmarshal(raw, &pin); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pin["package_digest"] != pkg.Digest() || pin["version"] != pkg.Metadata.Version || pin["package"] != pkg.Metadata.ID {
|
||||
t.Fatalf("stale pin %+v; digest %s", pin, pkg.Digest())
|
||||
}
|
||||
|
||||
base := func() map[string]any {
|
||||
return map[string]any{
|
||||
"id": "v14", "tenant": "tenant:platform",
|
||||
"subject": map[string]any{"id": "founder-human", "type": "human", "tenant": "tenant:platform", "attributes": map[string]any{
|
||||
"principal_type_source": "authentication-derived", "tenant_source": "registration-supplied", "groups": []any{"net-kingdom-admins"},
|
||||
"assurance": map[string]any{"level": "aal2", "mfa": true, "source": "key-cape", "methods": []any{"pwd", "otp"}, "at": time.Now().Unix()},
|
||||
}},
|
||||
"resource": map[string]any{"id": "memo:bea-bind-rotation-01", "type": "decision-memo", "system": "informed-decision", "tenant": "tenant:platform"},
|
||||
"action": "accept",
|
||||
"context": map[string]any{
|
||||
"review_group": "net-kingdom-admins", "memo_class": "bounded-execution", "intake_submitter": "nk-bind-executor",
|
||||
"approval_requester": "service:nk-bind-executor", "approval_human_control": true, "approval_action": "bounded-execution",
|
||||
"memo_version": 1, "approval_id": "706f4205-aaa7-44f6-be30-d0f57664d69c",
|
||||
"approval_binding_digest": "sha256:c8a1a9c8fb22616e3aacfb877ba7974feb5eedaed491f856a6c90719c502ebe4",
|
||||
},
|
||||
}
|
||||
}
|
||||
subject := func(d map[string]any) map[string]any { return d["subject"].(map[string]any) }
|
||||
assurance := func(d map[string]any) map[string]any {
|
||||
return subject(d)["attributes"].(map[string]any)["assurance"].(map[string]any)
|
||||
}
|
||||
ctx := func(d map[string]any) map[string]any { return d["context"].(map[string]any) }
|
||||
tests := []struct {
|
||||
name, effect, reason string
|
||||
change func(map[string]any)
|
||||
}{
|
||||
{"executor approval accepted by founder at aal2", "allow", "present_existing_eligible", func(map[string]any) {}},
|
||||
{"read", "allow", "present_existing_eligible", func(d map[string]any) { d["action"] = "read" }},
|
||||
{"actor may not approve its own approval", "deny", "self_approval_refused", func(d map[string]any) { subject(d)["id"] = "service:nk-bind-executor" }},
|
||||
{"submitter slug may not approve", "deny", "self_approval_refused", func(d map[string]any) { subject(d)["id"] = "nk-bind-executor" }},
|
||||
{"aal1 refused", "deny", "identity_refused", func(d map[string]any) { assurance(d)["level"] = "aal1" }},
|
||||
{"stale mfa refused", "deny", "stale_mfa", func(d map[string]any) { assurance(d)["at"] = time.Now().Unix() - 901 }},
|
||||
{"other approval action refused", "deny", "approval_action_not_admitted", func(d map[string]any) { ctx(d)["approval_action"] = "rotate" }},
|
||||
{"missing approval action refused", "deny", "approval_action_not_admitted", func(d map[string]any) { delete(ctx(d), "approval_action") }},
|
||||
{"other submitter refused", "deny", "unknown_submitter", func(d map[string]any) { ctx(d)["intake_submitter"] = "state-hub" }},
|
||||
{"other class refused", "deny", "unknown_class", func(d map[string]any) { ctx(d)["memo_class"] = "decision-record" }},
|
||||
{"unregistered service requester refused", "deny", "unknown_class", func(d map[string]any) { ctx(d)["approval_requester"] = "service:other" }},
|
||||
{"v13 informed-decision requester unchanged", "allow", "attribute_eligible", func(d map[string]any) {
|
||||
ctx(d)["approval_requester"] = "informed-decision"
|
||||
ctx(d)["intake_submitter"] = "state-hub"
|
||||
ctx(d)["memo_class"] = "decision-record"
|
||||
delete(ctx(d), "approval_action")
|
||||
}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
d := base()
|
||||
tc.change(d)
|
||||
encoded, _ := json.Marshal(d)
|
||||
var request api.CheckRequest
|
||||
if err := json.Unmarshal(encoded, &request); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := pkg.Evaluate(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(got.Effect) != tc.effect || got.Reason != tc.reason {
|
||||
t.Fatalf("got %s %s; want %s %s", got.Effect, got.Reason, tc.effect, tc.reason)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A present-existing registry row is skipped by the generated sets: it is
|
||||
// admitted only by the owner-authored present_existing_profiles.
|
||||
func TestAdmissionGeneratorSkipsPresentExistingRows(t *testing.T) {
|
||||
root := repoRoot(t)
|
||||
dir := t.TempDir()
|
||||
registry := filepath.Join(dir, "submitters.json")
|
||||
body := `{
|
||||
"system:serviceaccount:sso:nk-bind-executor": {
|
||||
"submitter_id": "nk-bind-executor",
|
||||
"memo_classes": ["bounded-execution"],
|
||||
"review_groups": ["net-kingdom-admins"],
|
||||
"daily_limit": 10,
|
||||
"present_existing": {"approval_actor": "service:nk-bind-executor", "actions": ["bounded-execution"]}
|
||||
}
|
||||
}`
|
||||
if err := os.WriteFile(registry, []byte(body), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
check := exec.Command("python3", filepath.Join(root, "tools/gen_informed_decision_admission.py"),
|
||||
"--source", registry, "--policy", sittingPolicyPath(t), "--check")
|
||||
if out, err := check.CombinedOutput(); err != nil || !strings.Contains(string(out), "0 submitters, 0 classes; in sync") {
|
||||
t.Fatalf("present-existing row changed the generated sets: %v\n%s", err, out)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue