Files
windmill/cli/test/raw_app_path_traversal_unit.test.ts
T
5b885ae311 fix: keep raw-app files within their app folder on sync pull (#10796)
* fix: keep raw-app files within their app folder on sync pull

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: validate raw-app file keys as stored, closing nul and duplicate-field bypasses

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: guard raw-app runnable ids too and fail closed on unparseable value

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: strip only a leading slash on raw-app file keys to match backend

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: strip only a leading slash on raw-app file keys to match backend

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-08-22 10:00:52 +00:00

34 lines
1.3 KiB
TypeScript

import { expect, test } from "bun:test";
import { join as joinPath } from "node:path";
import { rawAppPathWithinFolder } from "../src/commands/sync/sync.ts";
const APP = joinPath("u", "admin", "myapp.raw_app");
const BACKEND = joinPath(APP, "wm_backend");
test("keys that stay inside the folder resolve to a path within it", () => {
// `value.files` keys arrive with a leading slash the caller strips.
expect(rawAppPathWithinFolder(APP, "index.tsx")).toBe(
joinPath(APP, "index.tsx"),
);
expect(rawAppPathWithinFolder(APP, "src/util.ts")).toBe(
joinPath(APP, "src", "util.ts"),
);
// A runnable id names its yaml under the backend folder.
expect(rawAppPathWithinFolder(BACKEND, "a.yaml")).toBe(
joinPath(BACKEND, "a.yaml"),
);
});
test("keys that resolve outside the folder are rejected", () => {
for (const [base, rel] of [
[APP, "../sibling.ts"], // a files key into the app's parent folder
[APP, "../../../f/other/outside.ts"], // into an unrelated folder tree
[APP, "../../../../../../elsewhere.txt"], // above the app folder entirely
[BACKEND, "../../../../etc/evil.yaml"], // a runnable id escaping the backend folder
] as const) {
expect(() => rawAppPathWithinFolder(base, rel)).toThrow(
/escapes the app folder/,
);
}
});