GHSA-F43R-CC68-GPX4
Vulnerability from github – Published: 2025-12-19 22:53 – Updated: 2025-12-19 22:53Vulnerability 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
-
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:```
-
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)] -
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."""```
-
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() -
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 flow %s to path %s", flow.name, flow.fs_path)
PoC Description
When an authenticated user passes an arbitrary path in fs_path, the Flow JSON is written to that path. Since /tmp is usually writable, it is easy to reproduce. In a production environment, writing to system-protected directories may fail depending on permissions.
PoC
-
Before Exploit
-
After Exploit
bash curl -sS -X POST "http://localhost:7860/api/v1/flows/" \ -H "Content-Type: application/json" \ -H "x-api-key: sk-8Kyzf9IQ-UEJ_OtSTaJq4eniMT9_JKgZ7__q8PNkoxc" \ -d '{"name":"poc-etc","data":{"nodes":[],"edges":[]},"fs_path":"/tmp/POC.txt"}'
Impact
- Authenticated Arbitrary File Write (within server permission scope): Risk of corrupting configuration/log/task files, disrupting application behavior, and tampering with files read by other components.
- Both absolute and relative paths are allowed, enabling base directory traversal. The risk of overwriting system files increases in environments with root privileges or weak mount/permission settings.
- The file content is limited to Flow JSON, but the impact is severe if the target file is parsed by a JSON parser or is subject to subsequent processing.
- In production environments, it is essential to enforce a save root, normalize paths, block symlink traversal, and minimize permissions.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "langflow"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.7.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-68478"
],
"database_specific": {
"cwe_ids": [
"CWE-73"
],
"github_reviewed": true,
"github_reviewed_at": "2025-12-19T22:53:13Z",
"nvd_published_at": "2025-12-19T18:15:51Z",
"severity": "HIGH"
},
"details": "**Vulnerability Overview**\n\nIf an arbitrary path is specified in the request body\u0027s `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.\n\n**Vulnerable Code**\n\n1. It receives the request body (flow), updates the DB, and then passes it to the file-writing sink.\n \n https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/v1/flows.py#L154-L168\n \n ```python\n @router.post(\"/\", response_model=FlowRead, status_code=201)\n async def create_flow(\n *,\n session: DbSession,\n flow: FlowCreate,\n current_user: CurrentActiveUser,\n ):\n try:\n db_flow = await _new_flow(session=session, flow=flow, user_id=current_user.id)\n await session.commit()\n await session.refresh(db_flow)\n \n await _save_flow_to_fs(db_flow)\n \n except Exception as e:\n ```\n \n2. Applies authentication dependency (requires API Key/JWT) when accessing the endpoint.\n \n https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/utils/core.py#L36-L38\n \n ```python\n CurrentActiveUser = Annotated[User, Depends(get_current_active_user)]\n CurrentActiveMCPUser = Annotated[User, Depends(get_current_active_user_mcp)]\n DbSession = Annotated[AsyncSession, Depends(get_session)]\n ```\n \n3. The client can directly specify the save path, including `fs_path`.\n \n https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/v1/flows.py#L66-L70\n \n ```python\n ):\n try:\n await _verify_fs_path(flow.fs_path)\n \n \"\"\"Create a new flow.\"\"\"\n ```\n \n4. It attempts to create the file (or *the* file, in the case of a path without a parent) directly without path validation.\n \n https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/v1/flows.py#L45-L49\n \n ```python\n async def _verify_fs_path(path: str | None) -\u003e None:\n if path:\n path_ = Path(path)\n if not await path_.exists():\n await path_.touch()\n ```\n \n5. Serializes the Flow object to JSON and writes it to the specified path in \"w\" mode (overwriting).\n \n https://github.com/langflow-ai/langflow/blob/ac6e2d2eabeee28085f2739d79f7ce4205ca082c/src/backend/base/langflow/api/v1/flows.py#L52-L58\n \n ```python\n async def _save_flow_to_fs(flow: Flow) -\u003e None:\n if flow.fs_path:\n async with async_open(flow.fs_path, \"w\") as f:\n try:\n await f.write(flow.model_dump_json())\n except OSError:\n await logger.aexception(\"Failed to write flow %s to path %s\", flow.name, flow.fs_path)\n ```\n \n\n**PoC Description**\n\nWhen an authenticated user passes an arbitrary path in `fs_path`, the Flow JSON is written to that path. Since `/tmp` is usually writable, it is easy to reproduce. In a production environment, writing to system-protected directories may fail depending on permissions.\n\n**PoC**\n\n- **Before Exploit**\n \n\n \u003cimg width=\"1918\" height=\"658\" alt=\"image\" src=\"https://github.com/user-attachments/assets/fe3c2306-091d-4cb0-b4dc-c7fb63c03d8d\" /\u003e\n \n- **After Exploit**\n \n ```bash\n curl -sS -X POST \"http://localhost:7860/api/v1/flows/\" \\\n -H \"Content-Type: application/json\" \\\n -H \"x-api-key: sk-8Kyzf9IQ-UEJ_OtSTaJq4eniMT9_JKgZ7__q8PNkoxc\" \\\n -d \u0027{\"name\":\"poc-etc\",\"data\":{\"nodes\":[],\"edges\":[]},\"fs_path\":\"/tmp/POC.txt\"}\u0027\n ```\n \n \u003cimg width=\"1918\" height=\"742\" alt=\"image\" src=\"https://github.com/user-attachments/assets/cc0b0c96-1c2d-4d56-b558-5ba97e0ec174\" /\u003e\n \n\n### Impact\n\n- **Authenticated Arbitrary File Write (within server permission scope):** Risk of corrupting configuration/log/task files, disrupting application behavior, and tampering with files read by other components.\n- **Both absolute and relative paths are allowed, enabling base directory traversal.** The risk of overwriting system files increases in environments with root privileges or weak mount/permission settings.\n- **The file content is limited to Flow JSON,** but the impact is severe if the target file is parsed by a JSON parser or is subject to subsequent processing.\n- **In production environments, it is essential to enforce a save root, normalize paths, block symlink traversal, and minimize permissions.**",
"id": "GHSA-f43r-cc68-gpx4",
"modified": "2025-12-19T22:53:13Z",
"published": "2025-12-19T22:53:13Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/langflow-ai/langflow/security/advisories/GHSA-f43r-cc68-gpx4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-68478"
},
{
"type": "PACKAGE",
"url": "https://github.com/langflow-ai/langflow"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "External Control of File Name or Path in Langflow"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.