| 1 |
1 |
|
# MakeNotWork -- Audit Review
|
| 2 |
2 |
|
|
| 3 |
|
- |
**Last audited:** 2026-05-01 (Run 18, MNW server only)
|
| 4 |
|
- |
**Previous audit:** 2026-04-30 (Run 17, cross-project)
|
|
3 |
+ |
**Last audited:** 2026-05-02 (Run 19, MNW server + doc fuzz)
|
|
4 |
+ |
**Previous audit:** 2026-05-01 (Run 18, MNW server only)
|
| 5 |
5 |
|
|
| 6 |
6 |
|
## Overall Grade: A
|
| 7 |
7 |
|
|
| 8 |
|
- |
Run 18: 1,933 tests (1,209 unit + 724 integration). 0 clippy warnings. v0.4.6. ~80,470 LOC. 5 cold spots (1 bug, 4 minor). 34 integration test failures (likely related to uncommitted moderation/promo code changes).
|
|
8 |
+ |
Run 19: 1,923 tests passing (1,213 unit + 679 integration + 28 doc + 3 load; 10 ignored), 0 failed. 0 clippy warnings. v0.4.7. ~81,384 LOC. 2 cold spots (0 bugs, 2 minor). Combined with doc fuzz for creator email readiness assessment.
|
| 9 |
9 |
|
|
| 10 |
10 |
|
## Scorecard
|
| 11 |
11 |
|
|
| 12 |
12 |
|
| Dimension | Grade | Notes |
|
| 13 |
13 |
|
|-----------|:-----:|-------|
|
| 14 |
|
- |
| Code Quality | A | 3 production `.unwrap()` in git/raw.rs + 1 in helpers.rs (convention violations, not crash risks) |
|
| 15 |
|
- |
| Architecture | A- | 4 instances of inline SQL in route handlers (stripe/checkout, dashboard/forms, landing) |
|
| 16 |
|
- |
| Testing | A | 1,933 tests, 34 integration failures (uncommitted changes), proptest active |
|
|
14 |
+ |
| Code Quality | A | unwrap() in git/raw.rs fixed. helpers.rs split into formatting/crypto/rate_limit (395 lines from 1,268) |
|
|
15 |
+ |
| Architecture | A | Inline SQL in route handlers still present (4 locations) but minor |
|
|
16 |
+ |
| Testing | A | 1,923 tests, 0 failures. proptest active. Lockout test fixed (auth rate limit fast-tests override) |
|
| 17 |
17 |
|
| Security | A+ | Zero SQL injection vectors, constant-time compare everywhere, fail-closed scanning, CSRF on all forms |
|
| 18 |
|
- |
| Performance | A- | analytics.rs query duplication, hash_lookup creates new reqwest::Client per call |
|
| 19 |
|
- |
| Documentation | A | Module-level //! on every file, response conventions documented |
|
| 20 |
|
- |
| Dependencies | A | All deps at latest stable, async-trait cleanup opportunity (Rust 2024 native async) |
|
| 21 |
|
- |
| Frontend | A | Askama auto-escape, all `\|safe` uses verified safe, strong CSP, no raw innerHTML |
|
| 22 |
|
- |
| Type Safety | A | 50+ UUID newtypes, validated string types, Cents monetary newtype. Minor: moderation.rs uses raw Uuid |
|
| 23 |
|
- |
| Observability | A | 1 missing #[instrument] (api/mod.rs:public_projects), otherwise comprehensive |
|
|
18 |
+ |
| Performance | A | analytics.rs deduplicated (623->468 LOC). hash_lookup uses static reqwest::Client |
|
|
19 |
+ |
| Documentation | A- | Module-level //! on every file. No README.md (CONTRIBUTING.md partially fills role) |
|
|
20 |
+ |
| Dependencies | A- | Most deps current. tokio (1.49->1.50), uuid (1.20->1.22), chrono (0.4.43->0.4.44) pins stale |
|
|
21 |
+ |
| Frontend | A | Askama auto-escape, all `\|safe` uses verified safe, no raw innerHTML. No CSP header (inline onclick pattern) |
|
|
22 |
+ |
| Type Safety | A | 50+ UUID newtypes, validated string types, Cents monetary newtype. moderation.rs still uses raw Uuid |
|
|
23 |
+ |
| Observability | A | Comprehensive #[instrument] coverage. Structured logging throughout |
|
| 24 |
24 |
|
| Concurrency | A | ON CONFLICT, FOR UPDATE, atomic WHERE guards, advisory locks, optimistic versioning |
|
| 25 |
|
- |
| Resilience | A+ | Graceful shutdown with hard deadline, migration exit code 2, health monitor with status-transition alerts |
|
|
25 |
+ |
| Resilience | A | Graceful shutdown with hard deadline. One missing timeout: payments/connect.rs:198 raw reqwest call |
|
| 26 |
26 |
|
| API Consistency | A | Documented response conventions, json_error_layer, versioned SyncKit routes |
|
| 27 |
|
- |
| Migration Safety | A | 85 additive migrations, IF EXISTS on drops, CHECK constraints |
|
| 28 |
|
- |
| Codebase Size | A- | 80K LOC well-organized; helpers.rs (1,268 lines) should be split |
|
|
27 |
+ |
| Migration Safety | A | 87 additive migrations, IF EXISTS on drops, CHECK constraints |
|
|
28 |
+ |
| Codebase Size | A | 81K LOC well-organized. helpers.rs split complete. analytics.rs deduplicated |
|
| 29 |
29 |
|
|
| 30 |
30 |
|
## Module Heatmap
|
| 31 |
31 |
|
|
| 95 |
95 |
|
|
| 96 |
96 |
|
### Cold Spots
|
| 97 |
97 |
|
|
| 98 |
|
- |
1. **db/moderation.rs type safety (B):** Uses raw `Uuid` for action IDs and `String` for action_type instead of typed newtypes. Only file in db/ without typed IDs.
|
| 99 |
|
- |
2. **routes/git/raw.rs code quality (B):** Three `.unwrap()` calls on `Response::builder().body()` — violates no-unwrap convention.
|
| 100 |
|
- |
3. **db/analytics.rs code quality (B+):** 12 near-identical query blocks across timeseries/comparison functions. Query builder pattern would cut ~150 LOC.
|
| 101 |
|
- |
4. **routes/admin/ CSRF (B):** POST routes rely on AdminUser session + SameSite cookies but no explicit CSRF token validation.
|
| 102 |
|
- |
5. **wam_client.rs testing (B):** Zero unit tests for the WAM HTTP client.
|
|
98 |
+ |
1. **db/moderation.rs type safety (B):** Uses raw `Uuid` for action IDs and `String` for action_type instead of typed newtypes. Only file in db/ without typed IDs. (Carried from Run 18)
|
|
99 |
+ |
2. **payments/connect.rs resilience (B):** Missing timeout on raw reqwest call to Stripe resume-subscription API at line 198. Only unprotected outbound HTTP call in the codebase.
|
|
100 |
+ |
|
|
101 |
+ |
### Resolved Cold Spots (from Run 18)
|
|
102 |
+ |
- ~~routes/git/raw.rs unwrap()~~ -- Fixed
|
|
103 |
+ |
- ~~db/analytics.rs duplication~~ -- Fixed (Scope enum extraction)
|
|
104 |
+ |
- ~~wam_client.rs testing~~ -- Fixed (5 tests added)
|
| 103 |
105 |
|
|
| 104 |
106 |
|
## Mandatory Surprise
|
| 105 |
107 |
|
|
| 106 |
|
- |
**BUG: Sandbox creator tier mismatch.** `db/users.rs:259` inserts `'SmallFiles'` (PascalCase) into the `creator_tier` column, but `impl_str_enum!` maps `SmallFiles => "small_files"` (snake_case). When `auth.rs:178` parses the tier via `.parse().ok()`, the mismatch silently returns `None`. Sandbox users lose their SmallFiles tier privileges — storage limits, file upload permissions, and tier-gated features all fall back to no-tier defaults. Fix: change the SQL literal from `'SmallFiles'` to `'small_files'`.
|
|
108 |
+ |
**Hand-rolled Stripe v2 webhook signature verification.** `payments/webhooks.rs:184-228` implements v2 webhook signature verification from scratch because the `stripe` crate only supports v1. The implementation correctly parses `t=,v1=` headers, rejects timestamps >300s in either direction (replay + clock skew), and uses `hmac::Hmac::verify_slice()` which is constant-time via the `subtle` crate. Both past and future timestamp bounds are checked. Well-tested with 26 unit tests including edge cases (stale, future, wrong secret, within tolerance).
|
| 107 |
109 |
|
|
| 108 |
110 |
|
### Previous Surprises
|
| 109 |
111 |
|
|
|
112 |
+ |
**Run 18:** Sandbox tier mismatch bug (SmallFiles vs small_files). Fixed.
|
|
113 |
+ |
|
| 110 |
114 |
|
**Run 17:** TOCTOU-safe slug generation with retry loop + advisory lock pattern for sandbox IP cap.
|
| 111 |
115 |
|
|
| 112 |
116 |
|
**Run 15:** Session touch cache — DashMap with 30s TTL avoids N+1 session queries.
|
| 140 |
144 |
|
|
| 141 |
145 |
|
## Action Items
|
| 142 |
146 |
|
|
| 143 |
|
- |
### Run 18 (2026-05-01)
|
|
147 |
+ |
### Run 19 (2026-05-02)
|
| 144 |
148 |
|
|
| 145 |
|
- |
39. **[HIGH]** Fix sandbox tier: change `'SmallFiles'` to `'small_files'` in `db/users.rs:259`
|
| 146 |
|
- |
40. **[MEDIUM]** Extract inline SQL from route handlers to db/ layer (4 locations listed above)
|
| 147 |
|
- |
41. **[MEDIUM]** Add `ModerationActionId` newtype and `ModerationActionType` enum to `db/moderation.rs`
|
| 148 |
|
- |
42. **[MEDIUM]** Replace `.unwrap()` in `routes/git/raw.rs:80,142,190` with proper error handling
|
| 149 |
|
- |
43. **[MEDIUM]** Add `#[tracing::instrument]` to `routes/api/mod.rs:public_projects`
|
| 150 |
|
- |
44. **[LOW]** Replace production `.unwrap()` at `helpers.rs:52` with `unwrap_or_else` or `HeaderValue::from_static`
|
| 151 |
|
- |
45. **[LOW]** Replace production `.unwrap()` at `monitor.rs:105` with pattern match
|
| 152 |
|
- |
46. **[LOW]** Add unit tests to `wam_client.rs`
|
| 153 |
|
- |
47. **[LOW]** Add explicit cents/dollars format option to CSV import
|
| 154 |
|
- |
48. **[DEFERRED]** Split helpers.rs (~1,268 lines) into focused modules (formatting, crypto, rate_limit)
|
| 155 |
|
- |
49. **[DEFERRED]** Reduce analytics.rs query duplication via builder pattern or macro (~150 LOC savings)
|
| 156 |
|
- |
50. **[DEFERRED]** Remove `async-trait` in favor of Rust 2024 native async traits
|
|
149 |
+ |
51. **[MEDIUM]** Add timeout to `payments/connect.rs:198` raw reqwest call to Stripe resume-subscription API
|
|
150 |
+ |
52. **[MEDIUM]** Add `ModerationActionId` newtype and `ModerationActionType` enum to `db/moderation.rs` (carried from Run 18 #41)
|
|
151 |
+ |
53. **[LOW]** Add README.md to server/ with setup instructions and architecture overview link
|
|
152 |
+ |
54. **[LOW]** Bump dependency pins: tokio 1.49->1.50, uuid 1.20->1.22, chrono 0.4.43->0.4.44, yara-x 1.13->1.14, anyhow 1.0.101->1.0.102
|
|
153 |
+ |
55. **[DEFERRED]** Extract inline SQL from route handlers to db/ layer (4 locations, carried from Run 18 #40)
|
|
154 |
+ |
56. **[DEFERRED]** Remove `async-trait` in favor of Rust 2024 native async traits (carried from Run 18 #50)
|
|
155 |
+ |
57. **[DEFERRED]** Migrate inline `onclick` handlers to `addEventListener` to enable strict CSP
|
| 157 |
156 |
|
|
| 158 |
157 |
|
### Open (blocked on upstream)
|
| 159 |
158 |
|
23. Monitor aws-sdk-s3 for lru fix (RUSTSEC-2026-0002)
|
| 162 |
161 |
|
33. bincode unmaintained (RUSTSEC-2025-0141) — upstream via syntect/yara-x, warning only
|
| 163 |
162 |
|
|
| 164 |
163 |
|
### Previously resolved
|
| 165 |
|
- |
All items 1-22 and 31-38 from previous audits verified intact.
|
|
164 |
+ |
All items 1-22, 31-38, and most Run 18 items verified intact.
|
| 166 |
165 |
|
|
| 167 |
|
- |
## Previous Action Item Verification
|
|
166 |
+ |
## Previous Action Item Verification (Run 18)
|
| 168 |
167 |
|
|
| 169 |
168 |
|
| # | Item | Status |
|
| 170 |
169 |
|
|---|------|--------|
|
| 171 |
|
- |
| 23 | aws-sdk-s3 lru fix | Unfixed (upstream) |
|
| 172 |
|
- |
| 24 | async-stripe instant fix | Unfixed (upstream) |
|
| 173 |
|
- |
| 25 | aws-sdk-s3 rustls-webpki fix | Unfixed (upstream) |
|
| 174 |
|
- |
| 33 | bincode unmaintained | Unfixed (upstream) |
|
| 175 |
|
- |
| 34-38 | Run 15 items | All fixed/verified |
|
|
170 |
+ |
| 39 | Sandbox tier bug (`'SmallFiles'`->`'small_files'`) | Fixed |
|
|
171 |
+ |
| 40 | Extract inline SQL from route handlers | Unfixed (carried as #55) |
|
|
172 |
+ |
| 41 | Moderation typed IDs | Unfixed (carried as #52) |
|
|
173 |
+ |
| 42 | `.unwrap()` in git/raw.rs | Fixed |
|
|
174 |
+ |
| 43 | Missing `#[instrument]` on public_projects | Fixed |
|
|
175 |
+ |
| 44 | `.unwrap()` in helpers.rs | Fixed (helpers.rs split) |
|
|
176 |
+ |
| 45 | `.unwrap()` in monitor.rs | Fixed |
|
|
177 |
+ |
| 46 | wam_client.rs unit tests | Fixed (5 tests added) |
|
|
178 |
+ |
| 47 | CSV import cents/dollars format | Fixed (heuristic removed) |
|
|
179 |
+ |
| 48 | Split helpers.rs | Fixed (1,268->395 lines: formatting.rs, crypto.rs, rate_limit.rs) |
|
|
180 |
+ |
| 49 | Analytics.rs deduplication | Fixed (623->468 lines via Scope enum) |
|
|
181 |
+ |
| 50 | Remove async-trait | Unfixed (carried as #56) |
|
|
182 |
+ |
| 23-25, 33 | Upstream dep advisories | Unfixed (chronic, upstream-blocked) |
|
| 176 |
183 |
|
|
| 177 |
|
- |
No regressions found. Items 23-25, 33 remain open across 3+ consecutive audits (chronic, but upstream-blocked).
|
|
184 |
+ |
10 of 12 Run 18 items fixed. 2 carried forward. No regressions.
|
| 178 |
185 |
|
|
| 179 |
186 |
|
## Metrics Over Time
|
| 180 |
187 |
|
|
| 197 |
204 |
|
| 2026-04-22 (Run 15 corrected) | ~67,442 | -- | 1,359 | ~20 | 0 | 1 | A |
|
| 198 |
205 |
|
| 2026-04-30 (Run 17) | ~79,334 | -- | 1,861 | ~15.0 | 0 | 0 | A |
|
| 199 |
206 |
|
| 2026-05-01 (Run 18) | ~80,470 | -- | 1,933 (34 int. fail) | ~15.1 | 0 | 5 | A |
|
|
207 |
+ |
| 2026-05-02 (Run 19) | ~81,384 | -- | 1,923 (0 fail) | ~23.6 | 0 | 2 | A |
|
| 200 |
208 |
|
|
| 201 |
209 |
|
---
|
| 202 |
210 |
|
|