Review: a path is read as a path in the whole words, and Details say what the desk shows (issue 383)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group fix/383-a-proposal-reads-whole-on-the-phone ready: every member ready, and composed together they pass
mesh/delivery superseded: a newer head of the same pull request

The review of 2026-10-10: a mixed-case path with a digit ("/mnt/Photos_2024/Jochen") read as a
random string and was withheld from the whole words, the symptom again (outward.Secret now judges
a run with a slash piece by piece, as the messenger's CheckSecret does); and the Details line on
masking read, on the phone, as if something there were masked.
This commit is contained in:
jochen
2026-10-10 15:46:55 +02:00
parent 7a92224886
commit 68557b412f
4 changed files with 23 additions and 6 deletions
+1 -1
View File
@@ -178,7 +178,7 @@ func (p settingsProposal) ask(id string, machines []string) (asks.Ask, map[strin
var d strings.Builder var d strings.Builder
fmt.Fprintf(&d, "Fingerprint %s.\n", fingerprint(p.Digest)) fmt.Fprintf(&d, "Fingerprint %s.\n", fingerprint(p.Digest))
if !shownWhole { if !shownWhole {
d.WriteString("Parts shown as ‹address›, ‹path› or ‹withheld› may not leave the mesh where the sender is not proven.\n") d.WriteString("On a channel that does not prove who answers, these values are shown as ‹address›, ‹path› or ‹withheld›.\n")
} }
fmt.Fprintf(&d, "Read it whole, with this fingerprint: settings proposals %s at the controller's terminal, or "+ fmt.Fprintf(&d, "Read it whole, with this fingerprint: settings proposals %s at the controller's terminal, or "+
"mesh-controller.settings with proposal %s through the mesh MCP server.\n", id, id) "mesh-controller.settings with proposal %s through the mesh MCP server.\n", id, id)
+3 -3
View File
@@ -133,7 +133,7 @@ func TestAProposalAsksAtTheLevelApproveWithTheExactChange(t *testing.T) {
} }
for _, line := range []string{ for _, line := range []string{
"Fingerprint " + fingerprint(layerDigest(mountsSources)) + ".", "Fingerprint " + fingerprint(layerDigest(mountsSources)) + ".",
"Parts shown as ‹address›, ‹path› or ‹withheld› may not leave the mesh where the sender is not proven.", "On a channel that does not prove who answers, these values are shown as ‹address›, ‹path› or ‹withheld›.",
"Read it whole, with this fingerprint: settings proposals " + kept(r).ID + " at the controller's terminal", "Read it whole, with this fingerprint: settings proposals " + kept(r).ID + " at the controller's terminal",
"Approved, the layer is set at once and the machine takes it at its next push.", "Approved, the layer is set at once and the machine takes it at its next push.",
} { } {
@@ -141,7 +141,7 @@ func TestAProposalAsksAtTheLevelApproveWithTheExactChange(t *testing.T) {
t.Errorf("Details lack %q:\n%s", line, q.Details) t.Errorf("Details lack %q:\n%s", line, q.Details)
} }
} }
for _, howTo := range []string{"ingerprint", "settings proposals", "mesh-controller.settings", "may not leave", "Approved,"} { for _, howTo := range []string{"ingerprint", "settings proposals", "mesh-controller.settings", "does not prove", "Approved,"} {
if strings.Contains(q.Explanation, howTo) || strings.Contains(q.Whole, howTo) { if strings.Contains(q.Explanation, howTo) || strings.Contains(q.Whole, howTo) {
t.Errorf("the message carries the how-to %q", howTo) t.Errorf("the message carries the how-to %q", howTo)
} }
@@ -261,7 +261,7 @@ func TestAProposalShowsItsValuesWholeOnlyWhereTheSenderIsProvenAndNeverASecret(t
t.Fatal(err) t.Fatal(err)
} }
if q2 := r2.askSent(t); q2.Whole != "" || !strings.HasPrefix(q2.Explanation, "+ font-size: 13\n+ width: 500\nProposed by ") || if q2 := r2.askSent(t); q2.Whole != "" || !strings.HasPrefix(q2.Explanation, "+ font-size: 13\n+ width: 500\nProposed by ") ||
strings.Contains(q2.Details, "may not leave") { strings.Contains(q2.Details, "does not prove who answers") {
t.Errorf("%+v", q2) t.Errorf("%+v", q2)
} }
} }
+16 -2
View File
@@ -105,13 +105,27 @@ func Check(text string, machines ...string) (Refusal, bool) {
// Secret says whether a text carries a secret's shape, the one class that leaves the mesh nowhere — the // Secret says whether a text carries a secret's shape, the one class that leaves the mesh nowhere — the
// messenger's CheckSecret, one for one. It is what an ask's whole words (asks.Ask.Whole, novox/hq issue 383) // messenger's CheckSecret, one for one. It is what an ask's whole words (asks.Ask.Whole, novox/hq issue 383)
// are held to: on a channel that proves who answers, a path or an address is what the operator approves and // are held to: on a channel that proves who answers, a path or an address is what the operator approves and
// is shown; a secret never is. // is shown; a secret never is. A path is read as one: a run with a slash in it is judged piece by piece
// between the slashes, so "/mnt/Photos_2024/Jochen" is a path and not a random string (the review of
// 2026-10-10); the named shapes hold whatever the run holds.
func Secret(text string, machines ...string) (Refusal, bool) { func Secret(text string, machines ...string) (Refusal, bool) {
text = withoutMachines(text, machines) text = withoutMachines(text, machines)
if refusal, ok := secretShape(text); !ok { if refusal, ok := secretShape(text); !ok {
return refusal, false return refusal, false
} }
return randomRun(text) return randomRunOutsidePaths(text)
}
// randomRunOutsidePaths is randomRun with each run that holds a slash judged by its pieces between the slashes.
func randomRunOutsidePaths(text string) (Refusal, bool) {
for _, run := range reRun.FindAllString(text, -1) {
for _, piece := range strings.Split(run, "/") {
if len(piece) >= 20 && looksRandom(piece) {
return Refusal{"secret", "a long random-looking string"}, false
}
}
}
return Refusal{}, true
} }
// secretShape is the secret shapes a pattern names. // secretShape is the secret shapes a pattern names.
+3
View File
@@ -107,6 +107,9 @@ func TestSecretRefusesOnlyASecretsShape(t *testing.T) {
for _, text := range []string{ for _, text := range []string{
"recalbox=smb://nas.lan/recalbox@/mnt/recalbox:ro", "recalbox=smb://nas.lan/recalbox@/mnt/recalbox:ro",
"library=/mnt/library", "library=/mnt/library",
"photos=/mnt/Photos_2024/Jochen",
"games=smb://nas.lan/Recalbox_Games2024",
"/srv/media/Series_Archive/Season01",
"10.77.0.9:53", "10.77.0.9:53",
"jochen@example.com", "jochen@example.com",
"C:\\Users\\jo", "C:\\Users\\jo",