Skip to content

v0.33.0: security + correctness patch

Choose a tag to compare

@queelius queelius released this 02 May 00:53
· 671 commits to main since this release

Six Critical findings from the post-v0.32.0 code review, batched into
one focused security + correctness release. No API changes; clients
with the same wire usage are unaffected.

Fixed

Security

  • Constant-time admin token comparison (admin/auth.py). Naive
    != short-circuited on first byte mismatch, leaking the admin token
    byte-by-byte to a network attacker measuring response time. The
    admin API can disable models, restart workers, and trigger arbitrary
    muse pull of any HF URI (full code execution via crafted
    manifest), so this was the only thing standing between an attacker
    and supervisor takeover. Now uses secrets.compare_digest.

  • SSRF protection in _fetch_http_url (image_input.py). The
    image field on /v1/images/generations (img2img),
    /v1/images/embeddings, and /v1/images/animations accepted any
    http(s):// URL with no IP validation. An unauthenticated request
    could reach 169.254.169.254 (cloud metadata), the worker's own
    loopback ports (127.0.0.1:9001), and private LAN ranges. Now
    resolves the hostname and rejects private/loopback/link-local/
    multicast/reserved/unspecified IPs. Operators on trusted networks
    can opt out via MUSE_ALLOW_PRIVATE_FETCH=1.

  • Bounded reads on multipart uploads (image_input.py).
    decode_image_file did await file.read() (unbounded) then checked
    size after, fully buffering a 1GB upload before rejection. Used by
    /v1/images/edits, /variations, /upscale, /segment. Now uses
    await file.read(max_bytes + 1). Same hazard fixed in the data-URL
    path (a 1GB base64 string would inflate via b64decode before the
    size check; now pre-checks the b64 length before decoding).

Correctness

  • SSE error event on stream backend failure
    (chat_completion/routes.py). When chat_stream raised
    mid-stream, the consumer logged the error then yielded [DONE],
    indistinguishable from a clean empty completion. Now yields
    event: error carrying the OpenAI error envelope, then [DONE].
    Also switched the producer thread from
    asyncio.run_coroutine_threadsafe(...).result() to
    loop.call_soon_threadsafe(queue.put_nowait, ...) (the pattern
    used correctly in audio_speech), avoiding a producer-thread
    deadlock when the event loop is busy.

  • OpenAI error envelope on audio_speech encoding failure
    (audio_speech/routes.py). A failure in audio_to_wav_bytes
    raised HTTPException(detail=str(e)), yielding {"detail": "..."}
    instead of the documented {"error": {code, message, type}}
    envelope. Now uses error_response(...).

Operability

  • Pip subprocess timeouts (refresh.py, core/venv.py,
    core/install.py). All three pip-install pathways had no
    timeout=, so a hung PyPI mirror could wedge the parent
    indefinitely with no escape except SIGINT. All three now use
    _PIP_TIMEOUT = 1800 (30 min); refresh.py catches
    TimeoutExpired and returns a failed RefreshResult.

New env var

  • MUSE_ALLOW_PRIVATE_FETCH=1 opts out of the SSRF guard on
    _fetch_http_url. Default-off; operators on trusted networks who
    want image URLs to reach internal services can flip it on.

Test count

2259 (v0.32.0) → 2272 (v0.33.0). Net +13 regression guards across
the six fixes. Every pre-existing test still passes; no test changes
were required for the existing suite.

Plan + transcript

  • docs/superpowers/plans/2026-05-01-v0.33.0-security-correctness.md
  • Findings sourced from a code-reviewer agent run after the v0.32.0
    ship, surfacing 22 issues (6 Critical, 9 Important, 7 Minor). This
    release closes the 6 Critical. Important and Minor findings are
    tracked for follow-up patch releases.