From 9e9877e6ec8f6f2524d1705e30323c08d7d290f7 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 2 Nov 2026 12:05:27 +0100 Subject: [PATCH] Let one hand act warrant a retirement of several consumers mesh-hand-acts records one what per act, so an approve of two consumers could never be warranted. The judge reads each word of the act's what as a name, and nox records one act listing the consumers (issue 1126 review 1). Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL --- modules/mesh-vault/cmd/mesh-vault/sensitive.go | 9 +++++++-- modules/mesh-vault/cmd/mesh-vault/sensitive_test.go | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/mesh-vault/cmd/mesh-vault/sensitive.go b/modules/mesh-vault/cmd/mesh-vault/sensitive.go index a9c3e486..1bad865a 100644 --- a/modules/mesh-vault/cmd/mesh-vault/sensitive.go +++ b/modules/mesh-vault/cmd/mesh-vault/sensitive.go @@ -8,7 +8,7 @@ package main // - the operator's account: a caller the runtime proves as `person.`; or // - a recorded hand act: any other proven caller names (`act`) the hand act it recorded through mesh-hand-acts for // this verb, read back here by its id: recorded by that same caller, within HandActWithin, for this verb, and -// naming what it acts on when the verb acts on something named. +// naming what it acts on when the verb acts on something named (several names in one act's what, space-separated). // // Nothing else passes: an unproven caller, a module with no act, an act of another caller, verb or subject. A `by` // argument is a word, never the proof (actOf keeps it beside the proven caller). @@ -65,8 +65,13 @@ func JudgeSensitive(read ActReader, now func() time.Time, caller, verb, act stri case a.Verb != verb && !strings.HasSuffix(a.Verb, "."+verb): return fmt.Errorf("%s: the hand act %s is %q: it warrants no %s", verb, act, a.Verb, verb) } + // mesh-hand-acts records one what; an act for several names lists them space-separated, so each word is a name. + var named []string + for _, arg := range a.Args { + named = append(named, strings.Fields(arg)...) + } for _, name := range about { - if !slices.Contains(a.Args, name) { + if !slices.Contains(named, name) { return fmt.Errorf("%s: the hand act %s names %s, not %s", verb, act, strings.Join(a.Args, " "), name) } } diff --git a/modules/mesh-vault/cmd/mesh-vault/sensitive_test.go b/modules/mesh-vault/cmd/mesh-vault/sensitive_test.go index 1caa959c..962dae66 100644 --- a/modules/mesh-vault/cmd/mesh-vault/sensitive_test.go +++ b/modules/mesh-vault/cmd/mesh-vault/sensitive_test.go @@ -16,6 +16,8 @@ func TestEachSensitiveVerbJudgesItsOwnCaller(t *testing.T) { "old": {ID: "old", By: "novox.mesh-healers", At: now.Add(-25 * time.Hour), Verb: "rotate", Args: []string{"own/g14/app/key"}}, "other": {ID: "other", By: "novox.mesh-config", At: now.Add(-time.Hour), Verb: "rotate", Args: []string{"own/g14/app/key"}}, "del": {ID: "del", By: "novox.mesh-healers", At: now.Add(-time.Hour), Verb: "provisioner_delete", Args: []string{"d-app"}}, + // As mesh-hand-acts records nox's act: one what, the consumers space-separated (review 1 of issue 1126). + "two": {ID: "two", By: "shanks.node-tools", At: now.Add(-time.Minute), Verb: "mesh-vault.provisioner_retire_approve", Args: []string{"c1 c2"}}, } read := func(id string) (*HandActSeen, error) { if a, ok := acts[id]; ok { @@ -41,6 +43,8 @@ func TestEachSensitiveVerbJudgesItsOwnCaller(t *testing.T) { {"an act too old", "novox.mesh-healers", SensitiveRotate, "old", []string{"own/g14/app/key"}, "older than"}, {"an act not recorded", "novox.mesh-healers", SensitiveDelete, "none", []string{"d-app"}, "holds no hand act"}, {"a delete's own act", "novox.mesh-healers", SensitiveDelete, "del", []string{"d-app"}, ""}, + {"one act for two consumers", "shanks.node-tools", SensitiveRetireApprove, "two", []string{"c1", "c2"}, ""}, + {"one act naming two, asked for a third", "shanks.node-tools", SensitiveRetireApprove, "two", []string{"c1", "c3"}, "not c3"}, } { err := JudgeSensitive(read, clock, row.caller, row.verb, row.act, row.about...) if (row.want == "") != (err == nil) || (err != nil && !strings.Contains(err.Error(), row.want)) {