Every value an approval sets is in the note, whole where the sender is proven; the note is kept whole (issue 383)
mesh/merge-gate head bb5a342863ee merged into trunk 9c166398f204 — pass: builds claude-code, desk-channel, mesh-knowledge, messenger, telegram → ace, g14, …
mesh/repo-check head bb5a342863ee merged into trunk 9c166398f204 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery-group group fix/383-an-ask-reads-at-a-glance checking: 1 of 2 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head bb5a342863ee merged into trunk 9c166398f204 — pass: builds claude-code, desk-channel, mesh-knowledge, messenger, telegram → ace, g14, …
mesh/repo-check head bb5a342863ee merged into trunk 9c166398f204 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery-group group fix/383-an-ask-reads-at-a-glance checking: 1 of 2 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
The review of mesh-controller #304: a proposal of more than three keys showed only the names of the rest, and its Details could carry no value, so Approve bound values the operator never saw. The router now shows an ask's WholeDetails in the note in place of its Details on a kind that proves who answers, held to the secret rule alone, and refuses an ask whose whole details carry a secret's shape. Telegram keeps the note whole when a message is too long, cutting the body first and saying so, and escapes the note as every other part. SDK at mesh-sdk f474544.
This commit is contained in:
@@ -172,22 +172,39 @@ func (h *Holder) openAsk(r *Record) *AskRecord {
|
||||
// rule, is shown. The ask's state does not matter: a message edited to say how the ask ended still shows what
|
||||
// was approved.
|
||||
func (h *Holder) wholeOf(r *Record, kind string) string {
|
||||
if a := h.provenAsk(r, kind); a != nil {
|
||||
return a.Whole
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// wholeDetailsOf is an ask's Details with their values whole (asks.Ask.WholeDetails), shown in the note in place of
|
||||
// its Details where wholeOf would show its whole words: every value an approval sets, so the operator reads exactly
|
||||
// what they approve (the review of mesh-controller #304). Empty elsewhere.
|
||||
func (h *Holder) wholeDetailsOf(r *Record, kind string) string {
|
||||
if a := h.provenAsk(r, kind); a != nil {
|
||||
return a.WholeDetails
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// provenAsk is the ask of an ask's own record when the kind proves who answers and was given the ask's answers;
|
||||
// nil otherwise.
|
||||
func (h *Holder) provenAsk(r *Record, kind string) *asks.Ask {
|
||||
if r == nil || r.Origin != "ask" || r.Ask == "" || kind == "" || h.Bench == nil {
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
e := h.askOf(r.Ask)
|
||||
if e == nil || e.rec.Ask.Whole == "" {
|
||||
return ""
|
||||
}
|
||||
if !h.Bench.Promises(kind, "verified-sender") {
|
||||
return ""
|
||||
if e == nil || !h.Bench.Promises(kind, "verified-sender") {
|
||||
return nil
|
||||
}
|
||||
for _, tk := range e.rec.Tickets {
|
||||
if tk.Kind == kind {
|
||||
return e.rec.Ask.Whole
|
||||
a := e.rec.Ask
|
||||
return &a
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
// carriers are the kinds that can carry an answer of a level now: ready, offering a choice, and for
|
||||
@@ -474,7 +491,7 @@ func (h *Holder) TakeAsk(verb, asker string, body json.RawMessage) error {
|
||||
// The whole words are shown only where the sender is proven (wholeOf), so a path or an address may stand
|
||||
// in them; a secret's shape leaves the mesh nowhere, and an ask carrying one is refused as a whole, never
|
||||
// shown with its explanation alone (novox/hq issue 383).
|
||||
if refusal, ok := CheckSecret(a.Whole); !ok {
|
||||
if refusal, ok := CheckSecret(a.Whole + "\n" + a.WholeDetails); !ok {
|
||||
h.refuseAsk(asker, a, "its whole words carry "+refusal.String()+", which may not leave the mesh on any channel: withhold it")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -885,11 +885,17 @@ func (h *Holder) sayOn(r *Record, what, kind string) Message {
|
||||
note = append(note, line)
|
||||
}
|
||||
}
|
||||
for _, line := range strings.Split(strings.TrimSpace(rec.Ask.Details), "\n") {
|
||||
// On a kind that proves who answers, the Details whole (issue 383): every value an approval sets, held to
|
||||
// the secret rule alone, as the whole words are.
|
||||
details, passes := rec.Ask.Details, Check
|
||||
if wd := h.wholeDetailsOf(r, kind); wd != "" {
|
||||
details, passes = wd, CheckSecret
|
||||
}
|
||||
for _, line := range strings.Split(strings.TrimSpace(details), "\n") {
|
||||
if line = strings.TrimSpace(line); line == "" {
|
||||
continue
|
||||
}
|
||||
if refusal, ok := Check(line); !ok {
|
||||
if refusal, ok := passes(line); !ok {
|
||||
line = "(a line carrying " + refusal.What + " is withheld here)"
|
||||
}
|
||||
note = append(note, line)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -153,3 +154,57 @@ func TestAnAskWhoseWholeWordsCarryASecretIsRefused(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The review of mesh-controller #304: a proposal of five keys, one of them 300 characters long, shows two keys at
|
||||
// a glance; every key and every value, whole, is in the note on a kind that proves who answers, so the operator can
|
||||
// read exactly what Approve sets. The desk, which proves nothing, shows them masked.
|
||||
func TestEveryKeyAndItsWholeValueIsInTheNoteWhereTheSenderIsProven(t *testing.T) {
|
||||
h, tg, _, c, store, _ := newRouter(t)
|
||||
link(h, store, c, "42", c.now().Add(-2*time.Hour))
|
||||
var long strings.Builder
|
||||
long.WriteString("library=/storage")
|
||||
for i := 0; long.Len() < 300; i++ {
|
||||
long.WriteString(fmt.Sprintf("/album-%02d", i))
|
||||
}
|
||||
value := long.String()[:300]
|
||||
keys := map[string]string{"a": "1", "b": "2", "c": "3", "d": "4", "shares": value}
|
||||
masked := map[string]string{"a": "1", "b": "2", "c": "3", "d": "4", "shares": "library=‹path›"}
|
||||
lines := func(values map[string]string) string {
|
||||
var out []string
|
||||
for _, k := range []string{"a", "b", "c", "d", "shares"} {
|
||||
out = append(out, "+ "+k+": "+values[k])
|
||||
}
|
||||
return strings.Join(out, "\n")
|
||||
}
|
||||
a := proposalAsk(c, "s-9")
|
||||
a.Explanation = "Changes 5 setting(s) of mounts on shanks.\n+ a: 1\n+ b: 2\nand 3 more, each whole below: c, d, shares"
|
||||
a.Whole = ""
|
||||
a.Details = "Fingerprint 1234 5678 9abc def0 1234 5678.\n" + lines(masked)
|
||||
a.WholeDetails = "Fingerprint 1234 5678 9abc def0 1234 5678.\n" + lines(keys)
|
||||
submit(t, h, "mesh-controller", a)
|
||||
settle(h, c)
|
||||
if len(tg.sends) != 1 {
|
||||
t.Fatalf("telegram got %d messages", len(tg.sends))
|
||||
}
|
||||
phone := tg.sends[0]
|
||||
for k, v := range keys {
|
||||
if !strings.Contains(phone.Note, "+ "+k+": "+v) {
|
||||
t.Errorf("the phone's note lacks %s whole:\n%s", k, phone.Note)
|
||||
}
|
||||
}
|
||||
if strings.Contains(phone.Body, "/storage") || strings.Count(phone.Body, "\n") > 3 {
|
||||
t.Errorf("the body is not the glance:\n%s", phone.Body)
|
||||
}
|
||||
desk := h.sayOn(h.open["ask.mesh-controller.s-9"], "raised", "desktop")
|
||||
if strings.Contains(desk.Text(), "/storage") || !strings.Contains(desk.Note, "+ shares: library=‹path›") {
|
||||
t.Errorf("the desk shows a value whole, or not every key:\n%s", desk.Note)
|
||||
}
|
||||
// A secret's shape in the whole details refuses the ask as a whole.
|
||||
b := proposalAsk(c, "s-10")
|
||||
b.WholeDetails = "+ token: ghp_abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
submit(t, h, "mesh-controller", b)
|
||||
settle(h, c)
|
||||
if _, kept := store.asks["mesh-controller.s-10"]; kept || len(tg.sends) != 1 {
|
||||
t.Errorf("an ask whose whole details carry a secret was taken")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user