windmill-labs/windmill · error
raw app path ${JSON.stringify(relPath)} escapes the app fold
Error message
raw app path ${JSON.stringify(relPath)} escapes the app folder ${baseFolder} What it means
rawAppPathWithinFolder() joins an author-controlled key from a raw app definition (e.g. a `value.files` path or a runnable id) under the app's local folder and verifies the result stays inside it. It throws when the key contains `..` segments or is absolute, so the resolved path would escape the app folder. This is a security guard: raw app content is remote data, and without the check a malicious app could cause files to be written outside its own directory on pull.
Source
Thrown at cli/src/commands/sync/sync.ts:1134
/**
* Join a raw app's author-controlled key (`value.files` path, `value.runnables`
* id) under `baseFolder` and refuse anything that resolves outside it. Keys are
* remote data written to disk on pull, so a `..` segment must not walk a written
* file out of the app's own folder.
*/
export function rawAppPathWithinFolder(
baseFolder: string,
relPath: string,
): string {
const resolved = path.join(baseFolder, relPath);
const rel = path.relative(baseFolder, resolved);
if (
rel === "" ||
rel === ".." ||
rel.startsWith(".." + path.sep) ||
path.isAbsolute(rel)
) {
throw new Error(
`raw app path ${JSON.stringify(relPath)} escapes the app folder ${baseFolder}`,
);
}
return resolved;
}
export function ZipFSElement(
zip: JSZip,
useYaml: boolean,
defaultTs: "bun" | "deno",
resourceTypeToFormatExtension: Record<string, string>,
resourceTypeToIsFileset: Record<string, boolean>,
ignoreCodebaseChanges: boolean,
stripOnBehalfOf: boolean,
// Names a flow's rendered inline-script files after the checkout's own
// `!inline` references (module id -> file). The export carries script
// source, never a reference, so without a checkout to defer to every file
// is named from the step summary, and a file the checkout names otherwiseView on GitHub (pinned to e474e8803c)
Solutions
- Inspect the raw app definition's `value.files` and runnable keys; remove `..` segments and absolute paths so keys are relative names inside the app folder
- Re-export the app with relative file keys and re-sync
- If you trust the source but it legitimately needs nested paths, keep them within the folder (`sub/dir/file.ext` is fine; `../outside` is not)
- Never bypass this check for untrusted apps — it prevents arbitrary file writes on your machine
Example fix
// before (in app JSON)
"files": { "../../secrets.txt": "..." }
// after
"files": { "assets/secrets.txt": "..." } Defensive patterns
Strategy: validation
Validate before calling
function isSafeRelPath(p: string): boolean {
return !!p && !path.isAbsolute(p) && !p.split(/[\\/]/).includes('..');
}
// validate every value.files key / runnable id before syncing Type guard
function staysInFolder(baseFolder: string, rel: string): boolean {
const r = path.relative(baseFolder, path.resolve(baseFolder, rel));
return r !== '' && r !== '..' && !r.startsWith('..' + path.sep) && !path.isAbsolute(r);
} Try / catch
try {
const p = rawAppPathWithinFolder(baseFolder, key);
} catch (e) {
// reject/skip the hostile key; do NOT widen the folder to silence it
reportSkippedFile(key);
} Prevention
- Treat raw app file keys as untrusted remote data — always relative, no '..',' no absolute paths
- Validate keys when authoring or importing raw apps from external sources
- Never bypass rawAppPathWithinFolder for shared/community apps
When it happens
Trigger: Pulling/syncing a raw app whose `value.files` entries or runnable ids contain path traversal like `../../etc/x`, an absolute path (`/abs/...`), or an empty/'..' key — whether authored accidentally or maliciously in a shared app.
Common situations: Importing a raw app exported from another instance where files were stored with absolute paths; hand-editing an app's JSON and introducing `..` in a file key; receiving a shared community app with hostile keys.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Content of raw app file ${filePath} is not a string
- Path is outside the allowed job directory.
- Path traverses a symlink, which is not allowed.
- bundle produced no javascript:\n${buildOutput}
- Workspace folder not found, are you in the right directory?
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/0eb82627bd216ef3.
Report an issue: GitHub.