GitHub Security Advisory: GHSA-f43r-cc68-gpx4
Package: langflow (PIP)
Severity: HIGH (CVSS 7.1)
CWE: CWE-73: External Control of File Name or Path, CWE-610: Externally Controlled Reference to a Resource in Another Sphere
Vulnerable versions: < 1.7.1
Patched in: 1.7.1

Summary: External Control of File Name or Path in Langflow

Details:
**Vulnerability Overview**

If an arbitrary path is specified in the request body's `fs_path`, the server serializes the Flow object into JSON and creates/overwrites a file at that path. There is no path restriction, normalization, or allowed directory enforcement, so absolute paths (e.g., /etc/poc.txt) are interpreted as is.

**Vulnerable Code**

1. It receives the request body (flow), updates the DB, and then passes it to the file-writing sink.
    
    https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/v1/flows.py#L154-L168
    
    ```python
    @router.post("/", response_model=FlowRead, status_code=201)
    async def create_flow(
        *,
        session: DbSession,
        flow: FlowCreate,
        current_user: CurrentActiveUser,
    ):
        try:
            db_flow = await _new_flow(session=session, flow=flow, user_id=current_user.id)
            await session.commit()
            await session.refresh(db_flow)
    
            await _save_flow_to_fs(db_flow)
    
        except Exception as e:
    ```
    
2. Applies authentication dependency (requires API Key/JWT) when accessing the endpoint.
    
    https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/utils/core.py#L36-L38
    
    ```python
    CurrentActiveUser = Annotated[User, Depends(get_current_active_user)]
    CurrentActiveMCPUser = Annotated[User, Depends(get_current_active_user_mcp)]
    DbSession = Annotated[AsyncSession, Depends(get_session)]
    ```
    
3. The client can directly specify the save path, including `fs_path`.
    
    https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/v1/flows.py#L66-L70
    
    ```python
    ):
        try:
            await _verify_fs_path(flow.fs_path)
    
            """Create a new flow."""
    ```
    
4. It attempts to create the file (or *the* file, in the case of a path without a parent) directly without path validation.
    
    https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/v1/flows.py#L45-L49
    
    ```python
    async def _verify_fs_path(path: str | None) -> None:
        if path:
            path_ = Path(path)
            if not await path_.exists():
                await path_.touch()
    ```
    
5. Serializes the Flow object to JSON and writes it to the specified path in "w" mode (overwriting).
    
    https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/v1/flows.py#L52-L58
    
    ```python
    async def _save_flow_to_fs(flow: Flow) -> None:
        if flow.fs_path:
            async with async_open(flow.fs_path, "w") as f:
                try:
                    await f.write(flow.model_dump_json())
                except OSError:
                    await logger.aexception("Failed to write