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)
mesh/delivery merged: source.main moved to novox/mesh-catalog@0a6a5aba682e
mesh/check checking e3b4ecde8cf2 on 5e537f851f08
mesh/delivery merged: source.main moved to novox/mesh-catalog@0a6a5aba682e
mesh/check checking e3b4ecde8cf2 on 5e537f851f08
Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user