| 1 |
# Multithreaded — Code Review |
| 2 |
|
| 3 |
**Date:** 2026-04-12 |
| 4 |
**Version:** 0.3.2 |
| 5 |
**Reviewer:** Claude (Opus 4.6) |
| 6 |
**Scope:** Full codebase review — all Rust source, SQL migrations, templates, CSS, JS, deploy config, tests |
| 7 |
|
| 8 |
## Summary |
| 9 |
|
| 10 |
Multithreaded is a forum application (~8,800 LOC Rust, ~20,800 total) built on Axum/PostgreSQL with MNW OAuth integration. 3-crate workspace (mt-core, mt-db, main app). 21 migrations, 21 HTML templates, 510 lines JS, 1,761 lines CSS. 225+ tests (35 unit + 190 integration). 0 clippy warnings. Security posture is strong. Code is clean and well-organized. |
| 11 |
|
| 12 |
**Overall: A** — consistent with the standing audit grade. No new vulnerabilities found. Several minor structural items noted below. |
| 13 |
|
| 14 |
--- |
| 15 |
|
| 16 |
## Findings |
| 17 |
|
| 18 |
### [MEDIUM] Dependency advisories — 4 active |
| 19 |
|
| 20 |
`cargo audit` reports 4 vulnerabilities: |
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
| aws-lc-sys 0.38.0 | RUSTSEC-2026-0044 | HIGH | `cargo update -p aws-lc-sys` (>=0.39.0) | |
| 25 |
| aws-lc-sys 0.38.0 | RUSTSEC-2026-0048 | HIGH 7.4 | Same | |
| 26 |
| rustls-webpki 0.103.9 | RUSTSEC-2026-0049 | — | `cargo update -p rustls-webpki` (>=0.103.10) | |
| 27 |
| rand 0.8.5 + 0.9.2 | RUSTSEC-2026-0097 | — | Unsoundness with custom loggers (not applicable here, but upgrade when deps allow) | |
| 28 |
|
| 29 |
Additionally 3 allowed warnings (rsa, lru — transitive, no direct fix available). |
| 30 |
|
| 31 |
aws-lc-sys and rustls-webpki are straightforward `cargo update` fixes. The rand advisory (RUSTSEC-2026-0097) affects `rand::rng()` with custom loggers — MT doesn't use custom loggers, so no practical impact, but both 0.8.5 (direct dep) and 0.9.2 (transitive via axum/governor) are flagged. Consider bumping the direct `rand` dep from 0.8 to 0.9 when convenient. |
| 32 |
|
| 33 |
### [MEDIUM] Three route files exceed 500-line guideline |
| 34 |
|
| 35 |
Per project conventions, files with >500 lines of branching logic should be split. |
| 36 |
|
| 37 |
|
| 38 |
|
| 39 |
| `src/routes/mod.rs` | 578 | Route tree, form types, 15+ helper functions | |
| 40 |
| `src/routes/forum/views.rs` | 628 | 11 view handlers | |
| 41 |
| `src/routes/forum/actions.rs` | 600 | 10 action handlers | |
| 42 |
|
| 43 |
`mod.rs` could split helpers into `routes/helpers.rs` (validation, permission checks, markdown rendering) and keep forms + route tree in `mod.rs`. The forum files could split by page area (thread views vs. listing views, thread actions vs. post actions). |
| 44 |
|
| 45 |
`queries.rs` (1,503) and `mutations.rs` (879) are exempt — flat lists of SQL query functions with no branching logic. |
| 46 |
|
| 47 |
### [LOW] Pagination partial not used consistently |
| 48 |
|
| 49 |
`templates/pages/forum_directory.html` (lines 40-49) inlines pagination markup instead of using `{% include "partials/pagination.html" %}`. Only `thread.html` uses the partial. If the pagination design changes, it needs updating in two places. |
| 50 |
|
| 51 |
### [LOW] CSS: `.form-inline-row` has contradictory display properties |
| 52 |
|
| 53 |
```css |
| 54 |
.form-inline-row { display: inline; gap: 0.25rem; flex-direction: row; align-items: center; } |
| 55 |
``` |
| 56 |
|
| 57 |
`gap`, `flex-direction`, and `align-items` are flex/grid properties — they have no effect with `display: inline`. Used on 2 admin forms. Should be `display: inline-flex`. |
| 58 |
|
| 59 |
### [LOW] No `Cache-Control` on HTML responses |
| 60 |
|
| 61 |
Static files get caching from tower-http `ServeDir`. Image proxy sets `max-age=31536000`. But regular HTML pages have no explicit cache control headers. Adding `private, no-cache` for authenticated pages prevents stale content after logout/session change. |
| 62 |
|
| 63 |
### [INFO] Test harness duplication |
| 64 |
|
| 65 |
`tests/harness/mod.rs`: `new()` and `new_with_admin()` share ~40 lines of identical setup code. `tests/harness/client.rs`: `send_raw` and `send_raw_with_token` duplicate ~60 lines. Not a correctness issue, but a maintenance burden. |
| 66 |
|
| 67 |
### [INFO] `MaybeUser` silently swallows session errors |
| 68 |
|
| 69 |
In `auth.rs`, the `MaybeUser` extractor returns `MaybeUser(None)` on any session read error. This is intentionally infallible, but session store failures silently deauthenticate users without logging at the extraction point. A `tracing::warn!` on the error path would help debugging. |
| 70 |
|
| 71 |
### [INFO] No index on `posts.removed_at` |
| 72 |
|
| 73 |
The moderation page queries removed posts and thread views filter by removal status. At scale, a partial index `WHERE removed_at IS NOT NULL` on `posts` would help. Current data volumes don't require it. |
| 74 |
|
| 75 |
--- |
| 76 |
|
| 77 |
## Strengths |
| 78 |
|
| 79 |
- **Security is defense-in-depth.** CSRF (constant-time comparison, auto-injected), SSRF (comprehensive private IP blocking on link previews), XSS (docengine sanitization + Askama autoescaping), SQL injection (all parameterized), rate limiting (per-IP + per-user), HMAC internal API with replay protection, EXIF stripping on uploads, CSP headers. 11 dedicated XSS tests. |
| 80 |
- **Immutable post model.** Posts cannot be edited or deleted by users — only corrected via footnotes or mod-removed (content preserved). Philosophically consistent and well-executed. |
| 81 |
- **Test infrastructure is excellent.** Per-test database isolation. Cookie-aware HTTP client with auto CSRF extraction. Full Axum app in-process. 22 workflow test files covering every feature area. |
| 82 |
- **Clean 3-crate separation.** mt-core has zero deps beyond chrono. mt-db has no web framework deps. Main crate handles all HTTP concerns. No circular dependencies. |
| 83 |
- **Production hardening.** Systemd service with 18 security directives (NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, RestrictAddressFamilies, etc.). Memory cap 512M. Graceful shutdown. |
| 84 |
- **Quote verification.** SHA-256 hash of quoted text verified server-side before rendering attribution. Prevents fabricated quotes. |
| 85 |
- **Observability.** 86+ `#[instrument(skip_all)]` annotations. Structured logging via tracing with env filter. |
| 86 |
|
| 87 |
## Security Checklist |
| 88 |
|
| 89 |
|
| 90 |
|
| 91 |
| SQL injection | Pass — all queries parameterized via SQLx | |
| 92 |
| XSS | Pass — docengine sanitization + Askama autoescaping + CSP | |
| 93 |
| CSRF | Pass — synchronizer token, constant-time comparison, all mutating routes | |
| 94 |
| SSRF | Pass — link preview validates IPs (IPv4+IPv6 private ranges blocked) | |
| 95 |
| Auth bypass | Pass — fail-closed access checks, session cycling on login | |
| 96 |
| IDOR | Pass — all resource access checks use authenticated user context | |
| 97 |
| Rate limiting | Pass — per-IP (tower-governor) + per-user (15 posts/60s) + upload rate (20/hr) | |
| 98 |
| Secrets in source | Pass — env-based config, no secrets committed (env templates have placeholders) | |
| 99 |
| Dependency advisories | Fail — 4 active (2 HIGH via aws-lc-sys, fixable with cargo update) | |
| 100 |
|
| 101 |
## Metrics |
| 102 |
|
| 103 |
|
| 104 |
|
| 105 |
| Rust source LOC | ~8,800 | |
| 106 |
| Total LOC (all files) | ~20,800 | |
| 107 |
| Rust source files | 39 | |
| 108 |
| Test files | 24 (harness + workflows) | |
| 109 |
| Test count | 225+ (35 unit + 190 integration) | |
| 110 |
| Tests/KLOC | ~26 | |
| 111 |
| Clippy warnings | 0 | |
| 112 |
| Migrations | 21 | |
| 113 |
| HTML templates | 21 | |
| 114 |
| Query functions | 55+ | |
| 115 |
| Mutation functions | 30+ | |
| 116 |
| Dependencies (direct) | 28 | |
| 117 |
| Audit advisories | 4 (2 HIGH, 2 low/info) | |
| 118 |
|
| 119 |
## Action Items |
| 120 |
|
| 121 |
1. ~~**[MEDIUM]** Run `cargo update -p aws-lc-sys -p rustls-webpki` to fix 3 of 4 advisories~~ — Done. aws-lc-sys 0.39.1, rustls-webpki 0.103.11. |
| 122 |
2. ~~**[MEDIUM]** Split `routes/mod.rs` helpers into `routes/helpers.rs`~~ — Done. mod.rs 281, helpers.rs 315. |
| 123 |
3. ~~**[LOW]** Fix `.form-inline-row` CSS to `display: inline-flex`~~ — Done. |
| 124 |
4. ~~**[LOW]** Use pagination partial in `forum_directory.html`~~ — Done. |
| 125 |
5. ~~**[LOW]** Add `Cache-Control: private, no-cache` to HTML responses~~ — Done. |
| 126 |
6. ~~**[MEDIUM]** Split `forum/views.rs` and `forum/actions.rs`~~ — Done. views.rs 424, thread.rs 224, posts.rs 443, actions.rs 172. |
| 127 |
7. **[INFO]** Transitive dep advisories (rand, rsa, lru) — no fix available, monitor upstream. |
| 128 |
8. **[INFO]** Add partial index on `posts.removed_at` when data volume warrants it. |
| 129 |
9. **[INFO]** Add `tracing::warn!` to `MaybeUser` extractor on session read errors. |
| 130 |
|