Spaces:
Sleeping
Sleeping
Commit History
-
Fail closed on API auth, and compare tokens in constant time
Found in the 2026-08-11 security review of the DO droplet. _check_api_token was `if expected and x_api_token != expected` — it skipped the check entirely whenever the token env var was unset or empty. That is fail-open, and it matters here specifically because Caddy exempts /api/* from the Google OAuth gate (Miscellaneous/droplet-infra/Caddyfile in the monorepo), making this check the *entire* perimeter for those routes. One typo'd env file, or a container recreated without --env-file, would have published the whole API to the internet with no error anywhere — and a missing env var on a redeploy is not hypothetical, it already happened once to TranslationUI's HF_TOKEN. It now returns 503 when unconfigured, matching what _check_hf_token next to it already did; the inconsistency between the two is what gave the bug away. Also switched both token comparisons to hmac.compare_digest so they don't leak length/prefix information through timing. The test fixtures relied on the fail-open pat…f54c348
-
Fix path-traversal via project_id in the new job API
Unlike the UI, which only ever generates project_id itself (slugify + uuid hex, always safe), the new POST /api/jobs and GET /api/projects/{id} endpoints accept it directly from an external caller so an existing project can be targeted. tts/core/project_store.py's local (TTS_DATA_DIR) backend builds a real filesystem path directly from it — _project_path(project_id) = "projects/{project_id}/project.json", joined onto the data root — with no sanitization. An authenticated caller (holding a valid X-API-Token) could pass e.g. project_id=../../../../tmp/pwned for arbitrary file write/read inside the container, which runs as root (no USER directive in this repo's Dockerfile). project_id is now validated against the same safe-slug shape new_project_id() already produces before it reaches the store, on both the job submission and project-read endpoints. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>021451f -
Add async REST job API alongside the existing Gradio UI
Mirrors HuggingFaceSpaces/ArchiveAI-STT's server/api_jobs.py pattern: POST /api/jobs, GET /api/jobs/{id}, GET /api/jobs, GET /api/status, POST /api/drain, GET /api/ping, plus GET /api/projects and GET /api/projects/{id} for an external caller to poll for edits made through the UI (project updated_at changes on every UI edit). Prerequisite refactor: extracted the actual ElevenLabs call (_synthesize_turn_and_store) out of app.py into tts/core/turn_audio.py (synthesize_turn) and tts/core/turns.py (build_project_payload), so both the Gradio UI and the new API can call the same pure functions without either module importing from the other. app.py's version is now a thin wrapper that adds its Gradio-state-specific segment mutation on top. Existing tests patching the old app.synthesize_turn_audio location were updated to patch tts.core.turn_audio.synthesize_turn_audio instead; all 443 pre-existing tests still pass unchanged in behavior. A submitted job runs the same pipeline Turn Review's U…ab0a768