mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
2105540cca
* feat: add session recording to wmill app dev * fix: harden dev recorder shell, bundle staleness guard and save route * fix: keep dev-server recordings out of the raw app sync diff * style: drop em dashes from new cli comments * fix: tighten dev recorder save route origin, naming and io * test: pin that only the root recordings folder is skipped * fix: survive an oversized recording upload and match paths on windows * fix: keep the app at the root and settle runnables stranded by a reload * fix: make the recorder bundle hash stable on a crlf checkout * docs: state the preflight-free content type the origin check guards * test: build the sync-skip fixture with the platform separator * docs: align the origin-guard test comment with the code * chore: mark generated .gen.ts files as generated
44 lines
1.7 KiB
TypeScript
44 lines
1.7 KiB
TypeScript
/**
|
|
* Guards on the routes `wmill app dev --recording` adds: what may write a
|
|
* recording, what a recording may be named, and that two saves never collide.
|
|
*/
|
|
|
|
import { expect, test } from "bun:test";
|
|
import {
|
|
isOwnOrigin,
|
|
isRecordingFileName,
|
|
recordingFileName,
|
|
} from "../src/commands/app/devRecorder.ts";
|
|
|
|
test("only the shell's own origin may save a recording", () => {
|
|
expect(isOwnOrigin("http://localhost:4000", "localhost:4000")).toBe(true);
|
|
expect(isOwnOrigin("http://127.0.0.1:4000", "127.0.0.1:4000")).toBe(true);
|
|
// A cross-site POST carrying JSON under a simple content type needs no
|
|
// preflight, so a foreign origin sharing the port must still be refused.
|
|
expect(isOwnOrigin("http://attacker.example:4000", "localhost:4000")).toBe(
|
|
false,
|
|
);
|
|
expect(isOwnOrigin("null", "localhost:4000")).toBe(false);
|
|
// No Origin at all is a non-browser client, not a cross-site page.
|
|
expect(isOwnOrigin(undefined, "localhost:4000")).toBe(true);
|
|
});
|
|
|
|
test("recording names stay inside the recordings folder", () => {
|
|
expect(isRecordingFileName("recording-2026-01-01-00-00-00-000.json")).toBe(
|
|
true,
|
|
);
|
|
expect(isRecordingFileName("../../../etc/passwd")).toBe(false);
|
|
expect(isRecordingFileName("..%2Fx.json")).toBe(false);
|
|
expect(isRecordingFileName("sub/dir.json")).toBe(false);
|
|
expect(isRecordingFileName("recording.txt")).toBe(false);
|
|
});
|
|
|
|
test("two saves in the same millisecond get distinct names", () => {
|
|
const now = new Date("2026-01-01T00:00:00.123Z");
|
|
const first = recordingFileName(now, 0);
|
|
const second = recordingFileName(now, 1);
|
|
expect(first).toBe("recording-2026-01-01-00-00-00-123.json");
|
|
expect(second).not.toBe(first);
|
|
expect(isRecordingFileName(second)).toBe(true);
|
|
});
|