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 otherwise

View on GitHub (pinned to e474e8803c)

Solutions

  1. 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
  2. Re-export the app with relative file keys and re-sync
  3. 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)
  4. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/0eb82627bd216ef3. Report an issue: GitHub.