Skip to content

Commit 63319f2

Browse files
committed
fix(batch-export): harden local download Content-Disposition + doc volume
Address PR #57 review: - Encode Content-Disposition with an ASCII-only filename fallback plus a UTF-8 filename*, mirroring the trace download route, so a crafted export file name cannot break or inject the header. - Document the langfuse_batch_export_data volume in docs/deployment.md (volumes table + shared-mount note).
1 parent 0837479 commit 63319f2

2 files changed

Lines changed: 17 additions & 10 deletions

File tree

docs/deployment.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,19 @@ The application default for these variables is `s3`, but this repo's `docker-com
6565

6666
The Compose files use Docker named volumes for every stateful path. Treat these as the deployment's data directory set:
6767

68-
| Volume | Mounted path | Stores |
69-
| -------------------------- | -------------------------- | ----------------------------------------------------------------------------- |
70-
| `langfuse_postgres_data` | `/var/lib/postgresql/data` | Postgres application state: users, projects, API keys, prompts, config |
71-
| `langfuse_greptimedb_data` | `/greptimedb_data` | GreptimeDB analytics store: traces, observations, scores, dashboards |
72-
| `langfuse_redis_data` | `/data` | Redis queue state |
73-
| `langfuse_media_data` | `/langfuse_media_data` | Local media uploads when `LANGFUSE_MEDIA_STORAGE_BACKEND=local` |
74-
| `langfuse_event_data` | `/langfuse_event_data` | Local OTel carrier and eval blobs when `LANGFUSE_EVENT_STORAGE_BACKEND=local` |
75-
| `langfuse_minio_data` | `/data` in `minio` | Optional MinIO bucket data when the `s3` profile is enabled |
68+
| Volume | Mounted path | Stores |
69+
| ---------------------------- | ----------------------------- | ----------------------------------------------------------------------------- |
70+
| `langfuse_postgres_data` | `/var/lib/postgresql/data` | Postgres application state: users, projects, API keys, prompts, config |
71+
| `langfuse_greptimedb_data` | `/greptimedb_data` | GreptimeDB analytics store: traces, observations, scores, dashboards |
72+
| `langfuse_redis_data` | `/data` | Redis queue state |
73+
| `langfuse_media_data` | `/langfuse_media_data` | Local media uploads when `LANGFUSE_MEDIA_STORAGE_BACKEND=local` |
74+
| `langfuse_event_data` | `/langfuse_event_data` | Local OTel carrier and eval blobs when `LANGFUSE_EVENT_STORAGE_BACKEND=local` |
75+
| `langfuse_batch_export_data` | `/langfuse_batch_export_data` | Local batch-export files when `LANGFUSE_BATCH_EXPORT_STORAGE_BACKEND=local` |
76+
| `langfuse_minio_data` | `/data` in `minio` | Optional MinIO bucket data when the `s3` profile is enabled |
7677

7778
Runtime logs are not written to a separate application log directory by default. Web, worker, the standalone supervisor, Postgres, Redis, and GreptimeDB all write to container stdout/stderr; collect them through `docker compose logs` or your Docker logging driver. If you enable GreptimeDB file logging or replace Docker named volumes with bind mounts, keep the log directory outside the container's writable layer and include it in the same backup/retention plan as `langfuse_greptimedb_data`.
7879

79-
Do not run `docker compose down -v` on a real deployment unless you intentionally want to delete the service data. `docker compose down` removes containers and networks but keeps the named volumes; `down -v` removes them. If you replace the named volumes with bind mounts, keep the same container paths above and make sure the app containers can write to the media/event paths. In the split topology, both `langfuse-web` and `langfuse-worker` must mount the same `langfuse_media_data` and `langfuse_event_data` storage.
80+
Do not run `docker compose down -v` on a real deployment unless you intentionally want to delete the service data. `docker compose down` removes containers and networks but keeps the named volumes; `down -v` removes them. If you replace the named volumes with bind mounts, keep the same container paths above and make sure the app containers can write to the media/event paths. In the split topology, both `langfuse-web` and `langfuse-worker` must mount the same `langfuse_media_data`, `langfuse_event_data`, and `langfuse_batch_export_data` storage.
8081

8182
## 2. Migrations run automatically on startup
8283

web/src/pages/api/public/batch-exports/[batchExportId]/download.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,17 @@ export default async function handler(
8484
}
8585

8686
const downloadName = path.basename(token.fileName);
87+
// RFC 5987/6266: give an ASCII-only fallback (control chars, quotes, and
88+
// non-ASCII stripped so the header cannot be broken or injected) plus a
89+
// UTF-8 `filename*` carrying the real name. Mirrors the trace download route.
90+
const asciiFallback =
91+
downloadName.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "_") ||
92+
"batch-export";
8793
res.setHeader("Content-Type", token.contentType);
8894
res.setHeader("Content-Length", String(size));
8995
res.setHeader(
9096
"Content-Disposition",
91-
`attachment; filename="${downloadName}"`,
97+
`attachment; filename="${asciiFallback}"; filename*=UTF-8''${encodeURIComponent(downloadName)}`,
9298
);
9399

94100
await pipeline(createReadStream(filePath), res);

0 commit comments

Comments
 (0)