{"record":{"id":"8473edea5b167b8c","repo":"langflow-ai/langflow","slug":"invalid-fs-path-directory-traversal-is-not-a","errorCode":null,"errorMessage":"Invalid fs_path: directory traversal (..) is not allowed","messagePattern":"Invalid fs_path: directory traversal \\(\\.\\.\\) is not allowed","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"src/backend/base/langflow/api/v1/flows_helpers.py","lineNumber":66,"sourceCode":"def _get_safe_flow_path(fs_path: str, user_id: UUID, storage_service: StorageService) -> Path:\n    \"\"\"Get a safe filesystem path for flow storage, restricted to user's flows directory.\n\n    Allows both absolute and relative paths, but ensures they're within the user's flows directory.\n\n    Uses ``os.path.realpath`` + ``startswith`` for containment — the sanitiser pattern\n    recognised by CodeQL's ``py/path-injection`` analysis. ``realpath`` canonicalises\n    the path and follows symlinks, so the returned path is safe to pass to filesystem\n    operations.\n    \"\"\"\n    if not fs_path:\n        raise HTTPException(status_code=400, detail=\"fs_path cannot be empty\")\n\n    # Normalize path separators first (before security checks to prevent backslash bypass)\n    normalized_path = fs_path.replace(\"\\\\\", \"/\")\n\n    # Reject directory traversal and null bytes (check normalized path)\n    if \"..\" in normalized_path:\n        raise HTTPException(\n            status_code=400,\n            detail=\"Invalid fs_path: directory traversal (..) is not allowed\",\n        )\n    if \"\\x00\" in normalized_path:\n        raise HTTPException(\n            status_code=400,\n            detail=\"Invalid fs_path: null bytes are not allowed\",\n        )\n\n    # Build and canonicalise the safe base directory path.\n    base_dir = storage_service.data_dir / \"flows\" / str(user_id)\n    try:\n        base_dir_resolved = os.path.realpath(str(base_dir))\n    except (OSError, ValueError) as e:\n        raise HTTPException(status_code=400, detail=f\"Invalid base directory: {e}\") from e\n\n    # Determine if path is absolute (Unix or Windows style)\n    is_absolute = normalized_path.startswith(\"/\") or (len(normalized_path) > 1 and normalized_path[1] == \":\")","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/v1/flows_helpers.py#L48-L84","documentation":"HTTP 400: fs_path contained '..' after backslash-to-slash normalisation, i.e. an attempted directory traversal. The check runs on the normalised path, so both \"../x\" and \"..\\\\x\" forms are caught before any filesystem access, satisfying the CodeQL-recognised sanitiser pattern documented in the function.","triggerScenarios":"Any flow create/update where fs_path is \"../secrets.json\", \"a/../../etc/passwd\", or a backslash variant like \"..\\\\..\\\\x\"; also filename components that legitimately contain '..' (e.g. \"v2..old.json\") are rejected because the check is a plain substring match.","commonSituations":"Client builds the path from user input without sanitising; a flow exported from another instance contains an fs_path with parent references; filenames containing literal '..' (double-dot) sequences that are innocent but trip the substring check.","solutions":["Use a flat filename or a sub-path with no '..' segment: fs_path is always anchored under <data_dir>/flows/<user_id>/.","Sanitise client-side: strip path segments equal to '..' before sending.","If you genuinely need a file outside your flows dir, that is unsupported by design — copy the file into the flows directory instead."],"exampleFix":"# before\n{\"fs_path\": \"../shared/flow.json\"}\n# after\n{\"fs_path\": \"shared/flow.json\"}","handlingStrategy":"validation","validationCode":"if (fsPath.includes('..')) throw new Error('fs_path must not contain .. segments');","typeGuard":"const hasNoTraversal = (p: string) => !p.replace(/\\\\/g, '/').split('/').includes('..');","tryCatchPattern":null,"preventionTips":["Send flat relative filenames","Sanitise user input before placing it into fs_path","Remember '..' anywhere in the string (even 'v2..old') is rejected"],"tags":["security","path-traversal","validation","http-400","fs-path"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}