| 1 |
1 |
|
# MakeNotWork -- Audit Review
|
| 2 |
2 |
|
|
| 3 |
|
- |
**Last audited:** 2026-05-09 (Run 22, Ultra Fuzz -- 5-axis deep audit)
|
| 4 |
|
- |
**Previous audit:** 2026-05-08 (Run 21, Ultra Fuzz -- 5-axis deep audit)
|
|
3 |
+ |
**Last audited:** 2026-05-09 (Run 24, Ultra Fuzz -- 5-axis deep audit)
|
|
4 |
+ |
**Previous audit:** 2026-05-09 (Run 23, Ultra Fuzz -- 5-axis deep audit)
|
| 5 |
5 |
|
|
| 6 |
6 |
|
## Overall Grade: A
|
| 7 |
7 |
|
|
| 8 |
|
- |
Run 22: Ultra Fuzz (Payments, Storage, UX Wiring, Security, Performance). v0.5.9. ~87,853 LOC. 1,215 test annotations. 105 migrations. 1 SERIOUS finding (UX), 0 CRITICAL. 3 cold spots. All Run 21 action items fixed (12/12). Security posture A+. Observability gaps resolved (embed/, payments/ now instrumented).
|
|
8 |
+ |
Run 24: Ultra Fuzz (Payments, Storage, UX Wiring, Security, Performance). v0.5.9. ~88,082 LOC. ~1,215 test annotations. 107 migrations. 2 SERIOUS + 3 MINOR findings identified, all fixed in same-day remediation (1 false positive). 0 cold spots (all 2 resolved). 5/5 axes at A. All Run 23 fixes verified intact. No regressions.
|
| 9 |
9 |
|
|
| 10 |
10 |
|
## Scorecard
|
| 11 |
11 |
|
|
| 13 |
13 |
|
|-----------|:-----:|-------|
|
| 14 |
14 |
|
| Code Quality | A | Zero .unwrap() in production paths. Clean macro patterns throughout |
|
| 15 |
15 |
|
| Architecture | A | Clean layer separation. Trait-based backends for storage/email/payments |
|
| 16 |
|
- |
| Testing | A | 1,215 test annotations, proptest active, adversarial tests, comprehensive harness |
|
| 17 |
|
- |
| Security | A+ | SHA-256-based constant-time compare, fail-closed scanning, CSRF everywhere, Argon2id, HMAC webhooks, PKCE S256 |
|
| 18 |
|
- |
| Performance | A | Discover facet queries now parallelized via try_join!. Scheduler lock pinned. No remaining bottlenecks at launch scale |
|
| 19 |
|
- |
| Documentation | A- | Module-level //! on all major files. No README.md |
|
|
16 |
+ |
| Testing | A | ~1,215 test annotations, proptest active, adversarial tests, comprehensive harness |
|
|
17 |
+ |
| Security | A | Constant-time compare, fail-closed scanning, CSRF everywhere, Argon2id, HMAC webhooks, PKCE S256. DUMMY_HASH pattern on all login paths |
|
|
18 |
+ |
| Performance | A | Cart and download queries consolidated. Export writes to ZIP one-by-one. Advisory lock pinned |
|
|
19 |
+ |
| Documentation | A | Module-level //! on all major files. README.md present |
|
| 20 |
20 |
|
| Dependencies | A- | 4 transitive advisories (none exploitable). async-trait retained (required for dyn dispatch) |
|
| 21 |
|
- |
| Frontend | A | Askama auto-escape, json_escape prevents JSON-LD XSS, HTMX patterns consistent |
|
|
21 |
+ |
| Frontend | A | Askama auto-escape, json_escape prevents JSON-LD XSS, HTMX patterns consistent. CSRF field names consistent |
|
| 22 |
22 |
|
| Type Safety | A+ | 36 UUID newtypes, 25+ domain enums, validated string types, Cents/PriceCents monetary newtypes |
|
| 23 |
|
- |
| Observability | A | All route modules now instrumented including embed/ and payments/ (chronic items resolved) |
|
| 24 |
|
- |
| Concurrency | A | ON CONFLICT, FOR UPDATE, DashMap caches. Scheduler advisory lock now pinned to connection |
|
|
23 |
+ |
| Observability | A | All route modules instrumented including embed/ and payments/ |
|
|
24 |
+ |
| Concurrency | A | ON CONFLICT, FOR UPDATE, DashMap caches. Scheduler advisory lock pinned. Promo code atomically reserved at checkout |
|
| 25 |
25 |
|
| Resilience | A | Graceful shutdown with 10s deadline, timeouts on all outbound calls, fail-closed scanning |
|
| 26 |
26 |
|
| API Consistency | A | ListResponse wrapper, json_error_layer, versioned SyncKit routes |
|
| 27 |
|
- |
| Migration Safety | A | 105 additive migrations, IF NOT EXISTS guards, TIMESTAMPTZ throughout |
|
| 28 |
|
- |
| Codebase Size | A- | ~88K LOC. Oversized files remain (exports.rs, health.rs, tabs/user.rs) |
|
|
27 |
+ |
| Migration Safety | A | 107 additive migrations, IF NOT EXISTS guards, TIMESTAMPTZ throughout |
|
|
28 |
+ |
| Codebase Size | A | All three oversized files split into modules. health/mod.rs at 749 (monolithic probe fn) |
|
| 29 |
29 |
|
|
| 30 |
30 |
|
## Module Heatmap
|
| 31 |
31 |
|
|
| 35 |
35 |
|
| config.rs | A | A | A | A | n/a | A | A | A- | A |
|
| 36 |
36 |
|
| error.rs | A | A | A | A | n/a | A | A | A | A |
|
| 37 |
37 |
|
| auth.rs | A | A | A- | A+ | A | A | A | A | A |
|
| 38 |
|
- |
| csrf.rs | A | A | A | A | A- | A | A | A- | A |
|
|
38 |
+ |
| csrf.rs | A+ | A | A | A+ | A- | A | A | A- | A |
|
| 39 |
39 |
|
| constants.rs | A+ | n/a | A | A | n/a | A- | A | n/a | A |
|
| 40 |
40 |
|
| helpers.rs | A | A | A | A | A | A | A- | B+ | A |
|
| 41 |
41 |
|
| rate_limit.rs | A | A | A- | A | A | A | A | A | A |
|
| 45 |
45 |
|
| formatting.rs | A | A | A+ | A | A | A- | A | n/a | A |
|
| 46 |
46 |
|
| rss.rs | A | A | A | A | A | A | A- | n/a | A |
|
| 47 |
47 |
|
| synckit_auth.rs | A | A | A | A | A | A | A | A | A |
|
|
48 |
+ |
| metrics.rs | A | A | n/a | A | A- | A | A | A | A |
|
| 48 |
49 |
|
| db/enums.rs | A | A | A | A | n/a | A | A+ | n/a | A |
|
| 49 |
50 |
|
| db/id_types.rs | A | A | A | n/a | n/a | A | A+ | n/a | A |
|
| 50 |
51 |
|
| db/validated_types.rs | A | A | A+ | A | A | A | A+ | n/a | A |
|
| 54 |
55 |
|
| db/transactions.rs | A | A | B+ | A | A | A | A | A | A |
|
| 55 |
56 |
|
| db/discover.rs | A | A | B | A | A | A | A | n/a | A- |
|
| 56 |
57 |
|
| db/cart.rs | A | A | B | A | A | A | A | n/a | A |
|
| 57 |
|
- |
| db/creator_tiers.rs | A | A | A | A | A | A | A | n/a | A |
|
|
58 |
+ |
| db/creator_tiers.rs | A | A+ | A | A | A | A | A | n/a | A |
|
| 58 |
59 |
|
| db/versions.rs | A | A | A | A | A | A | A | n/a | A |
|
| 59 |
60 |
|
| db/builds.rs | A | A+ | A | A | A | A | A | n/a | A |
|
| 60 |
61 |
|
| db/pending_refunds.rs | A | A | B | A | A | A | A | n/a | A |
|
| 62 |
63 |
|
| db/promo_codes.rs | A | A | A | A | A | A | A | n/a | A |
|
| 63 |
64 |
|
| db/tips.rs | A | A | B | A | A | A | A | n/a | A |
|
| 64 |
65 |
|
| db/idempotency.rs | A | A | B- | A | A | A | A | n/a | A |
|
|
66 |
+ |
| db/pending_uploads.rs | B+ | A | n/a | A | A | A | A | n/a | A |
|
| 65 |
67 |
|
| db/models/* | A | A | B+ | A | n/a | A- | A | n/a | A |
|
| 66 |
68 |
|
| types/ | A | A | B | A | n/a | A | A | n/a | A |
|
| 67 |
69 |
|
| scanning/ | A | A+ | A- | A+ | A- | A | A- | A | A |
|
|
70 |
+ |
| scanning/archive.rs | A | A | A- | A- | A- | A | A- | A | A |
|
| 68 |
71 |
|
| payments/ | A | A | A- | A | A- | A | A | A | B+ |
|
| 69 |
72 |
|
| email/ | A | A | A | A | A- | A | A | B+ | A- |
|
| 70 |
|
- |
| scheduler/ | A | A | B+ | A | A | A | A | A- | A |
|
|
73 |
+ |
| scheduler/ | A | A+ | B+ | A | A | A | A | A- | A |
|
| 71 |
74 |
|
| scheduler/cleanup.rs | A | A | n/a | A | A | A | A | A- | A |
|
| 72 |
75 |
|
| validation/ | A | A | A+ | A+ | A | B+ | A | n/a | A |
|
| 73 |
76 |
|
| import/ | A | A | A | A | B+ | A- | A | B+ | A |
|
| 74 |
77 |
|
| git/ | A | A | A | A+ | B+ | A- | A | B | A |
|
| 75 |
|
- |
| git_ssh.rs | A | A | A | A- | A | A- | A | B+ | A |
|
|
78 |
+ |
| git_ssh.rs | A | A | A | A | A | A- | A | B+ | A |
|
| 76 |
79 |
|
| build_runner.rs | A | A- | B+ | A+ | A- | A- | A | A | A |
|
| 77 |
|
- |
| monitor.rs | A | A | A- | A | A | A | A | A | A |
|
| 78 |
|
- |
| templates/ | A | A- | n/a | **B+** | A | A- | A | B+ | B+ |
|
|
80 |
+ |
| monitor.rs | A | A | A- | A | A | A | A | A+ | A |
|
|
81 |
+ |
| templates/ | A | A- | n/a | A | A | A- | A | B+ | B+ |
|
| 79 |
82 |
|
| routes/auth.rs | A | A | n/a | A+ | A | A | A | A | A |
|
| 80 |
83 |
|
| routes/oauth.rs | A | A | n/a | A- | A | A | A | A- | A- |
|
| 81 |
84 |
|
| routes/admin/ | A | A | n/a | A | A | A | A | A | A |
|
| 82 |
|
- |
| routes/api/ | A | A | n/a | A | A | A | A | A | **B+** |
|
| 83 |
|
- |
| routes/api/exports.rs | A- | A- | n/a | A | **B-** | A | A | A | **B** |
|
| 84 |
|
- |
| routes/stripe/ | A | A | n/a | A | A- | A | A | A | **B+** |
|
|
85 |
+ |
| routes/api/ | A | A | n/a | A | A | A | A | A | B+ |
|
|
86 |
+ |
| routes/api/guest_checkout.rs | **B** | A | n/a | **B** | A | A | A | A | A |
|
|
87 |
+ |
| routes/api/cart.rs | A | A | n/a | A | A- | A | A | A | A |
|
|
88 |
+ |
| routes/api/exports/ | A- | A- | n/a | A | A- | A | A | A | A- |
|
|
89 |
+ |
| routes/stripe/ | A | A | n/a | A | A- | A | A | A | B+ |
|
| 85 |
90 |
|
| routes/stripe/checkout/ | A | A | n/a | A- | A | A | A | A | A |
|
|
91 |
+ |
| routes/stripe/checkout/cart.rs | A | A | n/a | A- | A | A | A | A | A |
|
| 86 |
92 |
|
| routes/synckit/ | A | A | n/a | A | A- | A | A | A | A |
|
| 87 |
93 |
|
| routes/pages/discover.rs | A | A | n/a | A | A | B+ | A | A | A |
|
| 88 |
|
- |
| routes/pages/ (other) | A | A | n/a | A | A | A | A | A- | **B+** |
|
|
94 |
+ |
| routes/pages/ (other) | A | A | n/a | A | A | A | A | A- | B+ |
|
| 89 |
95 |
|
| routes/embed/ | A | A | n/a | A | A | A- | A | A | A |
|
| 90 |
96 |
|
| routes/git/ | A | A | n/a | A | A | A | A | A | A |
|
| 91 |
97 |
|
| routes/storage/ | A | A | n/a | A | A | A | A | A | A |
|
|
98 |
+ |
| routes/storage/versions.rs | **B-** | A- | n/a | A | A | A | A | A | A |
|
| 92 |
99 |
|
| routes/storage/uploads.rs | A | A | n/a | A | A | A | A | A | A |
|
|
100 |
+ |
| routes/storage/downloads.rs | A | A | n/a | A | A- | A | A | A | A |
|
| 93 |
101 |
|
| routes/storage/images.rs | A | A | n/a | A | A | A | A | A | A |
|
| 94 |
102 |
|
| routes/postmark/ | A | A | n/a | A | A | A | n/a | A- | B+ |
|
| 95 |
103 |
|
|
| 97 |
105 |
|
|
| 98 |
106 |
|
### Cold Spots
|
| 99 |
107 |
|
|
| 100 |
|
- |
1. **templates/ security (B+):** Three templates (`fan_plus.html`, `project.html`, `sandbox.html`) use `name="csrf_token"` but CSRF middleware extracts `_csrf`. Forms with these fields will fail CSRF validation with 403.
|
| 101 |
|
- |
2. **routes/api/exports.rs performance (B-):** Content export downloads all S3 files into RAM before zipping. 2 GB cap exists but entire payload is buffered in memory. At concurrent exports, memory exhaustion is possible.
|
| 102 |
|
- |
3. **routes/api/exports.rs size (B):** 842 LOC, above 500-line guideline. Chronic from Run 21.
|
|
108 |
+ |
None. All Run 24 cold spots resolved in remediation.
|
| 103 |
109 |
|
|
| 104 |
|
- |
### Resolved Cold Spots (from Run 21)
|
|
110 |
+ |
### Resolved Cold Spots (from Run 24)
|
| 105 |
111 |
|
|
| 106 |
|
- |
- ~~routes/pages/discover.rs performance (B) + architecture (B-)~~ -- Facet queries now parallelized with `tokio::try_join!`.
|
| 107 |
|
- |
- ~~scheduler/ concurrency (B+)~~ -- Advisory lock now pinned to acquired connection.
|
| 108 |
|
- |
- ~~db/cart.rs correctness (B+)~~ -- PWYW minimum now enforced in `effective_price_cents()`.
|
| 109 |
|
- |
- ~~routes/embed/ observability (B)~~ -- All embed handlers now instrumented.
|
| 110 |
|
- |
- ~~payments/ observability (B+)~~ -- All payment functions now instrumented.
|
|
112 |
+ |
- ~~routes/api/guest_checkout.rs (B)~~ -- Added `starts_at` promo code validation. Matches all authenticated checkout paths.
|
|
113 |
+ |
- ~~routes/storage/versions.rs (B-)~~ -- Now uses atomic `try_replace_storage` for file replacements. No separate decrement/increment.
|
|
114 |
+ |
|
|
115 |
+ |
### Resolved Cold Spots (from Run 23)
|
|
116 |
+ |
|
|
117 |
+ |
- ~~versions.rs transaction safety (B)~~ -- Old S3 key now enqueued for deletion via `pending_s3_deletions`.
|
|
118 |
+ |
- ~~cart.rs performance (B-)~~ -- Consolidated into single-query `toggle_cart_preflight()`.
|
|
119 |
+ |
- ~~downloads.rs performance (B-)~~ -- Consolidated into single-query `check_item_access()`.
|
|
120 |
+ |
- ~~exports.rs performance (B-) + size (B)~~ -- Files written directly to ZIP one-by-one. Module split into `exports/mod.rs` + `exports/content.rs`.
|
|
121 |
+ |
- ~~archive.rs security (B+)~~ -- Decompression fallback uses 10x conservative multiplier.
|
|
122 |
+ |
- ~~checkout/cart.rs correctness (B+)~~ -- Was false positive; atomic `try_increment_use_count` already in place at checkout creation.
|
| 111 |
123 |
|
|
| 112 |
124 |
|
## Mandatory Surprises
|
| 113 |
125 |
|
|
| 114 |
|
- |
**Run 22 (5 surprises, one per axis):**
|
|
126 |
+ |
**Run 24 (5 surprises, one per axis):**
|
| 115 |
127 |
|
|
| 116 |
|
- |
1. **Payments -- Webhook signature verification (unexpectedly good):** `payments/webhooks.rs:186-231` implements constant-time HMAC verification with configurable timestamp tolerance, distinct error messages per failure mode, and comprehensive edge-case tests (stale timestamps, future timestamps, wrong secret). Gold standard implementation.
|
|
128 |
+ |
1. **Payments -- Claim token architecture for guest purchases (unexpectedly good):** `db/transactions.rs:74-173` creates unattached transactions with unique `claim_token`, sends secure download link, claims idempotently via `ON CONFLICT DO NOTHING`. Cryptographically sound, race-free, elegant.
|
| 117 |
129 |
|
|
| 118 |
|
- |
2. **Storage -- Cleanup scheduler resource lifecycle (unexpectedly good):** `scheduler/cleanup.rs` enqueues S3 deletions as durable records BEFORE any destructive work (fail-closed), retries with exponential backoff and attempt counting, decouples DB cleanup from S3 cleanup with transaction boundaries. The `pending_s3_deletions` queue survives crashes. Production-grade pattern.
|
|
130 |
+ |
2. **Storage -- recalculate_all_storage_batch LATERAL join (unexpectedly good):** `db/creator_tiers.rs:458-508` recalculates storage for ALL creators in a single query using LATERAL join instead of N+1 loop. Shows architectural awareness.
|
| 119 |
131 |
|
|
| 120 |
|
- |
3. **UX Wiring -- Form error recovery (unexpectedly good):** Login and form error patterns preserve user input via browser/HTMX state without re-rendering from server, avoiding the security footgun of echoing user input. Error messages are plain text, not reflected user input. Small UX detail that most platforms get wrong.
|
|
132 |
+ |
3. **UX Wiring -- CSRF constant-time token comparison (unexpectedly good):** `csrf.rs:61` uses `constant_time_compare()` for CSRF token validation. Most web frameworks skip this detail. Combined with dual-layer extraction (header + body), this is production-hardened CSRF.
|
| 121 |
133 |
|
|
| 122 |
|
- |
4. **Security -- Defense-in-depth layering (unexpectedly good):** Email tokens use HMAC-SHA256 with constant-time comparison + expiry bounds. Password reset URLs bind to password_hash (changing password invalidates links). Login tokens use double-hash (token in email, hash in DB). Passkey counter prevents cloning. TOTP replay prevention via `last_used_step`. Six independent layers, each independently correct.
|
|
134 |
+ |
4. **Security -- TOTP replay prevention via explicit time step matching (unexpectedly good):** `routes/api/totp.rs:254-266` stores matched time step in DB, rejects monotonically non-increasing steps. Completely eliminates TOTP replay across skew window. Most platforms get this wrong.
|
| 123 |
135 |
|
|
| 124 |
|
- |
5. **Performance -- Export buffering (unexpectedly bad):** `routes/api/exports.rs:656-680` downloads all S3 content files into `Vec<u8>` in RAM before building ZIP. With a 2 GB per-export cap, two concurrent exports could spike heap to 4+ GB. The rest of the codebase carefully uses presigned URLs for client-direct downloads, making this anomaly stand out.
|
|
136 |
+ |
5. **Performance -- Storage quota atomicity with idempotency checks (unexpectedly good):** `routes/storage/uploads.rs:206-219` checks idempotency before touching quotas, uses atomic `try_replace_storage` for file replacements. Sophisticated and rarely seen.
|
| 125 |
137 |
|
|
| 126 |
138 |
|
### Previous Surprises
|
| 127 |
139 |
|
|
| 128 |
|
- |
**Run 21:** Pending refund bidirectional matching, claim_pending_build FOR UPDATE SKIP LOCKED, json_escape, SHA-256-based constant_time_compare, scheduler advisory lock unpinned (bad, now fixed).
|
|
140 |
+ |
**Run 23:** Webhook signature gold standard, atomic storage quota enforcement, CSRF dual-layer extraction, archive decompression fallback (bad, fixed), advisory lock protocol.
|
| 129 |
141 |
|
|
| 130 |
|
- |
**Run 20:** Scanning module -- 6-layer anti-malware with actual decompression, VMProtect/UPX detection, ZIP bomb byte counting.
|
|
142 |
+ |
**Run 22:** Webhook signature gold standard, cleanup scheduler durable queue, form error recovery, defense-in-depth 6 layers, export RAM buffering.
|
|
143 |
+ |
|
|
144 |
+ |
**Run 21:** Pending refund bidirectional matching, claim_pending_build FOR UPDATE SKIP LOCKED, json_escape, SHA-256 constant_time_compare, scheduler advisory lock unpinned (bad, fixed).
|
|
145 |
+ |
|
|
146 |
+ |
**Run 20:** Scanning module -- 6-layer anti-malware with decompression, VMProtect/UPX detection, ZIP bomb byte counting.
|
| 131 |
147 |
|
|
| 132 |
148 |
|
**Run 19:** Hand-rolled Stripe v2 webhook signature verification with replay protection.
|
| 133 |
149 |
|
|
| 134 |
|
- |
**Run 18:** Sandbox tier mismatch bug (SmallFiles vs small_files). Fixed.
|
| 135 |
|
- |
|
| 136 |
|
- |
**Run 17:** TOCTOU-safe slug generation with retry loop + advisory lock pattern for sandbox IP cap.
|
| 137 |
|
- |
|
| 138 |
|
- |
**Run 15:** Session touch cache -- DashMap with 30s TTL avoids N+1 session queries.
|
| 139 |
|
- |
|
| 140 |
150 |
|
## Strengths
|
| 141 |
151 |
|
|
| 142 |
152 |
|
### 1. Security-in-depth
|
| 143 |
|
- |
Zero SQL injection vectors across 200+ queries. Argon2id (46MiB/2 iterations), SHA-256-based constant-time comparison (length-independent), CSRF synchronizer tokens, session fixation prevention, account lockout with anti-enumeration dummy hashes, PKCE S256 required, rate limiting on all endpoint classes, HMAC-signed URLs, 6-layer malware scanning with fail-closed, ZIP bomb detection, path traversal prevention, shell command validation, TOTP replay prevention via `last_used_step`, passkey counter updates preventing cloning attacks.
|
|
153 |
+ |
Zero SQL injection vectors across 200+ queries. Argon2id (46MiB/2 iterations), SHA-256-based constant-time comparison, CSRF synchronizer tokens (constant-time validated), session fixation prevention, account lockout with anti-enumeration dummy hashes on ALL login paths (web, SyncKit, OAuth), PKCE S256 required, rate limiting on all endpoint classes, HMAC-signed URLs, 6-layer malware scanning with fail-closed, ZIP bomb detection with 10x conservative multiplier, path traversal prevention, shell command validation, TOTP replay prevention via time step tracking, passkey counter updates.
|
| 144 |
154 |
|
|
| 145 |
155 |
|
### 2. Type safety discipline
|
| 146 |
|
- |
36 UUID newtypes via `define_pg_uuid_id!`, 25+ domain enums via `impl_str_enum!`, validated string types (Username, Slug, KeyCode), Cents/PriceCents monetary newtypes with proptest coverage. Compile-time template verification via Askama. All money math in integer cents (i32/i64), zero floating point in money paths. `SUM(BIGINT)::BIGINT` cast used consistently across all aggregate queries. License keys use CSPRNG (5 random words from 2048-word list, ~55 bits entropy).
|
|
156 |
+ |
36 UUID newtypes via `define_pg_uuid_id!`, 25+ domain enums via `impl_str_enum!`, validated string types, Cents/PriceCents monetary newtypes with proptest coverage. All money math in integer cents (i32/i64), zero floating point in money paths. `SUM(BIGINT)::BIGINT` cast used consistently. License keys use CSPRNG (~55 bits entropy). FOR UPDATE row locking on license activation prevents TOCTOU.
|
| 147 |
157 |
|
|
| 148 |
158 |
|
### 3. Payment robustness
|
| 149 |
|
- |
Three-layer webhook idempotency (event dedup table, status-based WHERE clauses, ON CONFLICT). Bidirectional pending refund matching. Atomic promo code reservation with cleanup on abandonment. `FOR UPDATE` row locking on tier deletion, license activation, pending refund claims. Self-purchase blocked across all checkout paths. Cart PWYW minimum now enforced. Tip amounts capped ($1-$10,000).
|
|
159 |
+ |
Three-layer webhook idempotency. Bidirectional pending refund matching. Atomic promo code enforcement at DB level. `FOR UPDATE` row locking on tier deletion, license activation, pending refund claims. Self-purchase blocked across all paths. Cart PWYW minimum enforced. Tip amounts capped. Guest purchase claim tokens are cryptographically sound and idempotent.
|
| 150 |
160 |
|
|
| 151 |
|
- |
### 4. Operational maturity (NEW)
|
| 152 |
|
- |
All Run 21 action items fixed (12/12) in one day. Chronic observability gaps in embed/ and payments/ resolved after two runs. Scheduler advisory lock pinned. Soft-delete purge now handles version S3 keys and storage decrement. Confirm uploads wrapped in transactions. Discover page parallelized. Pending S3 deletions durable queue deployed (migration 105).
|
|
161 |
+ |
### 4. Operational maturity
|
|
162 |
+ |
All Run 22-23 fixes verified intact. Idempotency key scope widened (migration 106). Pending S3 deletions UNIQUE constraint (migration 107). Scheduler advisory lock pinned. Soft-delete purge handles version S3 keys. Confirm uploads wrapped in transactions. Discover page parallelized. Content exports stream one-by-one. N+1 queries consolidated.
|
| 153 |
163 |
|
|
| 154 |
164 |
|
## Weaknesses
|
| 155 |
165 |
|
|
| 156 |
|
- |
### 1. Export memory buffering (NEW)
|
| 157 |
|
- |
Content export downloads all S3 files into RAM (up to 2 GB) before zipping. First real memory exhaustion vector at concurrent usage. Should stream ZIP directly or export to S3 with presigned download URL.
|
|
166 |
+ |
### Active (Run 24)
|
| 158 |
167 |
|
|
| 159 |
|
- |
### 2. CSRF token field name mismatch (NEW)
|
| 160 |
|
- |
Three templates use `csrf_token` field name but middleware extracts `_csrf`. Affected forms: fan_plus.html, project.html tip form, sandbox.html. These POST forms will fail with 403 if submitted without HTMX (which injects the token via header instead).
|
|
168 |
+ |
- **Guest checkout promo code validation gap** -- Missing `starts_at` check allows future-dated codes to be used early by guest buyers. Easy fix (2 lines).
|
|
169 |
+ |
- **Version replace storage counter corruption** -- Decrement-then-increment is not atomic. If increment fails, old storage is permanently lost from counter. Should use `try_replace_storage` or re-increment on failure.
|
| 161 |
170 |
|
|
| 162 |
|
- |
### 3. Idempotency key scope too narrow (NEW)
|
| 163 |
|
- |
Unique constraint is `(key, user_id)` but should be `(key, user_id, method, path)`. Same key reused across different endpoints returns cached response from the first endpoint. Low probability in practice (clients generate per-request keys) but schema should match intent.
|
|
171 |
+ |
### Resolved (Run 23)
|
| 164 |
172 |
|
|
| 165 |
|
- |
### 4. async-trait retained (RESOLVED -- not removable)
|
| 166 |
|
- |
3 trait definitions use `async-trait` for dyn-compatible async dispatch. Rust 2024 native async fn in traits is not dyn-compatible. Closing chronic item.
|
|
173 |
+ |
- ~~Version S3 key leak~~ -- Fixed. Old keys now enqueued via `pending_s3_deletions`.
|
|
174 |
+ |
- ~~Archive decompression fallback~~ -- Fixed. 10x conservative multiplier on decompression error.
|
|
175 |
+ |
- ~~Export memory buffering~~ -- Fixed. Files written to ZIP one-by-one.
|
|
176 |
+ |
- ~~N+1 query patterns~~ -- Fixed. Cart and download access control consolidated.
|
| 167 |
177 |
|
|
| 168 |
178 |
|
## Bug Reports by Axis
|
| 169 |
179 |
|
|
| 170 |
180 |
|
### Payments
|
| 171 |
|
- |
0 SERIOUS, 0 MINOR, 3 NOTE
|
|
181 |
+ |
0 CRITICAL, 1 SERIOUS, 0 MINOR, 2 NOTE
|
| 172 |
182 |
|
|
| 173 |
183 |
|
| # | Sev | Location | Description |
|
| 174 |
184 |
|
|---|-----|----------|-------------|
|
| 175 |
|
- |
| P1 | NOTE | `pricing.rs:134-148` | FixedPricing has no upper cap on amount. Not exploitable -- fixed-price path uses `item.price_cents`, not user input. |
|
| 176 |
|
- |
| P2 | NOTE | `pricing.rs:113-115` | FixedPricing `is_free()` hardcoded false. Unreachable -- `for_item()` routes price=0 to FreePricing. |
|
| 177 |
|
- |
| P3 | NOTE | `db/subscriptions.rs:399-407` | `has_active_subscription_to_project` doesn't check `cancel_at_period_end`. By design (access until period end). |
|
| 178 |
|
- |
|
| 179 |
|
- |
Previous SERIOUS findings (P1 cart PWYW bypass from Run 21) -- **FIXED**.
|
|
185 |
+ |
| P1 | **SERIOUS** | `routes/api/guest_checkout.rs:91-140` | Missing `starts_at` promo code validation. All 3 authenticated paths (cart.rs:150, item.rs:127, subscriptions.rs:192) check it. |
|
|
186 |
+ |
| P2 | NOTE | `db/license_keys.rs:158-233` | Activation limit enforced with FOR UPDATE + active count + idempotent upsert. Excellent. |
|
|
187 |
+ |
| P3 | NOTE | `payments/webhooks.rs:48-61` | Webhook deduplication correctly prevents double-credit. Solid. |
|
| 180 |
188 |
|
|
| 181 |
189 |
|
### Storage
|
| 182 |
|
- |
0 SERIOUS, 1 MINOR, 1 NOTE
|
|
190 |
+ |
0 CRITICAL, 1 SERIOUS, 1 MINOR, 1 NOTE
|
| 183 |
191 |
|
|
| 184 |
192 |
|
| # | Sev | Location | Description |
|
| 185 |
193 |
|
|---|-----|----------|-------------|
|
| 186 |
|
- |
| S1 | MINOR | `db/pending_s3_deletions.rs` | No UNIQUE constraint on `(s3_key, bucket)` in pending_s3_deletions table. `ON CONFLICT DO NOTHING` is idempotent but schema should be explicit. |
|
| 187 |
|
- |
| S2 | NOTE | `storage.rs:446-465` | `extract_s3_key_from_url` includes bucket name for path-style URLs. Only used for project images which handle it. |
|
| 188 |
|
- |
|
| 189 |
|
- |
Previous SERIOUS findings (S1 soft-delete purge, S2 non-atomic confirm from Run 21) -- **FIXED**.
|
|
194 |
+ |
| S1 | **SERIOUS** | `routes/storage/versions.rs:175-184` | Storage counter corruption: decrement at 175, try_increment at 182. If increment fails, decrement is not rolled back. |
|
|
195 |
+ |
| S2 | MINOR | `db/pending_uploads.rs:17` | ON CONFLICT DO UPDATE refreshes created_at, letting repeated presign calls prevent garbage collection. |
|
|
196 |
+ |
| S3 | NOTE | `routes/storage/uploads.rs:262` | Storage decrement rollback error swallowed with `.ok()`. Acceptable given background reconciliation. |
|
| 190 |
197 |
|
|
| 191 |
198 |
|
### UX Wiring
|
| 192 |
|
- |
1 SERIOUS, 0 MINOR, 3 NOTE
|
|
199 |
+ |
0 SERIOUS, 1 MINOR, 3 NOTE
|
| 193 |
200 |
|
|
| 194 |
201 |
|
| # | Sev | Location | Description |
|
| 195 |
202 |
|
|---|-----|----------|-------------|
|
| 196 |
|
- |
| U1 | **SERIOUS** | `templates/pages/fan_plus.html:97`, `templates/pages/project.html:209`, `templates/pages/sandbox.html:24` | CSRF field name mismatch: templates use `name="csrf_token"` but `csrf.rs:81` extracts `_csrf`. Forms submitted without HTMX JS will fail with 403. HTMX path works (header injection). |
|
| 197 |
|
- |
| U2 | NOTE | `templates/pages/login.html:13-16` | Login form lacks CSRF. Intentional (pre-auth exempt). |
|
| 198 |
|
- |
| U3 | NOTE | `templates/public.rs:471-482` | BuyPageTemplate lacks csrf_token field entirely. Intentional (guest checkout). |
|
| 199 |
|
- |
| U4 | NOTE | `formatting.rs:4-13` | `format_price` uses f64 division. Correct for all practical prices with `{:.2}`. |
|
|
203 |
+ |
| U1 | MINOR | `routes/pages/dashboard/forms.rs` | Blog editor doesn't preserve user input on slug validation error. |
|
|
204 |
+ |
| U2 | NOTE | `routes/pages/public/discover.rs:85` | Page clamped to `.max(1)` but no upper bound. DB returns empty; no impact. |
|
|
205 |
+ |
| U3 | NOTE | `templates/wizards/wizard_join.html` | Join wizard CSRF exemption justified (pre-auth) and documented. |
|
|
206 |
+ |
| U4 | NOTE | `templates/dashboards/dashboard-blog-editor.html:42` | hx-vals with UUID template variable. Safe (UUIDs contain no special chars). |
|
| 200 |
207 |
|
|
| 201 |
208 |
|
### Security
|
| 202 |
|
- |
0 SERIOUS, 1 MINOR, 2 NOTE
|
|
209 |
+ |
0 CRITICAL, 0 SERIOUS, 0 MINOR, 3 NOTE
|
| 203 |
210 |
|
|
| 204 |
211 |
|
| # | Sev | Location | Description |
|
| 205 |
212 |
|
|---|-----|----------|-------------|
|
| 206 |
|
- |
| X1 | MINOR | `db/idempotency.rs:50-52` | Unique constraint on `(key, user_id)` but queries filter by `(key, user_id, method, path)`. Same key on different endpoints returns wrong cached response. |
|
| 207 |
|
- |
| X2 | NOTE | `scanning/mod.rs:279` | Pipeline integration test only exercises 3 of 7 FileType variants. |
|
| 208 |
|
- |
| X3 | NOTE | `scanning/hash_lookup.rs:84` | MalwareBazaar `no_results` treated as Error (held for review). Deliberate fail-closed. |
|
|
213 |
+ |
| X1 | NOTE | `auth.rs:302-309` | verify_password is not independently constant-time on parse failure, but all callers use DUMMY_HASH. No exploitable timing channel. |
|
|
214 |
+ |
| X2 | NOTE | `db/totp.rs:126-146` | Backup code DB lookup not constant-time. Standard practice; not exploitable over network. |
|
|
215 |
+ |
| X3 | NOTE | `synckit_auth.rs:54-68` | JWT iat claim not validated. Standard; exp is validated. No impact. |
|
| 209 |
216 |
|
|
| 210 |
217 |
|
### Performance
|
| 211 |
|
- |
0 SERIOUS, 2 MINOR, 2 NOTE
|
|
218 |
+ |
0 CRITICAL, 0 SERIOUS, 2 MINOR, 1 NOTE
|
| 212 |
219 |
|
|
| 213 |
220 |
|
| # | Sev | Location | Description |
|
| 214 |
221 |
|
|---|-----|----------|-------------|
|
| 215 |
|
- |
| F1 | MINOR | `routes/api/exports.rs:656-680` | Content export downloads all S3 files into RAM before zipping. 2 GB cap but full heap buffering. Two concurrent exports = 4 GB spike. |
|
| 216 |
|
- |
| F2 | MINOR | `metrics.rs:206` | Idempotency middleware buffers full response body (up to 1MB). |
|
| 217 |
|
- |
| F3 | NOTE | `routes/synckit/subscribe.rs:123` | SSE broadcast channel size 16. Lag handled by filtering. By design. |
|
| 218 |
|
- |
| F4 | NOTE | `routes/synckit/sync.rs:98-100` | Push/pull fetch all devices for validation. Bounded at 50 per app. |
|
|
222 |
+ |
| F1 | MINOR | `metrics.rs:232` | Idempotency middleware: `.to_vec()` creates second allocation from Bytes. Minor efficiency loss. |
|
|
223 |
+ |
| F2 | MINOR | `metrics.rs:228` | Response body >1MB returns Body::empty(). Only affects chunked responses without Content-Length (rare). |
|
|
224 |
+ |
| F3 | NOTE | `routes/storage/uploads.rs:250-253` | Dynamic SQL column names via format!(). Hardcoded enum match arms -- not injection risk, but bypasses compile-time checking. |
|
| 219 |
225 |
|
|
| 220 |
226 |
|
## Cross-Cutting Concerns
|
| 221 |
227 |
|
|
| 222 |
|
- |
### CSRF field name inconsistency (UX + Security)
|
| 223 |
|
- |
Three templates use `csrf_token` instead of `_csrf`. While HTMX header injection means this doesn't block normal usage, it represents a defense-in-depth gap. If JS fails to load, these forms become non-functional. Single fix: standardize field names.
|
|
228 |
+ |
### Promo code validation inconsistency (Payments + UX)
|
|
229 |
+ |
The `starts_at` check is present in 4 checkout paths but missing from guest checkout. Pattern gap -- guest checkout was likely added later without copying all validations. A shared `validate_promo_code()` helper would prevent future divergence.
|
| 224 |
230 |
|
|
| 225 |
|
- |
### Export memory pattern (Performance + Storage)
|
| 226 |
|
- |
Export buffering is the sole remaining pattern where large data is loaded entirely into RAM. The rest of the codebase consistently uses streaming (presigned URLs, chunked S3 uploads, scan pipeline). Fixing this would complete the streaming pattern across the entire codebase.
|
|
231 |
+ |
### Storage counter atomicity (Storage + Performance)
|
|
232 |
+ |
The decrement-then-increment pattern in `versions.rs` is inconsistent with the atomic `try_replace_storage` used in `uploads.rs`. The versions path should use the same atomic pattern.
|
| 227 |
233 |
|
|
| 228 |
234 |
|
## Components Successfully Stress-Tested
|
| 229 |
235 |
|
|
| 230 |
236 |
|
### Payments (12 vectors survived)
|
| 231 |
|
- |
Webhook replay, double-credit, concurrent promo exhaustion, cross-user data access, self-purchase, negative/overflow amounts, floating-point money math, out-of-order webhooks, suspended creator purchases, SUM(BIGINT) pitfall, PWYW cart minimum enforcement, tip amount cap enforcement.
|
|
237 |
+ |
Webhook replay, double-credit, concurrent promo exhaustion (atomic at DB level), cross-user data access, self-purchase, negative/overflow amounts, floating-point money math, out-of-order webhooks, suspended creator purchases, SUM(BIGINT) pitfall, PWYW cart minimum enforcement, tip amount cap enforcement.
|
| 232 |
238 |
|
|
| 233 |
239 |
|
### Storage (10 vectors survived)
|
| 234 |
|
- |
Cross-user file overwrites, path traversal in filenames, content type smuggling, storage quota bypass, double-spend on idempotent confirm, malware file serving, orphaned upload cleanup, SUM(BIGINT) pitfall, transactional confirm uploads, soft-delete purge with version S3 keys.
|
|
240 |
+ |
Cross-user file overwrites, path traversal in filenames, content type smuggling, storage quota bypass (atomic enforcement), double-spend on idempotent confirm, malware file serving, orphaned upload cleanup, SUM(BIGINT) pitfall, transactional confirm uploads, soft-delete purge with version S3 keys.
|
| 235 |
241 |
|
|
| 236 |
242 |
|
### UX Wiring (10 vectors survived)
|
| 237 |
243 |
|
XSS via template injection, open redirect, user enumeration, markdown/HTML injection, pagination abuse, integer overflow in pricing, internal detail leakage, CSV injection, Unicode boundary attacks, JSON-LD breakout.
|
| 238 |
244 |
|
|
| 239 |
245 |
|
### Security (17 vectors survived)
|
| 240 |
|
- |
Virus scan bypass via ClamAV downtime, ZIP bomb, content-type spoofing, path traversal in archives, session fixation, timing-based user enumeration, brute force login, X-Forwarded-For spoofing, session reuse after password change, OAuth code replay, PKCE downgrade, token prediction, CSRF on state-changing endpoints, SSH command injection, passkey cloning, TOTP replay, IDOR on passkeys/sessions.
|
|
246 |
+ |
Virus scan bypass via ClamAV downtime (fail-closed), content-type spoofing, path traversal in archives, session fixation, timing-based user enumeration (dummy hash on all 3 login paths), brute force login (lockout), X-Forwarded-For spoofing (Cloudflare-aware), session reuse after password change, OAuth code replay, PKCE downgrade, token prediction (CSPRNG), CSRF on state-changing endpoints (constant-time), SSH command injection, passkey cloning (counter), TOTP replay (last_used_step), IDOR on passkeys/sessions, archive bombs (byte counting + 10x multiplier).
|
| 241 |
247 |
|
|
| 242 |
248 |
|
### Performance (10 vectors survived)
|
| 243 |
|
- |
Connection pool exhaustion, file scanning memory, SSE connection accumulation, scheduler job accumulation, background task leaks, ZIP bombs, path traversal, shell injection, lock ordering, discover page concurrent load.
|
|
249 |
+ |
Connection pool exhaustion (bounded at 25), file scanning memory (semaphore of 4), SSE connection accumulation (bounded), scheduler job accumulation (advisory lock), background task leaks (monitored), ZIP bombs (byte counting), bulk operations (batch queries, 100-item cap), rate limiter bypass (Cloudflare-aware IP), concurrent scan memory pressure, large file handling (streamed).
|
| 244 |
250 |
|
|
| 245 |
251 |
|
## Confidence Assessment
|
| 246 |
252 |
|
|
| 247 |
253 |
|
| Axis | Confidence | Notes |
|
| 248 |
254 |
|
|------|-----------|-------|
|
| 249 |
|
- |
| Payments | HIGH | Three-layer idempotency, integer money math, bidirectional refund matching. Cart PWYW now enforced. Tip cap enforced. CSPRNG license keys. |
|
| 250 |
|
- |
| Storage | HIGH | Presigned URL security solid. Confirms now transactional. Soft-delete purge handles version keys. Pending S3 deletions durable queue deployed. |
|
| 251 |
|
- |
| UX Wiring | HIGH | Askama auto-escape, json_escape defense-in-depth, comprehensive CSRF (one field name bug). No detail leakage. |
|
| 252 |
|
- |
| Security | HIGH | No CRITICAL or SERIOUS findings. Argon2id, constant-time everywhere, fail-closed scanning, PKCE S256. Idempotency key scope is low-risk. |
|
| 253 |
|
- |
| Performance | HIGH (current scale) | Discover parallelized. Scheduler lock pinned. Export buffering is sole remaining concern, low-frequency endpoint. |
|
|
255 |
+ |
| Payments | HIGH | One validation gap in guest checkout. Core payment logic excellent. |
|
|
256 |
+ |
| Storage | HIGH | One error-path storage corruption. Normal path is atomic and correct. |
|
|
257 |
+ |
| UX Wiring | HIGH | CSRF excellent. Templates safe. Minor UX polish only. |
|
|
258 |
+ |
| Security | HIGH | No auth bypasses. DUMMY_HASH on all login paths. Fail-closed scanning. |
|
|
259 |
+ |
| Performance | HIGH (current scale) | Pool size reasonable. Rate limiters well-tuned. Scheduler advisory-locked. |
|
| 254 |
260 |
|
|
| 255 |
261 |
|
## Metrics
|
| 256 |
262 |
|
|
| 257 |
263 |
|
- Modules audited: 55+
|
| 258 |
|
- |
- Total cold spots: 3
|
| 259 |
|
- |
- Bugs by severity: 0 critical, 1 serious, 4 minor, 10 note
|
|
264 |
+ |
- Total cold spots: 0 (all 2 resolved in remediation)
|
|
265 |
+ |
- Bugs by severity: 0 critical, 2 serious (all fixed), 3 minor (all fixed, 1 false positive), 10 note
|
| 260 |
266 |
|
- Axes at A or above: 5/5
|
| 261 |
267 |
|
|
| 262 |
268 |
|
## Axis Summary Grades
|
| 263 |
269 |
|
|
| 264 |
270 |
|
| Axis | Overall | Cold Spots | Mandatory Surprise |
|
| 265 |
271 |
|
|------|---------|------------|-------------------|
|
| 266 |
|
- |
| Payments | A | None | Webhook signature verification gold standard |
|
| 267 |
|
- |
| Storage | A | None | Cleanup scheduler durable S3 deletion queue |
|
| 268 |
|
- |
| UX Wiring | A- | templates/ CSRF field name (B+) | Form error recovery preserves input without reflecting |
|
| 269 |
|
- |
| Security | A | None | Defense-in-depth: 6 independent auth layers, each correct |
|
| 270 |
|
- |
| Performance | A- | exports.rs perf (B-), size (B) | Export RAM buffering anomaly vs streaming everywhere else |
|
|
272 |
+ |
| Payments | A | guest_checkout.rs (B) | Claim token architecture (good) |
|
|
273 |
+ |
| Storage | A | versions.rs (B-) | LATERAL join batch storage recalc (good) |
|
|
274 |
+ |
| UX Wiring | A | None | CSRF constant-time comparison (good) |
|
|
275 |
+ |
| Security | A | None | TOTP replay prevention via time step (good) |
|
|
276 |
+ |
| Performance | A | None | Storage quota atomicity + idempotency (good) |
|
| 271 |
277 |
|
|
| 272 |
278 |
|
## Recommended Priority Order
|
| 273 |
279 |
|
|
| 274 |
|
- |
1. **[SERIOUS] Fix CSRF field name mismatch** (`templates/pages/fan_plus.html:97`, `project.html:209`, `sandbox.html:24`) -- Change `name="csrf_token"` to `name="_csrf"` in all three templates. 5-minute fix, restores defense-in-depth for non-JS form submissions.
|
| 275 |
|
- |
2. **[MINOR] Fix idempotency key unique constraint** (`db/idempotency.rs`) -- Add migration to change unique constraint to `(key, user_id, method, path)`. Prevents wrong cached response on key reuse across endpoints.
|
| 276 |
|
- |
3. **[MINOR] Stream content exports** (`routes/api/exports.rs:656-680`) -- Replace in-memory buffering with streaming ZIP writer or export-to-S3-then-presign. Eliminates last RAM exhaustion vector.
|
| 277 |
|
- |
4. **[MINOR] Add UNIQUE on pending_s3_deletions** -- `CREATE UNIQUE INDEX idx_pending_s3_deletions_key_bucket ON pending_s3_deletions(s3_key, bucket)`. Schema hygiene.
|
| 278 |
|
- |
5. **[DEFERRED] Split oversized route files** -- exports.rs (842), health.rs (846), tabs/user.rs (815). Chronic from Run 21.
|
| 279 |
|
- |
6. **[DEFERRED] Add README.md to server/** -- Chronic from Run 19.
|
|
280 |
+ |
1. **[SERIOUS]** Add `starts_at` validation to guest checkout -- 2 lines, copy from `cart.rs:150`
|
|
281 |
+ |
2. **[SERIOUS]** Fix version replace storage rollback -- re-increment old storage on `try_increment` failure, or use `try_replace_storage`
|
|
282 |
+ |
3. **[MINOR]** Change pending_uploads to `ON CONFLICT DO NOTHING` -- prevents staleness attack
|
|
283 |
+ |
4. **[MINOR]** Blog editor: preserve input on validation error
|
|
284 |
+ |
5. **[MINOR]** Idempotency middleware: use `std::str::from_utf8` instead of `String::from_utf8(to_vec())`
|
|
285 |
+ |
6. **[DEFERRED]** Extract shared `validate_promo_code()` helper
|
| 280 |
286 |
|
|
| 281 |
287 |
|
## Action Items
|
| 282 |
288 |
|
|
| 283 |
|
- |
### Run 22 (2026-05-09)
|
|
289 |
+ |
### Run 24 (2026-05-09)
|
| 284 |
290 |
|
|
| 285 |
|
- |
82. **[SERIOUS]** Fix CSRF field name: change `csrf_token` to `_csrf` in `fan_plus.html`, `project.html`, `sandbox.html`
|
| 286 |
|
- |
83. **[MINOR]** Add migration: unique constraint on `idempotency_keys(key, user_id, method, path)` replacing `(key, user_id)`
|
| 287 |
|
- |
84. **[MINOR]** Stream content exports or export to S3 with presigned download (`routes/api/exports.rs:656-680`)
|
| 288 |
|
- |
85. **[MINOR]** Add `UNIQUE(s3_key, bucket)` to `pending_s3_deletions` table (migration)
|
| 289 |
|
- |
86. **[DEFERRED]** Split oversized route files: health.rs (846), exports.rs (842), tabs/user.rs (815) (chronic, from Run 21 #81)
|
| 290 |
|
- |
87. **[DEFERRED]** Add README.md to server/ (chronic, from Run 19 #53 -> #63 -> #80)
|
|
291 |
+ |
97. ~~**[SERIOUS]** Add `starts_at` validation to guest checkout~~ -- **Fixed.** Added `starts_at` check to `guest_checkout.rs:102-107`, matching all authenticated paths.
|
|
292 |
+ |
98. ~~**[SERIOUS]** Fix version replace storage counter rollback~~ -- **Fixed.** `versions.rs` now uses atomic `try_replace_storage` for replacements, `try_increment_storage` for fresh uploads. No separate decrement/increment.
|
|
293 |
+ |
99. ~~**[MINOR]** Change pending_uploads ON CONFLICT to DO NOTHING~~ -- **Fixed.** `pending_uploads.rs:17` now uses `DO NOTHING`, preventing staleness attack via repeated presign.
|
|
294 |
+ |
100. ~~**[MINOR]** Blog editor: preserve form input on validation error~~ -- **False positive.** Blog editor uses JS `fetch()` API; form state is preserved on error.
|
|
295 |
+ |
101. ~~**[MINOR]** Idempotency middleware: avoid double allocation~~ -- **Fixed.** `metrics.rs:232` now uses `std::str::from_utf8` with `to_owned()` only for the spawned task.
|
|
296 |
+ |
102. **[DEFERRED]** Extract shared `validate_promo_code()` helper to prevent checkout path divergence.
|
|
297 |
+ |
|
|
298 |
+ |
### Run 23 (2026-05-09) -- All Fixed
|
|
299 |
+ |
|
|
300 |
+ |
88. ~~**[SERIOUS]** Enqueue old S3 key on version file replacement~~ -- **Fixed.**
|
|
301 |
+ |
89. ~~**[SERIOUS]** Harden archive decompression fallback~~ -- **Fixed.**
|
|
302 |
+ |
90. ~~**[SERIOUS]** Reserve promo code slot atomically at checkout creation~~ -- **Already implemented** (false positive).
|
|
303 |
+ |
91. ~~**[MINOR]** Consolidate cart toggle into single JOIN query~~ -- **Fixed.**
|
|
304 |
+ |
92. ~~**[MINOR]** Consolidate download access control into single query~~ -- **Fixed.**
|
|
305 |
+ |
93. ~~**[MINOR]** Stream content exports~~ -- **Fixed.**
|
|
306 |
+ |
94. ~~**[MINOR]** Tighten CSV email validation~~ -- **Fixed.**
|
|
307 |
+ |
95. ~~**[DEFERRED]** Split oversized route files~~ -- **Fixed.**
|
|
308 |
+ |
96. ~~**[DEFERRED]** Add README.md to server/~~ -- **Fixed.**
|
| 291 |
309 |
|
|
| 292 |
310 |
|
### Open (blocked on upstream)
|
| 293 |
311 |
|
|
| 296 |
314 |
|
25. Monitor aws-sdk-s3 for rustls-webpki 0.101.7 fix (RUSTSEC-2026-0049)
|
| 297 |
315 |
|
33. bincode unmaintained (RUSTSEC-2025-0141) -- upstream via syntect/yara-x, warning only
|
| 298 |
316 |
|
|
| 299 |
|
- |
## Previous Action Item Verification (Run 21)
|
|
317 |
+ |
## Previous Action Item Verification (Run 23)
|
| 300 |
318 |
|
|
| 301 |
319 |
|
| # | Item | Status |
|
| 302 |
320 |
|
|---|------|--------|
|
| 303 |
|
- |
| 65 | Pin scheduler advisory lock to acquired connection | **Fixed** |
|
| 304 |
|
- |
| 66 | Enforce PWYW minimum in cart effective_price_cents() | **Fixed** |
|
| 305 |
|
- |
| 67 | Fix soft-delete purge: version S3 keys + storage decrement | **Fixed** |
|
| 306 |
|
- |
| 68 | Wrap confirm_upload DB writes in transaction | **Fixed** |
|
| 307 |
|
- |
| 69 | Optimize discover page: parallelize facet queries | **Fixed** |
|
| 308 |
|
- |
| 70 | Delete old S3 objects on image/audio/video replacement | **Fixed** |
|
| 309 |
|
- |
| 71 | Validate checkout cancel item_id as UUID | **Fixed** |
|
| 310 |
|
- |
| 72 | ClamAV contains("FOUND") -> ends_with("FOUND") | **Fixed** |
|
| 311 |
|
- |
| 73 | Case-normalize URL-encoded path traversal check | **Fixed** |
|
| 312 |
|
- |
| 74 | Add UNIQUE(s3_key) to pending_uploads | **Fixed** |
|
| 313 |
|
- |
| 75 | Add #[instrument] to embed/ handlers | **Fixed** |
|
| 314 |
|
- |
| 76 | Add #[instrument] to payments/ functions | **Fixed** |
|
| 315 |
|
- |
| 77 | Initialize PWYW amount_cents hidden field server-side | Not verified (low priority) |
|
| 316 |
|
- |
| 78 | Use AuthUser for OAuth authorize | Not verified (low priority) |
|
| 317 |
|
- |
| 80 | Add README.md to server/ | **Unfixed** (chronic, carried as #87) |
|
| 318 |
|
- |
| 81 | Split oversized route files | **Unfixed** (carried as #86) |
|
|
321 |
+ |
| 88 | Enqueue old S3 key on version file replacement | **Fixed** (verified: versions.rs:203-204 enqueues old key) |
|
|
322 |
+ |
| 89 | Harden archive decompression fallback | **Fixed** (verified: archive.rs:123 uses 10x multiplier) |
|
|
323 |
+ |
| 90 | Reserve promo code slot atomically | **Already implemented** (false positive) |
|
|
324 |
+ |
| 91 | Consolidate cart toggle | **Fixed** |
|
|
325 |
+ |
| 92 | Consolidate download access control | **Fixed** |
|
|
326 |
+ |
| 93 | Stream content exports | **Fixed** |
|
|
327 |
+ |
| 94 | Tighten CSV email validation | **Fixed** |
|
|
328 |
+ |
| 95 | Split oversized route files | **Fixed** |
|
|
329 |
+ |
| 96 | Add README.md | **Fixed** |
|
| 319 |
330 |
|
|
| 320 |
|
- |
12 of 14 actionable Run 21 items fixed. 2 deferred items carried forward. No regressions.
|
|
331 |
+ |
9 of 9 Run 23 items verified fixed.
|
| 321 |
332 |
|
|
| 322 |
333 |
|
### Chronic Items (unfixed across 3+ consecutive runs)
|
| 323 |
334 |
|
|
| 324 |
|
- |
| Item | First flagged | Runs unfixed |
|
| 325 |
|
- |
|------|--------------|-------------|
|
| 326 |
|
- |
| Add README.md to server/ | Run 19 | 4 (19, 20, 21, 22) |
|
|
335 |
+ |
None. Both previous chronic items (README.md, oversized route files) resolved in Run 23.
|
| 327 |
336 |
|
|
| 328 |
|
- |
Note: embed/ and payments/ observability gaps resolved in Run 22 after 2 runs (20, 21). async-trait closed as not removable.
|
|
337 |
+ |
### False Positives Identified (Run 24)
|
| 329 |
338 |
|
|
| 330 |
|
- |
## Delta Since Run 21
|
|
339 |
+ |
The following agent findings were investigated and determined not to be real bugs:
|
|
340 |
+ |
- **verify_password timing attack** -- Mitigated by DUMMY_HASH in all callers (routes/auth.rs:111, routes/synckit/auth.rs:59+69, routes/oauth.rs:294). Hash always comes from DB or dummy.
|
|
341 |
+ |
- **Backup code SQL WHERE timing** -- Standard database practice. ~1ms query time variance is indistinguishable from network jitter over TCP.
|
|
342 |
+ |
- **SQL injection via format!** -- Column names are hardcoded string literals from exhaustive enum match. No user input.
|
|
343 |
+ |
- **Multi-seller Stripe isolation** -- Requires API key compromise. Cart scoped to single seller at application level.
|
|
344 |
+ |
- **JWT iat validation** -- Standard JWT behavior. exp is validated. No security impact.
|
|
345 |
+ |
- **Password length not centralized** -- Present at all entry points (routes/auth.rs, routes/synckit/auth.rs:58, routes/oauth.rs). Argon2id has internal limits.
|
| 331 |
346 |
|
|
| 332 |
|
- |
### Fixed
|
| 333 |
|
- |
- All 5 SERIOUS Run 21 findings fixed (scheduler lock, cart PWYW, soft-delete purge, confirm transaction, discover queries)
|
| 334 |
|
- |
- All 7 MINOR Run 21 findings fixed (old S3 cleanup, cancel UUID, ClamAV FOUND, path traversal case, pending_uploads UNIQUE, embed instrumentation, payments instrumentation)
|
| 335 |
|
- |
- embed/ and payments/ now have full `#[instrument]` coverage -- resolves chronic observability gap
|
| 336 |
|
- |
- 4 new migrations (102-105): pending_uploads unique, unique_plays, nullable_item_id_indexes, pending_s3_deletions
|
| 337 |
|
- |
- Version bumped from 0.5.7 to 0.5.9
|
| 338 |
|
- |
- LOC grew from ~87,427 to ~87,853 (+426)
|
| 339 |
|
- |
- Test annotations from 1,218 to 1,215 (-3, likely test consolidation)
|
|
347 |
+ |
## Delta Since Run 23
|
| 340 |
348 |
|
|
| 341 |
|
- |
### New Findings (not in Run 21)
|
| 342 |
|
- |
- CSRF field name mismatch in 3 templates (SERIOUS)
|
| 343 |
|
- |
- Idempotency key unique constraint too narrow (MINOR)
|
| 344 |
|
- |
- Content export RAM buffering (MINOR)
|
| 345 |
|
- |
- Pending S3 deletions missing UNIQUE constraint (MINOR)
|
|
349 |
+ |
### Fixed (from Run 23)
|
|
350 |
+ |
All 9 Run 23 action items verified fixed. No regressions detected.
|
| 346 |
351 |
|
|
| 347 |
|
- |
### Grade Changes
|
| 348 |
|
- |
- Performance: A- -> A (discover page fixed)
|
| 349 |
|
- |
- Concurrency: A- -> A (scheduler lock fixed)
|
| 350 |
|
- |
- Observability: A- -> A (embed/ + payments/ instrumented)
|
| 351 |
|
- |
- Overall: A (held)
|
| 352 |
|
- |
- Cold spots: 5 -> 3 (net -2)
|
| 353 |
|
- |
- SERIOUS findings: 5 -> 1 (net -4)
|
|
352 |
+ |
### New Findings (not in Run 23)
|
|
353 |
+ |
- Guest checkout missing `starts_at` promo validation (SERIOUS) -- new
|
|
354 |
+ |
- Storage counter corruption on version replace failure (SERIOUS) -- new (distinct from Run 23's S3 key leak -- this is about counter rollback, not cleanup)
|
|
355 |
+ |
- Pending uploads staleness attack via ON CONFLICT (MINOR) -- new
|