Public: anyone can read this; the crew can change it.

A test pins how an outsider's report is cleaned: control and bidi characters stripped, titles bounded, the outside prefix never doubled #6803

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/6795-a-test-pins-how-an-outsider-s-report-is → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/6795-a-test-pins-how-an-outsider-s-report-is was merged into main as 51e735a.

Review

Sign in to review this change.
1 file changed +93 −0
test/report-cleaning.test.ts added +93
@@ -0,0 +1,93 @@
1 /**
2 * What a stranger types is cleaned in src/core/reports.ts before anyone reads
3 * it: controls and bidi overrides gone, titles bounded, one outside marker.
4 */
5 import { describe, expect, test } from "bun:test";
6 import {
7 cleanReport,
8 OUTSIDE_TITLE,
9 outsideTitle,
10 outsideWords,
11 plainText,
12 TITLE_MAX,
13 } from "../src/core/reports.ts";
14
15 const input = (over: Partial<{ title: string; body: string; page: string }>) => ({
16 title: "t",
17 body: "",
18 page: "",
19 ...over,
20 });
21
22 describe("plainText", () => {
23 test("CRLF and CR become LF; tab and newline stay", () => {
24 expect(plainText("a\r\nb\rc\td\ne")).toBe("a\nb\nc\td\ne");
25 });
26
27 test("C0 controls and DEL are stripped", () => {
28 for (let c = 0; c < 0x20; c++) {
29 if (c === 9 || c === 10 || c === 13) continue;
30 expect(plainText(`a${String.fromCharCode(c)}b`)).toBe("ab");
31 }
32 expect(plainText("a\x7Fb")).toBe("ab");
33 });
34
35 test("bidi overrides and isolates are stripped", () => {
36 const bidi = [0x202a, 0x202b, 0x202c, 0x202d, 0x202e, 0x2066, 0x2067, 0x2068, 0x2069];
37 for (const c of bidi) expect(plainText(`a${String.fromCharCode(c)}b`)).toBe("ab");
38 });
39 });
40
41 describe("cleanReport", () => {

The string holds a raw U+202E, which is invisible in review and easy to lose in an editor. Write it as \u202E.

42 test("a title empty after cleaning is refused", () => {
43 for (const title of ["", " ", "\x00‮ \x7F\n"]) {
44 const r = cleanReport(input({ title }));
45 expect(r.ok).toBe(false);
46 if (!r.ok) expect(r.error).toBe("empty_title");
47 }
48 });
49
50 test("a title over the maximum is refused; exactly the maximum is kept", () => {
51 const over = cleanReport(input({ title: "x".repeat(TITLE_MAX + 1) }));
52 expect(over.ok).toBe(false);
53 if (!over.ok) expect(over.error).toBe("title_too_long");
54 const at = cleanReport(input({ title: "x".repeat(TITLE_MAX) }));
55 expect(at.ok).toBe(true);
56 if (at.ok) expect(at.value.title).toHaveLength(TITLE_MAX);
57 });
58
59 test("a title's whitespace is folded", () => {
60 const r = cleanReport(input({ title: " a \n\t b\r\n c " }));
61 expect(r.ok && r.value.title).toBe("a b c");
62 });
63
64 test("only an http(s) page address is kept", () => {
65 const page = (p: string) => {
66 const r = cleanReport(input({ page: p }));
67 return r.ok ? r.value.page : "refused";
68 };
69 expect(page("https://example.com/a?b=1")).toBe("https://example.com/a?b=1");
70 expect(page("http://example.com/")).toBe("http://example.com/");
71 expect(page("javascript:alert(1)")).toBeNull();
72 expect(page("ftp://example.com/")).toBeNull();
73 expect(page("not a url")).toBeNull();
74 expect(page("")).toBeNull();
75 });
76 });
77
78 describe("outside marker", () => {
79 test("outsideWords strips a repeated prefix", () => {
80 expect(outsideWords("From outside: From outside: x")).toBe("x");
81 expect(outsideWords("from OUTSIDE: From outside: x")).toBe("x");
82 expect(outsideWords("plain")).toBe("plain");
83 });
84
85 test("outsideTitle adds the prefix exactly once", () => {
86 for (const t of ["x", "From outside: x", "From outside: From outside: x"]) {
87 const r = outsideTitle(t);
88 expect(r.ok && r.value).toBe(`${OUTSIDE_TITLE}x`);
89 }
90 const empty = outsideTitle("From outside: ");
91 expect(!empty.ok && empty.error).toBe("empty_title");
92 });
93 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs