feat(session): add shared session id validation
Session ids become directory names server-side and topic levels on the broker, so they must reject path separators, ./.., whitespace and Windows reserved device names. toSessionId derives a safe id from a display name; isValidSessionId is the boundary check shared by the CLI and the web client.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
SESSION_ID_MAX_LENGTH,
|
||||
isValidSessionId,
|
||||
toSessionId,
|
||||
} from "./session-id";
|
||||
|
||||
describe("isValidSessionId", () => {
|
||||
test.each(["default", "a", "A1", "my-game", "under_score", "-"])(
|
||||
"accepts %p",
|
||||
(id) => {
|
||||
expect(isValidSessionId(id)).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
test.each([
|
||||
"",
|
||||
".",
|
||||
"..",
|
||||
"../evil",
|
||||
"..\\..\\evil",
|
||||
"a/b",
|
||||
"a\\b",
|
||||
"a b",
|
||||
"a.b",
|
||||
"a/b/../c",
|
||||
])("rejects %p", (id) => {
|
||||
expect(isValidSessionId(id)).toBe(false);
|
||||
});
|
||||
|
||||
test("rejects non-strings and over-long ids", () => {
|
||||
expect(isValidSessionId(null)).toBe(false);
|
||||
expect(isValidSessionId(undefined)).toBe(false);
|
||||
expect(isValidSessionId(42)).toBe(false);
|
||||
expect(isValidSessionId("a".repeat(SESSION_ID_MAX_LENGTH + 1))).toBe(false);
|
||||
});
|
||||
|
||||
test("rejects Windows reserved device names", () => {
|
||||
expect(isValidSessionId("con")).toBe(false);
|
||||
expect(isValidSessionId("COM1")).toBe(false);
|
||||
expect(isValidSessionId("Nul")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("toSessionId", () => {
|
||||
test("slugifies names", () => {
|
||||
expect(toSessionId("My Game")).toBe("my-game");
|
||||
expect(toSessionId(" Curse of Strahd!! ")).toBe("curse-of-strahd");
|
||||
expect(toSessionId("")).toBe("session");
|
||||
expect(toSessionId("!!!")).toBe("session");
|
||||
});
|
||||
|
||||
test("avoids Windows reserved device names", () => {
|
||||
expect(toSessionId("CON")).toBe("con-session");
|
||||
expect(isValidSessionId(toSessionId("CON"))).toBe(true);
|
||||
});
|
||||
|
||||
test("always yields a valid id, including for hostile input", () => {
|
||||
const names = [
|
||||
"My Game",
|
||||
"",
|
||||
"..",
|
||||
"../etc/passwd",
|
||||
"a/b\\c",
|
||||
"CON",
|
||||
"Ünïcödé",
|
||||
"x".repeat(200),
|
||||
"\0\0",
|
||||
];
|
||||
for (const name of names) {
|
||||
expect(isValidSessionId(toSessionId(name))).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user