diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 22:31:12 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 22:31:12 +0200 |
| commit | a91b713dcec4d17f76155f0cd6b26903c59b9c19 (patch) | |
| tree | 44b1354e152c71a978738ee6ec47164ccef10b81 /cmd/krino/hostile_test.go | |
| parent | c03a72f1d7b598c1fe8fd01bb1f5bbfbd0256313 (diff) | |
| download | krino-a91b713dcec4d17f76155f0cd6b26903c59b9c19.tar.gz krino-a91b713dcec4d17f76155f0cd6b26903c59b9c19.zip | |
plan 10: stderr messages cannot be split by quoted newlines; config paths escaped; krino new refuses control characters; independent terminal oracle
Diffstat (limited to 'cmd/krino/hostile_test.go')
| -rw-r--r-- | cmd/krino/hostile_test.go | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/cmd/krino/hostile_test.go b/cmd/krino/hostile_test.go index d5ce041..16a5b19 100644 --- a/cmd/krino/hostile_test.go +++ b/cmd/krino/hostile_test.go @@ -10,6 +10,8 @@ import ( "strings" "testing" "time" + + "krino/internal/journal" ) // hostileNames are file names a download can carry that try to take over @@ -104,6 +106,36 @@ func TestHostileNamesNeverReachTheTerminal(t *testing.T) { checkAny("-\x1b[2Jflag.txt") os.Remove(link) + // A newline in a name given on the command line must not start a line + // that reads as krino's own (re-review term F1). + forged := filepath.Join(dl, "gone\nkrino: FORGED line") + for _, line := range strings.Split(checkAny("explain", forged), "\n") { + if strings.HasPrefix(line, "krino: FORGED") { + t.Errorf("a quoted name forged a stderr line: %q", line) + } + } + + // Hand-written hostile log fields: a damaged or edited log is shown + // escaped too (re-review term F4). + j, err := journal.Open(filepath.Join(h, ".local", "state", "krino", "krino.log")) + if err != nil { + t.Fatal(err) + } + future := time.Date(2027, 1, 1, 0, 0, 0, 0, time.UTC) + hostileRun := "20270101T000000-\x1b]0;pwned\x07\u202e" + for _, e := range []journal.Entry{ + {Time: future, Run: hostileRun, Action: "run-start", Status: "ok"}, + {Time: future, Run: hostileRun, Dir: "dl\x1b[2J", File: "a.pdf", Step: 1, Action: "mo\x1b[31mve", Status: "ok", Src: "/x", Dst: "/y"}, + {Time: future, Run: hostileRun, Action: "run-end", Status: "ok"}, + } { + if err := j.Append(e); err != nil { + t.Fatal(err) + } + } + j.Close() + checkAny("log") + checkAny("undo", "-n") + // A newline inside file content (a zip entry's name) must not forge an // explain trace line (review M5). var buf bytes.Buffer @@ -130,3 +162,34 @@ func TestHostileNamesNeverReachTheTerminal(t *testing.T) { check("log") check("undo", "-n") } + +// TestHostileDirectoryPathIsEscaped: a (path ...) holding escape codes - a +// directory unpacked from a download - is shown escaped by check and in the +// plan's header (re-review cli F1, term F5). +func TestHostileDirectoryPathIsEscaped(t *testing.T) { + h := home(t) + dir := filepath.Join(h, "x\x1b]0;pwned\x07\u202ey") + if err := os.MkdirAll(dir, 0o755); err != nil { + t.Fatal(err) + } + if code, _, errOut := runCLI(t, "init"); code != 0 { + t.Fatal(errOut) + } + conf := filepath.Join(h, ".config", "krino") + if err := os.WriteFile(filepath.Join(conf, "krino.conf"), []byte("(include \"evil\")\n"), 0o644); err != nil { + t.Fatal(err) + } + body := "(path \"" + strings.ReplaceAll(dir, `\`, `\\`) + "\")\n(rule \"all\" (move \"Out\"))\n" + if err := os.WriteFile(filepath.Join(conf, "dirs", "evil.conf"), []byte(body), 0o644); err != nil { + t.Fatal(err) + } + for _, args := range [][]string{{"check"}, {"-n"}} { + code, out, errOut := runCLI(t, args...) + if code != 0 { + t.Fatalf("krino %q: exit %d\n%s\n%s", args, code, out, errOut) + } + if bad := firstUnsafe(out+errOut, true); bad != "" { + t.Errorf("krino %q printed %s:\n%q", args, bad, out) + } + } +} |
