From e3b4ecde8cf2c356055269f7865439a230c2cedc Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 22:42:00 +0100 Subject: [PATCH] Name the copy wait's ownership mark nonce, not token, so the state accepts it; the copy test's state refuses credential names as the bus does (issue 1966) Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../cmd/mesh-build-image/copy.go | 15 ++++---- .../cmd/mesh-build-image/helpers_test.go | 38 +++++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/modules/mesh-build-image/cmd/mesh-build-image/copy.go b/modules/mesh-build-image/cmd/mesh-build-image/copy.go index 28655df7e..21fe20ecc 100644 --- a/modules/mesh-build-image/cmd/mesh-build-image/copy.go +++ b/modules/mesh-build-image/cmd/mesh-build-image/copy.go @@ -99,7 +99,7 @@ func (m *Module) copy(ctx context.Context, args map[string]any) (any, error) { now = m.Now } asked := CopyWait{Digest: digest, Module: module, Artifact: name, From: from, Node: m.Node, - Asked: now().UTC(), Token: strconv.FormatInt(now().UnixNano(), 36) + "-" + m.Node} + Asked: now().UTC(), Nonce: strconv.FormatInt(now().UnixNano(), 36) + "-" + m.Node} // Waited for before the ask, in memory and in the state mirror-waits, so an outcome said at once, or heard by another // instance of the module (the seat's events reach one of them), is not missed. heard, stop := m.Outcomes.Expect(digest) @@ -107,7 +107,7 @@ func (m *Module) copy(ctx context.Context, args map[string]any) (any, error) { if _, err := m.Outcomes.Waits.Put(key, asked); err != nil { return nil, fmt.Errorf("%s%s: keeping the copy's wait in %s: %v", builds.NotStored, name, MirrorWaitsState, err) } - defer m.Outcomes.drop(key, asked.Token) + defer m.Outcomes.drop(key, asked.Nonce) said := buildstep.NarrateSeat(ctx, buildstep.ImageSeat, "mirror of "+from) raw, err := m.call()("seat:"+buildstep.ImageSeat+"."+reg.VerbMirror, reg.MirrorInput{Name: module + "/" + name, Source: from, Digest: digest}) if err != nil { @@ -170,7 +170,8 @@ const MirrorWaitsState = "mirror-waits" // MirrorWaitKey is a copy's key in mirror-waits: its digest, with the colon a key cannot hold as a dash. func MirrorWaitKey(digest string) string { return strings.Replace(digest, ":", "-", 1) } -// CopyWait is one value of mirror-waits: the copy asked, and its outcome once heard. +// CopyWait is one value of mirror-waits: the copy asked, and its outcome once heard. Nonce tells this copy's wait from +// a later copy's of the same digest; it is no credential, and no field here may be named as one (the state refuses it). type CopyWait struct { Digest string `json:"digest"` Module string `json:"module"` @@ -178,7 +179,7 @@ type CopyWait struct { From string `json:"from"` Node string `json:"node"` Asked time.Time `json:"asked"` - Token string `json:"token"` + Nonce string `json:"nonce"` Outcome *MirrorOutcome `json:"outcome,omitempty"` } @@ -281,9 +282,9 @@ func (o *Outcomes) kept(key string) (MirrorOutcome, bool) { return *w.Outcome, true } -// drop removes key's wait when it is still the copy's own (token): a later copy of the same digest keeps its own. -func (o *Outcomes) drop(key, token string) { - if w, err := o.read(key); err == nil && w != nil && w.Token == token { +// drop removes key's wait when it is still the copy's own (nonce): a later copy of the same digest keeps its own. +func (o *Outcomes) drop(key, nonce string) { + if w, err := o.read(key); err == nil && w != nil && w.Nonce == nonce { _ = o.Waits.Delete(key) } } diff --git a/modules/mesh-build-image/cmd/mesh-build-image/helpers_test.go b/modules/mesh-build-image/cmd/mesh-build-image/helpers_test.go index 024543477..4e01d5282 100644 --- a/modules/mesh-build-image/cmd/mesh-build-image/helpers_test.go +++ b/modules/mesh-build-image/cmd/mesh-build-image/helpers_test.go @@ -115,6 +115,9 @@ func (w *memWaits) Put(key string, value any) (uint64, error) { if err != nil { return 0, err } + if f := credentialShaped(b); f != "" { + return 0, fmt.Errorf("the state refuses a field %q, which names a credential", f) + } w.mu.Lock() if w.m == nil { w.m = map[string]json.RawMessage{} @@ -339,3 +342,38 @@ func tool(t *testing.T, m *Module, name string) func(map[string]any) (any, error t.Fatalf("no tool %s", name) return nil } + +// credentialShaped is the first field name in a JSON value that ends as the state's guard refuses (the bus's +// credentialEndings), or "": the in-memory state refuses it as the bus does, so every copy test meets the guard. +func credentialShaped(value []byte) string { + var v any + if json.Unmarshal(value, &v) != nil { + return "" + } + var walk func(any) string + walk = func(v any) string { + switch t := v.(type) { + case map[string]any: + for k, x := range t { + norm := strings.NewReplacer("-", "", "_", "", " ", "").Replace(strings.ToLower(k)) + for _, end := range []string{"password", "passwd", "secret", "token", "credential", "credentials", + "authorization", "apikey", "privatekey", "accesskey", "cookie"} { + if strings.HasSuffix(norm, end) { + return k + } + } + if f := walk(x); f != "" { + return f + } + } + case []any: + for _, x := range t { + if f := walk(x); f != "" { + return f + } + } + } + return "" + } + return walk(v) +}