langflow-ai/langflow · warning · HTTPException

Invalid fs_path: directory traversal (..) is not allowed

Error message

Invalid fs_path: directory traversal (..) is not allowed

What it means

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.

Source

Thrown at src/backend/base/langflow/api/v1/flows_helpers.py:66

def _get_safe_flow_path(fs_path: str, user_id: UUID, storage_service: StorageService) -> Path:
    """Get a safe filesystem path for flow storage, restricted to user's flows directory.

    Allows both absolute and relative paths, but ensures they're within the user's flows directory.

    Uses ``os.path.realpath`` + ``startswith`` for containment — the sanitiser pattern
    recognised by CodeQL's ``py/path-injection`` analysis. ``realpath`` canonicalises
    the path and follows symlinks, so the returned path is safe to pass to filesystem
    operations.
    """
    if not fs_path:
        raise HTTPException(status_code=400, detail="fs_path cannot be empty")

    # Normalize path separators first (before security checks to prevent backslash bypass)
    normalized_path = fs_path.replace("\\", "/")

    # Reject directory traversal and null bytes (check normalized path)
    if ".." in normalized_path:
        raise HTTPException(
            status_code=400,
            detail="Invalid fs_path: directory traversal (..) is not allowed",
        )
    if "\x00" in normalized_path:
        raise HTTPException(
            status_code=400,
            detail="Invalid fs_path: null bytes are not allowed",
        )

    # Build and canonicalise the safe base directory path.
    base_dir = storage_service.data_dir / "flows" / str(user_id)
    try:
        base_dir_resolved = os.path.realpath(str(base_dir))
    except (OSError, ValueError) as e:
        raise HTTPException(status_code=400, detail=f"Invalid base directory: {e}") from e

    # Determine if path is absolute (Unix or Windows style)
    is_absolute = normalized_path.startswith("/") or (len(normalized_path) > 1 and normalized_path[1] == ":")

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Use a flat filename or a sub-path with no '..' segment: fs_path is always anchored under <data_dir>/flows/<user_id>/.
  2. Sanitise client-side: strip path segments equal to '..' before sending.
  3. If you genuinely need a file outside your flows dir, that is unsupported by design — copy the file into the flows directory instead.

Example fix

# before
{"fs_path": "../shared/flow.json"}
# after
{"fs_path": "shared/flow.json"}
Defensive patterns

Strategy: validation

Validate before calling

if (fsPath.includes('..')) throw new Error('fs_path must not contain .. segments');

Type guard

const hasNoTraversal = (p: string) => !p.replace(/\\/g, '/').split('/').includes('..');

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/8473edea5b167b8c. Report an issue: GitHub.