• v3.2.0 16d098f5f2

    v3.2.0
    All checks were successful
    build-and-release / docker (push) Successful in 3s
    Stable

    debe released this 2026-10-12 08:52:13 +00:00 | 10 commits to main since this release

    Full audit pass (security, API edge cases, frontend, data integrity, load) with fixes.

    Fixed — data safety

    • purge could destroy a live topic. deleteTopic() now only ever deletes a topic
      that is already in the trash, so a stray DELETE can never wipe live data.
    • Editing an update discarded the edit when the AI was unreachable (the request
      returned 500 and the new text was lost). The edit is now saved first and the analysis
      is marked failed, exactly like creating an update.
    • An update inside a trashed topic could be deleted while the topic was still
      recoverable; that path is now refused.
    • /api/restore accepted anything and replaced the whole database with it (empty
      titles, unknown statuses, non-timestamp dates, orphan updates, duplicate ids). Backup
      files are now validated before a single row is touched, and failures return 400 with
      invalid_backup instead of a raw SQLite 500.
    • NUL characters silently truncated text on write (SQLite stops at \0). They are
      now rejected with invalid_characters.
    • owner was accepted when creating a topic and then silently dropped.

    Fixed — security

    • Stored XSS: formatDate() returned non-timestamp input verbatim into innerHTML
      in three places, and statusBadge() injected a crafted status unescaped. Both are now
      escaped/whitelisted, and restore validation rejects the payload that carried them.
    • /api/models was not admin-gated and honoured a caller-supplied ?base=, which
      forwarded the stored AI key to any host (SSRF + key exfiltration). It is now
      administrator-only, together with /api/backup and /api/restore.
    • Open redirect: ?next=//evil.com passed the startsWith('/') check. Only
      same-origin absolute paths are accepted now.
    • No session expiry server-side — a 400-day-old session still authenticated. Idle
      (7 days) and absolute (30 days) limits are enforced on every request.
    • Malformed cookies (%zz) and a JSON null body produced 500s; both are handled.
    • Security headers were missing entirely. Every response now carries a CSP
      (frame-ancestors 'none', no inline scripts), nosniff, DENY and Referrer-Policy.
    • Session cookies get Secure when the app is served over HTTPS.
    • The data directory is 0700 and the database 0600 (they hold session tokens and keys).
    • /api/health no longer discloses the AI endpoint or model to unauthenticated callers.
    • Saving an unreachable SimpleAuth URL is refused, so it can no longer lock every
      administrator out with no way back through the UI.
    • Removed the dead sauth_admin_key setting.

    Fixed — robustness

    • No outbound timeouts: a hung AI or identity provider held a request open for
      300 s and leaked the socket. All outbound calls now time out (AI 180 s, models 20 s,
      SimpleAuth 15 s).
    • The advertised 413 is now delivered instead of resetting the connection.
    • Field length caps (title 500, description 20 000, owner/author 200, update 50 000) so
      one request can no longer store megabytes.
    • ai_base set to an empty string no longer silently reverts to the DeepSeek default,
      and a JSON null no longer becomes the literal string "null".
    • AI failures now return 502 ai_unreachable instead of a generic 500.
    • GET /api/topics/:id/updates without a cursor returns the most recent page instead of
      an empty list.

    Fixed — interface

    • "Add update" was broken whenever sign-in was disabled: the client stopped sending
      an author but the server still required one. The composer now shows a name field in
      that mode only.
    • .hidden had no CSS rule, so the admin-only Settings entry stayed visible to members.

    Added

    • 13 regression tests covering every fix above, plus migration tests for the two older
      database schemas (v1 and 2.6-era) proving that an existing installation upgrades with
      no data loss.
    Downloads