fix(security): private temp dir for docker installer, https for OMDb, md5 non-security flag

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-10-04 18:57:34 +02:00
parent 4f30ee5c0d
commit 668681dd24
3 changed files with 12 additions and 6 deletions

View File

@ -13,6 +13,7 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
- **Voice list rows no longer show the "Source unknown" badge** (it crowded the play buttons). The badge stays in the voice inspector, next to the Source & consent panel.
### Fixed
- **Security hardening (Sonar findings).** The Docker installer downloads `get-docker.sh` into a private `mkdtemp` dir (not a predictable `/tmp` path) and removes it afterwards; the OMDb poster lookup uses HTTPS; the fish.audio cache key marks MD5 as `usedforsecurity=False`.
- **Background jobs could be garbage-collected mid-run.** Fire-and-forget `asyncio.create_task` calls (magazine, conversation, tts, stt, library, sources) now go through `core.tasks.spawn`, which keeps a strong reference until the task ends and logs failures by exception class only.
- **Voice archetype gallery broke `npm run check:frontend`**: its `say` helper collided with `magazine.js`; renamed to `archSay`.
- **Export failed with HTTP 500 when provenance tagging failed** (EPUB/ZIP/article MP3 with audio ffmpeg cannot tag); it now ships the untagged file and logs a warning.

View File

@ -6,6 +6,7 @@ import platform
import queue
import shutil
import subprocess
import tempfile
import threading
import webbrowser
from pathlib import Path
@ -186,7 +187,7 @@ class GuidedInstaller(tk.Tk):
self.log.see("end")
self.after(120, self._drain_log)
def run_threaded(self, title: str, commands: list[list[str]]) -> None:
def run_threaded(self, title: str, commands: list[list[str]], cleanup_dir: str | None = None) -> None:
def worker():
self.log_line(f"\n== {title} ==")
for cmd in commands:
@ -203,6 +204,8 @@ class GuidedInstaller(tk.Tk):
except Exception as exc:
self.log_line(f"Failed: {exc}")
break
if cleanup_dir:
shutil.rmtree(cleanup_dir, ignore_errors=True)
threading.Thread(target=worker, daemon=True).start()
def command_ok(self, cmd: list[str]) -> bool:
@ -260,11 +263,13 @@ class GuidedInstaller(tk.Tk):
"Run Docker's official Linux convenience installer?\n\nThis may ask for sudo privileges and needs internet access.",
):
return
tmp = tempfile.mkdtemp(prefix="docker-install-") # 0700, unpredictable name
script = os.path.join(tmp, "get-docker.sh")
self.run_threaded("Install Docker", [
["curl", "-fsSL", "https://get.docker.com", "-o", "/tmp/get-docker.sh"],
["sh", "/tmp/get-docker.sh"],
["curl", "-fsSL", "https://get.docker.com", "-o", script],
["sh", script],
["docker", "version"],
])
], cleanup_dir=tmp)
def generate_compose(self) -> None:
GENERATED_DIR.mkdir(parents=True, exist_ok=True)

View File

@ -875,7 +875,7 @@ async def fishaudio_voices(request: Request):
params["tag"] = tags
import hashlib
cache_dir = CONFIG_DIR / "fishaudio_cache"
cache_key = hashlib.md5(json.dumps(params, sort_keys=True, default=str).encode()).hexdigest()
cache_key = hashlib.md5(json.dumps(params, sort_keys=True, default=str).encode(), usedforsecurity=False).hexdigest()
cache_file = cache_dir / f"{cache_key}.json"
try:
r = await asyncio.to_thread(
@ -1176,7 +1176,7 @@ async def movie_poster(title: str = ""):
clean = re.sub(r"\s*\(.*?\)\s*$", "", title).strip()
result = {"poster": "", "year": "", "title": title}
try:
url = f"http://www.omdbapi.com/?t={_q(clean)}&type=movie&apikey=trilogy"
url = f"https://www.omdbapi.com/?t={_q(clean)}&type=movie&apikey=trilogy"
r = await asyncio.to_thread(lambda: requests.get(url, timeout=8))
if r.ok:
d = r.json()