Show what a desk's secret prompt is for, and declare mesh-mail's (issue 789)
The desk's prompt named only the module and the secret's key, so the operator asking for one of mesh-mail's mail-credential-* accounts could not tell which one. node-launcher.secret (modules/rofi) now shows the controller's optional what as its own line under the module and secret names, escaped as text so the catalogue's words never reach the prompt as markup; omitted as before when none is declared. mesh-mail's mail-credential-* family now declares that line with the long-form own-secrets shape the controller's manifest parser reads (novox/hq issue 789 part b; part a landed in mesh-controller).
This commit is contained in:
@@ -22,7 +22,8 @@
|
||||
"own-secrets": {
|
||||
"mail-credential-*": {
|
||||
"path": "${dir:state}/mail-credential-*.secret",
|
||||
"issued-by": "outside"
|
||||
"issued-by": "outside",
|
||||
"prompt": "the password of the mail account {member}"
|
||||
}
|
||||
},
|
||||
"tools": [
|
||||
|
||||
@@ -46,6 +46,13 @@ var aName = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}$`)
|
||||
// break, no markup, no punctuation a sentence is written with — so the prompt's words stay this holder's.
|
||||
var aCaller = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._/@ -]{0,79}$`)
|
||||
|
||||
// escapeMarkup is `what` shown as text, never markup (novox/hq issue 789): the window's message is read with
|
||||
// pango markup on, so a catalogue's words reach it with the characters pango gives meaning to turned to their
|
||||
// entities first — never interpreted, whatever a module's manifest holds.
|
||||
var escapeMarkupReplacer = strings.NewReplacer("&", "&", "<", "<", ">", ">")
|
||||
|
||||
func escapeMarkup(s string) string { return escapeMarkupReplacer.Replace(s) }
|
||||
|
||||
// secretQuestion reads the call. The prompt is this holder's words, written from the module, the secret and the
|
||||
// machine (the review of 2026-10-09, M4): it says the mesh's controller asks, which the bus makes true — it
|
||||
// denies these subjects to every principal but the controller — and a caller's own words are refused, so
|
||||
@@ -81,14 +88,37 @@ func secretQuestion(args map[string]any) (SecretQuestion, error) {
|
||||
}
|
||||
askedBy = v
|
||||
}
|
||||
// What the secret is for, in the catalogue's own words (novox/hq issue 789): the controller's own line, read
|
||||
// from the secret's declared `prompt` and never a word the caller chose. One line, so it is shown under the
|
||||
// module and secret names as its own; its characters are never markup, whatever the catalogue holds.
|
||||
what := ""
|
||||
if raw, there := args["what"]; there {
|
||||
v, err := text(args, "what", true)
|
||||
if err != nil {
|
||||
return q, err
|
||||
}
|
||||
if strings.ContainsAny(v, "\n\r") {
|
||||
return q, fmt.Errorf("what %q holds more than one line", raw)
|
||||
}
|
||||
what = v
|
||||
}
|
||||
q.Prompt = names["secret"] + " for " + names["module"]
|
||||
q.Message = fmt.Sprintf("The mesh's controller asks for %s, the own secret of %s on %s.", names["secret"],
|
||||
names["module"], names["node"])
|
||||
if askedBy != "" {
|
||||
q.Message += fmt.Sprintf(" It was asked for by %s.", askedBy)
|
||||
if what != "" {
|
||||
q.Message += "\nFor: " + escapeMarkup(what)
|
||||
}
|
||||
q.Message += " What you type is not shown, and is sealed before it leaves this machine. Type it only if you " +
|
||||
tail := ""
|
||||
if askedBy != "" {
|
||||
tail += fmt.Sprintf(" It was asked for by %s.", askedBy)
|
||||
}
|
||||
tail += " What you type is not shown, and is sealed before it leaves this machine. Type it only if you " +
|
||||
"expect this ask."
|
||||
if what != "" {
|
||||
q.Message += "\n" + strings.TrimSpace(tail)
|
||||
} else {
|
||||
q.Message += tail
|
||||
}
|
||||
key, err := text(args, "seal_to", true)
|
||||
if err != nil {
|
||||
return q, err
|
||||
|
||||
@@ -150,3 +150,61 @@ func TestThePromptIsWrittenHereFromNamesAndSaysTheControllerAsks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// What the secret is for shows as its own line under the module and secret names, in the catalogue's own
|
||||
// words (novox/hq issue 789): read from the secret's declared `prompt`, never a word the caller chose, and
|
||||
// never shown without a declared one — an old module, or one that declares none, prompts as it does today.
|
||||
func TestWhatShowsAsItsOwnLineUnderTheNamesWhenDeclaredAndNothingWhenNot(t *testing.T) {
|
||||
key := base64.StdEncoding.EncodeToString(make([]byte, 32))
|
||||
base := map[string]any{"module": "mesh-mail", "secret": "mail-credential-gmail", "node": "anchor", "seal_to": key}
|
||||
q, err := secretQuestion(base)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(q.Message, "For:") {
|
||||
t.Errorf("a \"For:\" line with no what declared: %q", q.Message)
|
||||
}
|
||||
|
||||
withWhat := map[string]any{}
|
||||
for k, v := range base {
|
||||
withWhat[k] = v
|
||||
}
|
||||
withWhat["what"] = "the password of the mail account gmail"
|
||||
q, err = secretQuestion(withWhat)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(q.Message, "\nFor: the password of the mail account gmail") {
|
||||
t.Errorf("no \"For:\" line of its own: %q", q.Message)
|
||||
}
|
||||
if !strings.Contains(q.Message, "What you type is not shown") {
|
||||
t.Errorf("the rest of the message was lost: %q", q.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// What the secret is for is shown as text, never markup (novox/hq issue 789): the prompt's window reads its
|
||||
// message with markup on, so what the catalogue holds reaches it with the characters markup gives meaning to
|
||||
// turned to their entities first, and a line holding more than one is refused before anybody sees it.
|
||||
func TestWhatIsShownAsTextNeverMarkupAndIsOneLine(t *testing.T) {
|
||||
key := base64.StdEncoding.EncodeToString(make([]byte, 32))
|
||||
base := map[string]any{"module": "mesh-mail", "secret": "mail-credential-gmail", "node": "anchor", "seal_to": key,
|
||||
"what": "the password of <b>the</b> mail & account"}
|
||||
q, err := secretQuestion(base)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(q.Message, "For: the password of <b>the</b> mail & account") {
|
||||
t.Errorf("markup was not escaped: %q", q.Message)
|
||||
}
|
||||
|
||||
for _, bad := range []string{"a\nb", "a\rb"} {
|
||||
multi := map[string]any{}
|
||||
for k, v := range base {
|
||||
multi[k] = v
|
||||
}
|
||||
multi["what"] = bad
|
||||
if _, err := secretQuestion(multi); err == nil {
|
||||
t.Errorf("what=%q holding more than one line was taken", bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user