Fix bug-audit round 2: facets, tx safety, SSR layout, theme + DRY

Fix bug-audit round 2: facets, tx safety, SSR layout, theme + DRY

#48 in Riparion/riparion-cms — merged 2026-06-02

Summary

A fresh parallel multi-agent audit (DB layer, auth/security, server logic, frontend/SSR, KISS/DRY) following the round-1 fixes in #46. Findings were de-duplicated and verified against the code to rule out false positives. No Critical issues this round.

High

  • Comment-status consts. Added STATUS_APPROVED/STATUS_PENDING/STATUS_REJECTED and routed the comment call sites through them — the same scattered-string-literal class as the already-fixed STATUS_PUBLISHED, left half-done for comments (a typo would compile and silently break moderation).
  • Facet bind/SQL coupling. facet_clause now returns its ordered bind values alongside the SQL fragment (mirroring queryloop_where), applied with a single loop. The four copy-pasted .bind() blocks could silently drift out of lockstep with the placeholder order, with no compiler help.

Medium

  • Taxonomy delete transactions. delete_category_db / delete_tag_db ran two execute()s with no transaction; a failure between them left a half-applied state. Now wrapped in pool.begin()/commit() like delete_post_db.
  • SSR layout swap. FeedHome read the feed layout via use_resource (doesn't resolve during SSR), so the server rendered the default layout and the client re-rendered into the real one after hydration. Switched to use_server_future.
  • Hardcoded theme colors. Replaced bg-[#111] / bg-[#0f1116] panel surfaces with bg-[var(--primary-color-2)] so they track the --surface-hue theme knob.

Low

  • insert_variant_db binds width/height as i32 to match the INTEGER (int4) columns rather than relying on a Postgres int8→int4 assignment cast.
  • Dashboard "Top referrers" list keyed on referrer instead of the loop index.
  • Home <head> emits og:url via head::absolute_url + a <link rel="canonical"> (both omitted when SITE_URL is unset, matching posts/pages).
  • Added PAGE_COLUMNS / PAGE_SECTION_COLUMNS consts; routed the four duplicated page SELECT projections through them.

A number of lower-impact / intentional findings were reviewed and deliberately left unchanged (anonymous SSE replaying draft chart data, shared media-library usage visibility, the comment burst cap, per-read syntect highlighting, etc.) — see the local audit notes.

Test plan

  • cargo fmt --all --check
  • cargo clippy — server (sqlite), server (postgres), and wasm client — all clean under -D warnings
  • cargo test --features server,sqlite — 42 passed
  • Migrations untouched, so the live-Postgres CI job is unaffected.

🤖 Generated with Claude Code

Last updated 2026-06-03