// SPDX-License-Identifier: GPL-3.0-or-later package main import ( "os" "path/filepath" "strings" "testing" "time" ) // hostileNames are file names a download can carry that try to take over // the terminal krino prints them to, or to fake what it shows. var hostileNames = []string{ "esc\x1b[2K\x1b[1Ahidden.pdf", "bell\a.pdf", "cr\rspoof.pdf", "c1\u009b31mred.pdf", "bidi\u202egnp.pdf", "new\nline.pdf", "-rf.pdf", } // TestHostileNamesNeverReachTheTerminal: with files named to attack the // terminal, and an extraction tool whose error message carries an escape // sequence, nothing krino prints - plan, verbose lists, warnings, explain, // apply, log, undo plan - holds a raw control character. func TestHostileNamesNeverReachTheTerminal(t *testing.T) { h := home(t) dl := filepath.Join(h, "dl") if err := os.MkdirAll(dl, 0o755); err != nil { t.Fatal(err) } old := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) for _, n := range hostileNames { p := filepath.Join(dl, n) if err := os.WriteFile(p, []byte("content of a hostile file"), 0o644); err != nil { t.Fatal(err) } if err := os.Chtimes(p, old, old); err != nil { t.Fatal(err) } } bin := filepath.Join(h, "bin") if err := os.Mkdir(bin, 0o755); err != nil { t.Fatal(err) } tool := "#!/bin/sh\nprintf 'bad \\033[2Jpdf\\n' >&2\nexit 1\n" if err := os.WriteFile(filepath.Join(bin, "pdftotext"), []byte(tool), 0o755); err != nil { t.Fatal(err) } t.Setenv("PATH", bin) if code, _, errOut := runCLI(t, "init"); code != 0 { t.Fatal(errOut) } if code, _, errOut := runCLI(t, "new", "dl", dl); code != 0 { t.Fatal(errOut) } rules := "(path \"~/dl\")\n(rule \"read\" (when (content \"acme\")) (stop))\n(rule \"all\" (move \"Out\"))\n" if err := os.WriteFile(filepath.Join(h, ".config", "krino", "dirs", "dl.conf"), []byte(rules), 0o644); err != nil { t.Fatal(err) } check := func(args ...string) string { t.Helper() code, out, errOut := runCLI(t, args...) if code != 0 { t.Fatalf("krino %q: exit %d\n%s\n%s", args, code, out, errOut) } for stream, text := range map[string]string{"stdout": out, "stderr": errOut} { if bad := firstUnsafe(text, true); bad != "" { t.Errorf("krino %q: %s holds %s:\n%q", args, stream, bad, text) } } return out } out := check("-n", "-v") if !strings.Contains(out, `esc\x1b[2K\x1b[1Ahidden.pdf`) || !strings.Contains(out, `bad \x1b[2Jpdf`) { t.Errorf("names and the tool's message should be shown escaped:\n%s", out) } check("explain", filepath.Join(dl, hostileNames[0])) check("-y") check("log") check("undo", "-n") }