89 lines
3.7 KiB
TypeScript
89 lines
3.7 KiB
TypeScript
|
|
import { test } from "node:test";
|
||
|
|
import assert from "node:assert/strict";
|
||
|
|
|
||
|
|
import { sanitizeTerminalInput } from "./terminalInputSanitize";
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput strips zero-width space (U+200B)", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\u200b"), "ls");
|
||
|
|
assert.equal(sanitizeTerminalInput("\u200bls"), "ls");
|
||
|
|
assert.equal(sanitizeTerminalInput("l\u200bs"), "ls");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput strips BOM / ZWNBSP (U+FEFF)", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("\ufeffls"), "ls");
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\ufeff"), "ls");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput strips soft hyphen (U+00AD)", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\u00ad"), "ls");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput preserves ZWNJ (U+200C) and ZWJ (U+200D)", () => {
|
||
|
|
// ZWNJ is meaningful in Persian orthography; ZWJ joins emoji sequences.
|
||
|
|
// PTYs / remote programs can process these raw bytes, so they must not be
|
||
|
|
// stripped — otherwise filenames, args, or passwords containing them break.
|
||
|
|
assert.equal(sanitizeTerminalInput("a\u200cb"), "a\u200cb");
|
||
|
|
assert.equal(sanitizeTerminalInput("a\u200db"), "a\u200db");
|
||
|
|
// Emoji with ZWJ (👨💻 = man + ZWJ + laptop) is preserved
|
||
|
|
assert.equal(sanitizeTerminalInput("👨\u200d💻"), "👨\u200d💻");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput strips directional marks (U+200E, U+200F)", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("\u200els\u200f"), "ls");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput strips word joiner and invisible operators (U+2060-2064)", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\u2060"), "ls");
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\u2061"), "ls");
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\u2062"), "ls");
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\u2063"), "ls");
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\u2064"), "ls");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput returns empty string for zero-width-only input", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("\u200b"), "");
|
||
|
|
assert.equal(sanitizeTerminalInput("\u200b\ufeff\u2060"), "");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput does not strip ZWNJ/ZWJ-only input", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("\u200c"), "\u200c");
|
||
|
|
assert.equal(sanitizeTerminalInput("\u200d"), "\u200d");
|
||
|
|
assert.equal(sanitizeTerminalInput("\u200c\u200d"), "\u200c\u200d");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput preserves regular ASCII and control characters", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("ls\r"), "ls\r");
|
||
|
|
assert.equal(sanitizeTerminalInput("\r"), "\r");
|
||
|
|
assert.equal(sanitizeTerminalInput("\n"), "\n");
|
||
|
|
assert.equal(sanitizeTerminalInput("\u0003"), "\u0003"); // Ctrl+C
|
||
|
|
assert.equal(sanitizeTerminalInput("\u007f"), "\u007f"); // DEL
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput preserves CJK and emoji characters", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("你好"), "你好");
|
||
|
|
assert.equal(sanitizeTerminalInput("😀"), "😀");
|
||
|
|
// Full-width punctuation (common CJK IME output) is preserved
|
||
|
|
assert.equal(sanitizeTerminalInput(",。!?"), ",。!?");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput preserves Kitty escape sequences", () => {
|
||
|
|
const kittySeq = "\u001b[0;;97:97u";
|
||
|
|
assert.equal(sanitizeTerminalInput(kittySeq), kittySeq);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput handles empty and falsy input", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput(""), "");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput strips multiple interspersed zero-width characters", () => {
|
||
|
|
assert.equal(sanitizeTerminalInput("\u200bl\u200bs\u200b \u200b-l\u200ba\u200b"), "ls -la");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("sanitizeTerminalInput is stable across repeated calls", () => {
|
||
|
|
const input = "ls\u200b\r";
|
||
|
|
const first = sanitizeTerminalInput(input);
|
||
|
|
const second = sanitizeTerminalInput(first);
|
||
|
|
assert.equal(first, second);
|
||
|
|
assert.equal(first, "ls\r");
|
||
|
|
});
|