Skip to content

Commit 1f39acd

Browse files
1 parent a8e7f0b commit 1f39acd

1 file changed

Lines changed: 2 additions & 2 deletions

File tree

advisories/github-reviewed/2026/03/GHSA-ph9w-r52h-28p7/GHSA-ph9w-r52h-28p7.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-ph9w-r52h-28p7",
4-
"modified": "2026-03-25T20:54:05Z",
4+
"modified": "2026-03-27T20:51:07Z",
55
"published": "2026-03-20T20:56:14Z",
66
"aliases": [
77
"CVE-2026-33497"
88
],
99
"summary": "langflow: /profile_pictures/{folder_name}/{file_name} endpoint file reading",
10-
"details": "### Summary\nIn the download_profile_picture function of the /profile_pictures/{folder_name}/{file_name} endpoint, the folder_name and file_name parameters are not strictly filtered, which allows the secret_key to be read across directories.\n\n### Details\nsrc/backend/base/langflow/api/v1/files.py\n\n![image](https://github.com/user-attachments/assets/19bc387c-93d8-475b-bab0-2675669c1c36)\n\nstorage local get_file, directly concatenate flow_id and file_name\n![image](https://github.com/user-attachments/assets/daf47b73-6e1b-414e-bb8b-66b67cf5a9e9)\n\n### PoC\n```curl --path-as-is 'http://127.0.0.1:7860/api/v1/files/profile_pictures/../secret_key' ```\n```QNuonm***********```\n\n### Impact\nsecret_key is used for jwt authentication. Attackers can forge authentication tokens and log into the system.",
10+
"details": "## Vulnerability\n\n### Path Traversal in `GET /api/v1/files/profile_pictures/{folder_name}/{file_name}`\n\nThe `download_profile_picture` function in `src/backend/base/langflow/api/v1/files.py` constructed file paths by directly concatenating the user-supplied `folder_name` and `file_name` path parameters without sanitization or boundary validation. The resulting path was passed to the filesystem without verifying it remained within the intended directory.\n\nAn unauthenticated attacker could supply traversal sequences (e.g. `../secret_key`) to navigate outside the profile pictures directory and read arbitrary files on the server filesystem.\n\nThis exposed the server to:\n\n- **Sensitive file disclosure** — any file readable by the application process could be retrieved\n- **Secret key exfiltration** — the application's `secret_key` file, used as JWT signing material, could be read directly via `../secret_key`\n- **Authentication bypass** — with the `secret_key` in hand, an attacker can forge valid JWT tokens and authenticate as any user, including administrators\n\n---\n\n## Proof of Concept\n\n```bash\ncurl --path-as-is 'http://<host>:7860/api/v1/files/profile_pictures/../secret_key'\n```\n\nA successful response returns the raw secret key value used to sign all JWT authentication tokens in the instance.\n\n---\n\n## Fix\n\nThe fix was applied in `src/backend/base/langflow/api/v1/files.py` (PR #12263).\n\nTwo layers of defense were introduced:\n\n**1. Typed path validation** — the `folder_name` and `file_name` parameters were changed from plain `str` to `ValidatedFolderName` and `ValidatedFileName` annotated types that reject traversal characters at the FastAPI input layer.\n\n**2. Path containment check** — `Path.name` is used to strip any directory component from the inputs before path construction, and `Path.is_relative_to()` verifies the resolved path remains within the allowed base directory. This replaces the previous `startswith()` check, which was susceptible to prefix-ambiguity bugs.\n\n```diff\n @router.get(\"/profile_pictures/{folder_name}/{file_name}\")\n async def download_profile_picture(\n- folder_name: str,\n- file_name: str,\n+ folder_name: ValidatedFolderName,\n+ file_name: ValidatedFileName,\n settings_service: Annotated[SettingsService, Depends(get_settings_service)],\n ):\n```\n\n```diff\n- file_path = (config_path / \"profile_pictures\" / folder_name / file_name).resolve()\n+ safe_folder = Path(folder_name).name\n+ safe_file = Path(file_name).name\n+ file_path = (config_path / \"profile_pictures\" / safe_folder / safe_file).resolve()\n\n allowed_base = (config_path / \"profile_pictures\").resolve()\n- if not str(file_path).startswith(str(allowed_base)):\n- raise HTTPException(status_code=404, detail=\"Profile picture not found\")\n+ if not file_path.is_relative_to(allowed_base):\n+ raise HTTPException(status_code=404, detail=\"Profile picture not found\")\n```\n\n---\n\n## Workarounds\n\nIf you cannot upgrade immediately, restrict network access to the `/api/v1/files/profile_pictures/` endpoint at the reverse-proxy or firewall level. Rotating the `secret_key` is strongly recommended if exposure cannot be ruled out.\n\n---\n\n## Acknowledgements\n\nWe thank the security researcher who responsibly disclosed this vulnerability.\n\n- [r00tuser111](https://github.com/r00tuser111)",
1111
"severity": [
1212
{
1313
"type": "CVSS_V4",

0 commit comments

Comments
 (0)