aboutsummaryrefslogtreecommitdiff
path: root/cmd/krino/hostile_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 22:31:12 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 22:31:12 +0200
commita91b713dcec4d17f76155f0cd6b26903c59b9c19 (patch)
tree44b1354e152c71a978738ee6ec47164ccef10b81 /cmd/krino/hostile_test.go
parentc03a72f1d7b598c1fe8fd01bb1f5bbfbd0256313 (diff)
downloadkrino-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.go63
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)
+ }
+ }
+}