Merge pull request 'A kept call holds a command's first word, never the line (issue 397)' (#210) from fix/397-a-kept-call-holds-no-command-line into main
This commit was merged in pull request #210.
This commit is contained in:
@@ -468,6 +468,31 @@ func kept(args json.RawMessage) json.RawMessage {
|
|||||||
if words, ok := v.([]any); ok && len(words) > 0 {
|
if words, ok := v.([]any); ok && len(words) > 0 {
|
||||||
out[k] = []any{words[0], "(the rest given, not kept)"}
|
out[k] = []any{words[0], "(the rest given, not kept)"}
|
||||||
}
|
}
|
||||||
|
case k == "command":
|
||||||
|
// The controller's own command line: its first word, never the rest, as a mesh-cli line's
|
||||||
|
// first word is. `settings set <module> '<value>' --node <node>` through the `command` verb
|
||||||
|
// put the value itself in this record, which answers anyone who may call the seat (novox/hq
|
||||||
|
// issue 397). Its words are parted by any whitespace, because the verb's own splitter parts
|
||||||
|
// them on a space, a tab or a newline, and a line written with tabs is the same line. The
|
||||||
|
// first word is capped as every other string here is: the record is written before the verb
|
||||||
|
// judges the line, so one word may be anything a caller sent, a token included.
|
||||||
|
if !isString {
|
||||||
|
out[k] = "(given, not kept)"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
words := strings.Fields(s)
|
||||||
|
if len(words) == 0 {
|
||||||
|
out[k] = ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
first := words[0]
|
||||||
|
if len(first) > 120 {
|
||||||
|
first = first[:120] + "…"
|
||||||
|
}
|
||||||
|
if len(words) > 1 {
|
||||||
|
first += " (the rest given, not kept)"
|
||||||
|
}
|
||||||
|
out[k] = first
|
||||||
case isString && len(s) <= 120:
|
case isString && len(s) <= 120:
|
||||||
out[k] = s
|
out[k] = s
|
||||||
case isString:
|
case isString:
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
|
|
||||||
@@ -166,6 +167,55 @@ func TestACallKeepsNoSettingsOrSecrets(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A command line's own words may carry a setting's value or a secret, so only its first word is kept
|
||||||
|
// — as a mesh-cli line's is (novox/hq issue 397). Each case says the whole of what is kept, because
|
||||||
|
// what matters is as much what is left out as what is there.
|
||||||
|
func TestACallKeepsNoCommandLine(t *testing.T) {
|
||||||
|
long := strings.Repeat("s3cret", 50) // one word, 300 bytes: a token, as far as this can tell
|
||||||
|
for line, want := range map[string]string{
|
||||||
|
`settings set notes 'the operator's own passphrase' --node laptop`: "settings (the rest given, not kept)",
|
||||||
|
`settings set notes --values {"token":"s3cret"}`: "settings (the rest given, not kept)",
|
||||||
|
// Parted by a tab or a newline, which the verb's splitter reads as this line's words too.
|
||||||
|
"settings\tset\tnotes\tthe-operators-passphrase": "settings (the rest given, not kept)",
|
||||||
|
"builds\n--limit 5": "builds (the rest given, not kept)",
|
||||||
|
"builds\t--limit 5": "builds (the rest given, not kept)",
|
||||||
|
// Its first word is kept, and the spaces before it are not part of it.
|
||||||
|
` node show laptop`: "node (the rest given, not kept)",
|
||||||
|
// A command of one word is kept whole: there is nothing after it to withhold.
|
||||||
|
`builds`: "builds",
|
||||||
|
// One word is still capped, as every other string in a kept record is.
|
||||||
|
long: long[:120] + "…",
|
||||||
|
} {
|
||||||
|
raw, err := json.Marshal(map[string]any{"command": line})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var got struct{ Command string }
|
||||||
|
if err := json.Unmarshal(kept(raw), &got); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got.Command != want {
|
||||||
|
t.Errorf("%q is kept as %q; want %q", line, got.Command, want)
|
||||||
|
}
|
||||||
|
// Whole words, so that "set" can be looked for although "settings" is kept, and "show" cannot
|
||||||
|
// be found in a word such as "shown".
|
||||||
|
for _, never := range []string{"set", "notes", "laptop", "show", "passphrase", "operators"} {
|
||||||
|
for _, word := range strings.FieldsFunc(got.Command, func(r rune) bool { return !unicode.IsLetter(r) }) {
|
||||||
|
if word == never {
|
||||||
|
t.Errorf("%q is kept as %q: it carries the word %q", line, got.Command, never)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a string, so its first word cannot be taken: none of it is kept. The verb refuses such a
|
||||||
|
// call, but the record is written before it judges it.
|
||||||
|
raw, _ := json.Marshal(map[string]any{"command": []string{"settings", "set", "notes", "s3cret"}})
|
||||||
|
if got := string(kept(raw)); strings.Contains(got, "s3cret") {
|
||||||
|
t.Errorf("kept %s", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only the newest KeptCalls are kept.
|
// Only the newest KeptCalls are kept.
|
||||||
func TestTheLogKeepsTheNewest(t *testing.T) {
|
func TestTheLogKeepsTheNewest(t *testing.T) {
|
||||||
l := NewCallLog()
|
l := NewCallLog()
|
||||||
|
|||||||
Reference in New Issue
Block a user