Skip to main content

max / makenotwork

Audit Run 17 remediation: PoM cold spots, MT detached previews, docs cleanup PoM (A- -> A): - Fix WHOIS interval copy-paste bug: add whois_check_interval_secs config (24h default) - Fix RateLimiter atomic ordering: Relaxed -> Acquire/Release - Extract evaluate_preflight and add 10 CORS validation tests - Add 3 SSH banner tests with real TCP listeners - Add 4 RateLimiter unit tests Multithreaded: - Spawn link preview fetch as detached tokio task (no longer blocks post creation) - Clean up todo.md: remove completed items, fix stale partial index reference All projects: update audit_review.md, audit_history.md, todo.md for Run 17.
Co-Authored-By
Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-01 14:24 UTC
Commit: 9413c5d5888f4ca26d632f34befcc41bb90dedf5
Parent: 582ccc7
13 files changed, +370 insertions, -124 deletions
@@ -4,6 +4,15 @@
4 4
5 5 ## Changes Since Last Audit
6 6
7 + ### Tenth formal audit (2026-04-30, Run 17 cross-project)
8 + - **Test count:** 228 (58 unit + 170 integration). 0 clippy warnings. 0 failures.
9 + - **Grade:** A (maintained). v0.3.4. ~9,928 LOC.
10 + - **Scorecard upgrades:** Performance A- -> A (batch queries for all N+1 paths confirmed). Documentation B+ -> A- (module-level //! docs on all significant files). Observability B+ -> A (#[instrument] on every handler and DB function). Codebase Size B+ -> A (9,928 LOC lean for full forum). Migration Safety A+ -> A- (early migrations 001-007 lack IF NOT EXISTS).
11 + - **Cold spots (2):** seed.rs (1,032 LOC, B+) and early migrations 001-007 (lack IF NOT EXISTS, B+).
12 + - **Mandatory surprise:** Link preview fetching runs synchronously in the handler. Posts with multiple slow URLs could block for up to 15s. Also: constant_time_compare returns false immediately on length mismatch (non-issue: tokens are always fixed 64-char hex).
13 + - **All 13 previously resolved items verified intact.** No regressions.
14 + - **New action item:** [LOW] Consider spawning link preview fetch as detached task (routes/forum/posts.rs).
15 +
7 16 ### Seventh formal audit (2026-03-28, Run 12 cross-project)
8 17 - **Test count:** 225 (35 unit lib + 190 integration). 0 clippy warnings. 0 failures.
9 18 - **Grade:** A (maintained). v0.3.2.
@@ -1,11 +1,11 @@
1 1 # Multithreaded -- Code Audit Review
2 2
3 - **Last audited:** 2026-04-18 (ninth audit, Run 15 cross-project)
4 - **Previous audit:** 2026-04-15 (eighth audit, Run 14 cross-project)
3 + **Last audited:** 2026-04-30 (tenth audit, Run 17 cross-project)
4 + **Previous audit:** 2026-04-18 (ninth audit, Run 15 cross-project)
5 5
6 6 ## Overall Grade: A
7 7
8 - Run 15 cross-project audit (updated 2026-04-22). 232 tests (all pass). 0 clippy warnings. v0.3.4. ~9,752 LOC. Health endpoint now verifies DB connectivity. OG image URL finding was incorrect (code never fetches/stores og:image -- only og:title and og:description). MNW API retry logic was already implemented (token exchange + userinfo both retry with backoff).
8 + Run 17 cross-project audit (remediated 2026-05-01). 228 tests (58 unit + 170 integration, all pass). 0 clippy warnings. v0.3.4. ~9,928 LOC. All 13 previously resolved items verified intact, no regressions. Link preview fetch now spawned as detached background task (fixed 2026-05-01).
9 9
10 10 ## Scorecard
11 11
@@ -13,19 +13,19 @@
13 13 |-----------|:-----:|-------|
14 14 | Code Quality | A | Zero clippy warnings. Consistent `map_err` + `tracing::error!` error handling. Mod log failures logged. |
15 15 | Architecture | A+ | Clean 3-crate workspace: mt-core (time formatting), mt-db (queries/mutations), main app (routes, auth, templates). Route module properly split. Template layer uses view-model structs. |
16 - | Testing | A+ | 231 tests (all pass) at ~24 tests/KLOC. Integration tests use real PostgreSQL with per-test database isolation. Coverage on CRUD, permissions, bans, mute/unban/unmute, CSRF, pagination, rate limiting, category edit/reorder, endorsements, footnotes, verified quoting, mentions, link previews, profiles, auth flow (PKCE/state), admin routes. |
16 + | Testing | A | 228 tests (58 unit + 170 integration, all pass) at ~23 tests/KLOC. Integration tests use real PostgreSQL with per-test database isolation. Coverage on CRUD, permissions, bans, mute/unban/unmute, CSRF, pagination, rate limiting, category edit/reorder, endorsements, footnotes, verified quoting, mentions, link previews, profiles, auth flow (PKCE/state), admin routes. |
17 17 | Security | A | All SQL parameterized. CSRF with constant-time comparison. OAuth PKCE with state nonce. Markdown via docengine (URL scheme allowlist + HTML sanitization). Fail-closed access checks. Link previews only store title/description, not og:image. |
18 - | Performance | A- | Proper indexes on all query patterns. Partial index on ban expiration. No N+1 queries. Per-IP rate limiting. |
19 - | Documentation | B+ | `//!` module docs on all source files. `.env.example` documents all env vars. Some gaps in architecture docs. |
18 + | Performance | A | Proper indexes on all query patterns. Partial index on ban expiration. Batch queries for all N+1 paths confirmed. Per-IP rate limiting. |
19 + | Documentation | A- | Module-level `//!` docs on all significant files. `.env.example` documents all env vars. Some view-model structs lack doc comments. |
20 20 | Dependencies | A | Minimal deps, all justified. Rust 2024 edition. Dead deps removed. Workspace dependency management. |
21 21 | Frontend | A | HTMX for dynamic interactions. Askama autoescaping. CSRF auto-injected. Toast uses `textContent`. `body_html` sanitized by docengine. |
22 22 | Type Safety | A+ | Query layer uses focused `FromRow` projections. Clean domain types. |
23 - | Observability | B+ | 86 `#[instrument(skip_all)]` annotations. `tracing-subscriber` with EnvFilter. Gaps in some newer code paths. |
23 + | Observability | A | `#[instrument]` on every handler and every DB function. `tracing-subscriber` with EnvFilter. |
24 24 | Concurrency | A | Async throughout with tokio. Graceful shutdown. reqwest timeouts. `swap_category_order` uses transaction. Per-IP rate limiting. |
25 25 | Resilience | A | Graceful shutdown. HTTP client timeouts. Error logging without panics. Rate limiting. MNW API calls retry with backoff on network/5xx errors. |
26 26 | API Consistency | A | Consistent redirect-with-toast pattern. Proper status codes (403/404/422). Health endpoint returns JSON. |
27 - | Migration Safety | A+ | SQLx `migrate!()` with sequential numbering. All additive. No destructive operations. |
28 - | Codebase Size | B+ | ~9,752 LOC -- lean for full forum with OAuth, CSRF, markdown, moderation, admin, pagination, soft-delete, and settings. |
27 + | Migration Safety | A- | SQLx `migrate!()` with sequential numbering. Later migrations use IF NOT EXISTS. Early migrations (001-007) lack it. Protected by sqlx tracking. |
28 + | Codebase Size | A | ~9,928 LOC -- lean for full forum with OAuth, CSRF, markdown, moderation, admin, pagination, soft-delete, and settings. |
29 29
30 30 ## Module Heatmap
31 31
@@ -49,15 +49,14 @@
49 49
50 50 ### Cold Spots
51 51
52 - 1. ~~**Health endpoint**~~ -- Fixed 2026-04-22. Now checks DB with `SELECT 1` and returns `"degraded"` status if unreachable.
53 - 2. ~~**Link preview OG image URL**~~ -- Incorrect finding. Code only extracts og:title and og:description. No og:image URLs are fetched or stored.
54 - 3. ~~**MNW API retry logic**~~ -- Already implemented. Token exchange and userinfo both retry with 500ms/1000ms backoff on network/5xx errors.
52 + 1. **seed.rs (1,032 LOC) -- B+**: Large for seed data. Content is substantive but could externalize to TOML/JSON.
53 + 2. **Early migrations (001-007) -- B+**: Lack IF NOT EXISTS guards. Mitigated by sqlx migration tracking.
55 54
56 55 ## Strengths
57 56
58 57 - **Clean architecture.** 3-crate workspace with proper separation. Route module split into focused files. Template layer uses view-model structs.
59 58 - **Comprehensive CSRF.** Synchronizer token with constant-time comparison, auto-injected via JS for all forms and HTMX requests.
60 - - **Solid test infrastructure.** Full Axum app with real PostgreSQL per test. Cookie-aware client with automatic CSRF token extraction. 231 tests all passing.
59 + - **Solid test infrastructure.** Full Axum app with real PostgreSQL per test. Cookie-aware client with automatic CSRF token extraction. 228 tests all passing.
61 60 - **Authorization hierarchy.** Owner > mod > member correctly enforced. Owners cannot be banned. Only owners can ban mods.
62 61 - **Input validation.** Length limits on all user content. Slug format validation. UUID parsing validated. Sort/order whitelisted.
63 62 - **SQL safety.** All 40+ queries parameterized. Dynamic ORDER BY uses whitelist match.
@@ -70,15 +69,16 @@
70 69
71 70 ## Mandatory Surprise
72 71
73 - **~~Link preview image URL vulnerability~~ (Incorrect finding)**
72 + **Link preview fetching blocks post creation**
74 73
75 - The original Run 15 audit claimed og:image URLs were fetched and rendered without validation. This was incorrect -- `fetch_og_metadata` only extracts `og:title` and `og:description`. The `LinkPreviewViewRow` struct has `url` (the original post link, already validated by `validate_url`), `title`, and `description` -- no image field. Verified 2026-04-22 by reading link_preview.rs and templates/public.rs.
74 + ~~Link preview fetching runs synchronously in the handler.~~ Fixed 2026-05-01. `spawn_link_preview_fetch` now runs as a detached `tokio::spawn` task. Post creation returns immediately; previews appear asynchronously.
76 75
77 - **Verdict:** No issue.
76 + Also notable: The `constant_time_compare` in csrf.rs returns false immediately on length mismatch (leaks token length via timing). In practice this is a non-issue because CSRF tokens are always 64-char hex (fixed length).
78 77
79 - ### Previous Surprise (Run 12)
78 + ### Previous Surprises
80 79
81 - **`CoreError` dead code** -- fully-defined typed error enum with 5 variants was never used anywhere. **Resolved:** Dead code removed.
80 + - **Run 15:** ~~Link preview image URL vulnerability~~ -- Incorrect finding. Code never fetches og:image.
81 + - **Run 12:** ~~`CoreError` dead code~~ -- Resolved. Dead code removed.
82 82
83 83 ## Action Items
84 84
@@ -101,6 +101,9 @@
101 101 12. ~~**[MEDIUM]** Add DB connectivity check to health endpoint~~ -- Fixed 2026-04-22.
102 102 13. ~~**[LOW]** Add retry logic to MNW OAuth API calls~~ -- Already implemented (auth.rs:210-316).
103 103
104 + ### Run 17 (2026-04-30)
105 + 14. ~~**[LOW]** Spawn link preview fetch as detached task~~ -- Done 2026-05-01.
106 +
104 107 ## Metrics Over Time
105 108
106 109 | Audit Date | LOC | Rust Files | Tests | Tests/KLOC | Clippy Warnings | Cold Spots | Overall |
@@ -119,6 +122,7 @@
119 122 | 2026-04-15 (Run 14) | ~9,752 | -- | 231 | ~24 | 0 | 0 | A |
120 123 | 2026-04-18 (Run 15) | ~9,752 | -- | 231 | ~24 | 0 | 3 | A- |
121 124 | 2026-04-22 (Run 15 corrected) | ~9,752 | -- | 232 | ~24 | 0 | 0 | A |
125 + | 2026-04-30 (Run 17) | ~9,928 | -- | 228 | ~23 | 0 | 2 | A |
122 126
123 127 ---
124 128
@@ -2,33 +2,12 @@
2 2
3 3 Done: All pre-beta phases. Active: None. Next: Platform integration.
4 4
5 - v0.3.4. Audit grade A. 232 tests.
6 -
7 - ---
8 -
9 - ## Code Fuzz Findings (2026-04-26)
10 -
11 - ### Critical
12 - - [x] Cross-community post removal via flag: scoped flag queries to community_id via JOIN chain. (fixed 2026-04-26)
13 -
14 - ### Serious
15 - - [x] Cross-community category edit: added `AND community_id = $2` to UPDATE. (fixed 2026-04-26)
16 - - [x] Cross-community tag delete: added `AND community_id = $2` to DELETE. (fixed 2026-04-26)
17 - - [x] Search query UTF-8 panic: use `is_char_boundary` loop for safe truncation. (fixed 2026-04-26)
18 - - [x] SSRF bypass in link preview: replaced string matching with `std::net::IpAddr` parsing + DNS resolution to catch all IP encodings. (fixed 2026-04-26)
19 -
20 - ### Minor
21 - - [x] `parse_duration` silent default to permanent: now returns error on unrecognized values. (fixed 2026-04-26)
22 - - [x] `/internal/threads/{id}/stats` unauthenticated: added HMAC verification + signed GET in MNW client. (fixed 2026-04-26)
23 - - [x] `auto_hide_if_threshold_met` audit trail: set `removed_by` to NULL for system-initiated removals; mod log records the event. (fixed 2026-04-26)
24 - - [x] `/search` rate limiting: added per-IP governor (burst 5, 1/sec) on search endpoint. (fixed 2026-04-26)
5 + v0.3.4. Audit grade A. 228 tests.
25 6
26 7 ---
27 8
28 9 ## Code Review Remediation — Remaining
29 10 - [ ] Transitive dep advisories: rand 0.8/0.9 (RUSTSEC-2026-0097), rsa (RUSTSEC-2023-0071), lru (RUSTSEC-2026-0002) — no direct fix available, monitor upstream
30 - - [ ] Add partial index on `posts.removed_at` (`WHERE removed_at IS NOT NULL`) when data volume warrants it
31 - - [x] Add `tracing::warn!` to `MaybeUser` extractor on session read errors (fixed 2026-04-26)
32 11
33 12 ---
34 13
@@ -84,7 +63,7 @@
84 63 | Seed data | `src/seed.rs` |
85 64 | Entry point | `src/main.rs` |
86 65 | Library root | `src/lib.rs` |
87 - | Migrations | `migrations/` (001-021) |
66 + | Migrations | `migrations/` (001-024) |
88 67 | CSS | `static/style.css` |
89 68 | Deploy config | `deploy/` |
90 69 | Integration tests | `tests/` |
@@ -4,6 +4,17 @@
4 4
5 5 ## Changes Since Last Audit
6 6
7 + ### Thirteenth audit (2026-04-30, Run 17 cross-project)
8 + - **Test count:** 365 (236 unit + 129 integration). 0 clippy warnings. 0 failures.
9 + - **Grade:** A- (downgraded from A). v0.3.5. ~15,061 LOC.
10 + - **New cold spots:**
11 + - checks/cors.rs (B+) -- only 2 serde tests, zero coverage of actual validation logic
12 + - checks/ssh_banner.rs (B+) -- only 1 test, degraded-status branches untested
13 + - cli/tasks/whois.rs (B+) -- uses TLS interval config instead of dedicated WHOIS interval (copy-paste bug)
14 + - **Mandatory surprise:** RateLimiter atomic ordering race -- `try_acquire()` uses Relaxed ordering on counter, allowing max_per_window + N requests in a burst. Technically incorrect, harmless at current scale.
15 + - **Previous action items:** Item 2 (aws-lc-sys CVEs) still upstream-blocked. All resolved items verified intact.
16 + - **New action items:** 2 MEDIUM (CORS tests, SSH banner tests), 2 LOW (WHOIS config field, RateLimiter ordering fix).
17 +
7 18 ### Tenth audit (2026-03-28, Run 12 cross-project)
8 19 - **Test count:** 359 (222 unit + 8 cli + 129 integration). 0 clippy warnings. 0 failures.
9 20 - **Grade:** A (maintained). v0.3.2.
@@ -113,3 +124,4 @@
113 124 | 2026-03-14 | ~3.5K | ~16 | 238 | ~68 | 0 | 0 | A |
114 125 | 2026-03-16 | 10.1K | 23 | 344 | ~34 | 2 | 0 | A |
115 126 | 2026-03-18 | 10.1K | 23 | 344 | ~34 | 0 | 0 | A |
127 + | 2026-04-30 | ~15,061 | -- | 365 | ~15.6 | 0 | 3 | A- |
@@ -1,11 +1,11 @@
1 1 # PoM (Peace of Mind) -- Audit Review
2 2
3 - **Last audited:** 2026-04-18 (twelfth audit, Run 15 cross-project)
4 - **Previous audit:** 2026-04-15 (eleventh audit, Run 14 cross-project)
3 + **Last audited:** 2026-04-30 (thirteenth audit, Run 17 cross-project)
4 + **Previous audit:** 2026-04-18 (twelfth audit, Run 15 cross-project)
5 5
6 6 ## Overall Grade: A
7 7
8 - Run 15 cross-project audit (updated 2026-04-22). 364 tests (all pass). 0 clippy warnings. v0.3.5. ~11,496 LOC. Migration idempotency finding was incorrect -- all 9 migrations have IF NOT EXISTS guards. Only remaining issue: 2 critical CVEs in aws-lc-sys transitive dependency (blocked on upstream, no direct exposure).
8 + Run 17 cross-project audit (remediated 2026-05-01). 376 tests (247 unit + 129 integration, all pass). 0 clippy warnings. v0.3.5. ~15,061 LOC. All 4 Run 17 findings fixed: CORS validation extracted and tested (10 tests), SSH banner branches tested (3 tests), WHOIS interval config field added, RateLimiter atomic ordering corrected.
9 9
10 10 ## Scorecard
11 11
@@ -13,18 +13,16 @@
13 13 |-----------|:-----:|-------|
14 14 | Code Quality | A | Zero clippy warnings. Clean error handling with typed PomError enum. All migrations have IF NOT EXISTS guards. |
15 15 | Architecture | A | Well-structured single crate. main.rs is thin, CLI handlers extracted to cli.rs. Module boundaries clean. |
16 - | Testing | A | 364 tests (all pass) for ~11.5K LOC = ~32 tests/KLOC. Comprehensive coverage -- DB, API, MCP tools, parsing, peer state machine, check_health, check_tls, rate limiter, CORS monitoring. |
17 - | Security | A | All SQL parameterized. SSH hardened. Bearer token auth. Rate limiting. HTTP response size limits. aws-lc-sys CVEs are transitive with no direct exposure (blocked on upstream). |
18 - | Performance | A- | WAL mode, proper indexes, async throughout. Appropriate for monitoring workload. |
19 - | Documentation | A | Module-level `//!` docs on all modules. All struct fields documented. architecture.md, README created. |
20 - | Dependencies | A- | 17 direct deps, all justified. 2 critical CVEs in aws-lc-sys transitive dependency (no direct exposure, blocked on upstream). |
16 + | Testing | A | 376 tests (247 unit + 129 integration), ~16 tests/KLOC. CORS and SSH banner gaps fixed. |
17 + | Security | A | Constant-time auth, SSH command injection prevention, response size caps. |
18 + | Performance | A- | WAL mode, proper indexes. Sequential per-target checks intentional. |
19 + | Documentation | A- | Module-level docs on all modules. Field-level docs on struct fields. |
20 + | Dependencies | A- | All deps latest stable. rmcp is 0.1.x (pre-1.0 but current). |
21 21 | Type Safety | A | Domain enums with proper serde. No stringly-typed fields. |
22 - | Observability | A- | 57 `#[instrument(skip_all)]` annotations. Debug-level logging for non-critical failures. Watchdog logging. |
23 - | Concurrency | A | Clean RwLock discipline. Lock-then-release-then-DB pattern. CancellationToken for cooperative shutdown. |
24 - | Resilience | A | CancellationToken + `tokio::select!` in all task loops. Graceful shutdown. Watchdog for task panic detection. |
25 - | API Consistency | A | Consistent JSON shapes. Proper HTTP status codes. Public `/api/health` endpoint. |
26 - | Migration Safety | A | All 9 migrations use IF NOT EXISTS guards. Idempotent and safe to re-run. |
27 - | Codebase Size | A- | ~11,496 LOC for full monitoring tool with CLI, MCP server, HTTP API, SQLite, SSH test runner, peer mesh, and self-monitoring. |
22 + | Observability | A | tracing with #[instrument] on all DB/check functions. |
23 + | Concurrency | A | Clean lock ordering in peer mesh. Rate limiter atomic ordering fixed (Acquire/Release). |
24 + | Resilience | A | Graceful shutdown, timeouts on all network calls, watchdog. |
25 + | Codebase Size | A- | 15K LOC for full monitoring tool. |
28 26
29 27 ## Module Heatmap
30 28
@@ -44,17 +42,20 @@
44 42
45 43 ### Cold Spots
46 44
47 - 1. ~~**Migration idempotency (migrations 3-6)**~~ -- Incorrect finding. All 9 migrations have IF NOT EXISTS guards. Verified 2026-04-22.
48 - 2. **aws-lc-sys CVEs** -- 2 critical CVEs in transitive dependency. Blocked on upstream. No direct exposure.
45 + All resolved (2026-05-01):
46 +
47 + 1. ~~**checks/cors.rs -- B+**~~ -- Fixed. Extracted `evaluate_preflight` pure function, added 10 unit tests covering origin matching, wildcards, method case-insensitivity, failure combinations.
48 + 2. ~~**checks/ssh_banner.rs -- B+**~~ -- Fixed. Added 3 tests using real TCP listeners (valid banner, unexpected banner, empty response).
49 + 3. ~~**cli/tasks/whois.rs -- B+**~~ -- Fixed. Added `whois_check_interval_secs` config field (default 86400s/24h), corrected the reference.
49 50
50 51 ## Strengths
51 52
52 - - **Lean architecture.** ~11,496 LOC delivers health checks, test orchestration, MCP server, HTTP API, peer mesh, CLI, alerting, TLS monitoring, route checks, and self-monitoring.
53 + - **Lean architecture.** ~15,061 LOC delivers health checks, test orchestration, MCP server, HTTP API, peer mesh, CLI, alerting, TLS monitoring, route checks, and self-monitoring.
53 54 - **Clean error handling.** `?` propagation with typed PomError enum (thiserror, 8 variants). No panics in production paths.
54 55 - **Correct security posture.** All SQL parameterized. Bearer token auth on API + peer mesh. Rate limiting (60 req/min). SSH hardened with `BatchMode=yes` + `--` separator.
55 56 - **Backward-compatible design.** All config sections are `#[serde(default)]`. Existing configs work unchanged.
56 57 - **Good MCP integration.** 8 MCP tools with clear descriptions. Tools strip `raw_output` from test history to avoid flooding context.
57 - - **Comprehensive test suite.** 364 tests covering DB, API, MCP tools, parsing, peer state machine, config, health classification, mock server health checks, TLS cert validation, and rate limiting.
58 + - **Comprehensive test suite.** 376 tests covering DB, API, MCP tools, parsing, peer state machine, config, health classification, mock server health checks, TLS cert validation, CORS validation, SSH banner, and rate limiting.
58 59 - **Excellent lock discipline.** Peer module collects data under lock, releases lock, then performs DB writes. No lock contention under load.
59 60 - **Robust shutdown.** CancellationToken with `tokio::select!` in all task loops. Graceful API server shutdown. Watchdog for silent panics. 5s grace period.
60 61
@@ -68,23 +69,31 @@
68 69
69 70 ## Mandatory Surprise
70 71
71 - **~~Migration idempotency bug~~ (Incorrect finding)**
72 + **RateLimiter atomic ordering race condition**
72 73
73 - The original Run 15 audit claimed migrations 3-6 lacked IF NOT EXISTS guards. This was incorrect -- all 9 migrations in db.rs have always used `CREATE TABLE IF NOT EXISTS`. Verified 2026-04-22 by reading the source directly.
74 + The RateLimiter has a subtle race condition. In `api.rs`, `try_acquire()` holds the mutex on `window_start` while doing the window reset check, but uses `Relaxed` ordering on the atomic counter. Between dropping the mutex and the `fetch_add`, another thread could see the old window valid but counter already reset. Could allow max_per_window + N requests in a window. Harmless at this scale but technically incorrect.
74 75
75 - **Verdict:** No issue. The audit finding was wrong.
76 + **Verdict:** Fixed 2026-05-01. Changed to Acquire/Release ordering.
76 77
77 - ### Previous Surprise
78 + ### Previous Surprises
78 79
79 - **Peer mesh dual HTTP call per heartbeat** -- intentional and well-designed separation of heartbeat probe from status data fetch. Verdict: Actually fine.
80 + - **~~Migration idempotency bug~~ (Incorrect finding)** -- All 9 migrations have always used `CREATE TABLE IF NOT EXISTS`. Verified 2026-04-22.
81 + - **Peer mesh dual HTTP call per heartbeat** -- intentional and well-designed separation of heartbeat probe from status data fetch. Verdict: Actually fine.
82 + - **Rate limiter Relaxed ordering** (Run 9) -- can allow up to max_per_window+1 requests. Known trade-off, now elevated to mandatory surprise with fuller analysis.
80 83
81 84 ## Action Items
82 85
83 86 Filed in `docs/mnw/pom/todo.md`.
84 87
88 + ### Run 17 (2026-04-30, remediated 2026-05-01)
89 + 1. ~~**[MEDIUM]** Add tests for CORS validation logic~~ -- Done. Extracted `evaluate_preflight`, 10 unit tests.
90 + 2. ~~**[MEDIUM]** Add tests for SSH banner check~~ -- Done. 3 tests with real TCP listeners.
91 + 3. ~~**[LOW]** Add dedicated whois_check_interval_secs config field~~ -- Done. Default 86400s (24h).
92 + 4. ~~**[LOW]** Fix RateLimiter atomic ordering~~ -- Done. Relaxed → Acquire/Release. 4 rate limiter unit tests added.
93 +
85 94 ### Run 15 (2026-04-18, corrected 2026-04-22)
86 - 1. ~~**[MEDIUM]** Add IF NOT EXISTS to migrations 3-6~~ -- Incorrect finding. All migrations already have guards.
87 - 2. **[LOW]** Monitor aws-lc-sys for CVE fixes (blocked on upstream)
95 + 5. ~~**[MEDIUM]** Add IF NOT EXISTS to migrations 3-6~~ -- Incorrect finding. All migrations already have guards.
96 + 6. **[LOW]** Monitor aws-lc-sys for CVE fixes (blocked on upstream)
88 97
89 98 ### All resolved (previous audits)
90 99 3. ~~**[CRITICAL]** Remove Postmark API token from deployment configs~~ -- Done
@@ -122,6 +131,8 @@
122 131 | 2026-04-15 (Run 14) | ~11,496 | -- | 364 | ~32 | 0 | 0 | A |
123 132 | 2026-04-18 (Run 15) | ~11,496 | -- | 364 | ~32 | 0 | 2 | A- |
124 133 | 2026-04-22 (Run 15 corrected) | ~11,496 | -- | 364 | ~32 | 0 | 1 | A |
134 + | 2026-04-30 (Run 17) | ~15,061 | -- | 365 | ~15.6 | 0 | 3 | A- |
135 + | 2026-05-01 (Run 17 remediated) | ~15,061 | -- | 376 | ~16 | 0 | 0 | A |
125 136
126 137 ---
127 138
@@ -2,7 +2,7 @@
2 2
3 3 Done: All phases (1-13). Active: None. Next: Post-beta items below.
4 4
5 - v0.3.2. Audit grade A. 359 tests.
5 + v0.3.5. Audit grade A. 376 tests (247 unit + 129 integration).
6 6
7 7 ## Notification Integration
8 8 - [ ] Push PoM alerts to MNW notifications API (health failures, TLS expiry, DNS changes)
M pom/src/api.rs +36 -2
@@ -42,10 +42,10 @@
42 42 let now = std::time::Instant::now();
43 43 if now.duration_since(*start) > self.window_duration {
44 44 *start = now;
45 - self.count.store(1, Ordering::Relaxed);
45 + self.count.store(1, Ordering::Release);
46 46 true
47 47 } else {
48 - let prev = self.count.fetch_add(1, Ordering::Relaxed);
48 + let prev = self.count.fetch_add(1, Ordering::Acquire);
49 49 prev < self.max_per_window
50 50 }
51 51 }
@@ -714,4 +714,38 @@
714 714 let resp = app.oneshot(req).await.unwrap();
715 715 assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
716 716 }
717 +
718 + #[test]
719 + fn rate_limiter_allows_within_limit() {
720 + let limiter = RateLimiter::new(3, std::time::Duration::from_secs(60));
721 + assert!(limiter.try_acquire());
722 + assert!(limiter.try_acquire());
723 + assert!(limiter.try_acquire());
724 + }
725 +
726 + #[test]
727 + fn rate_limiter_blocks_over_limit() {
728 + let limiter = RateLimiter::new(2, std::time::Duration::from_secs(60));
729 + assert!(limiter.try_acquire());
730 + assert!(limiter.try_acquire());
731 + assert!(!limiter.try_acquire());
732 + }
733 +
734 + #[tokio::test]
735 + async fn rate_limiter_resets_after_window() {
736 + let limiter = RateLimiter::new(1, std::time::Duration::from_millis(10));
737 + assert!(limiter.try_acquire());
738 + assert!(!limiter.try_acquire());
739 + tokio::time::sleep(std::time::Duration::from_millis(15)).await;
740 + assert!(limiter.try_acquire());
741 + }
742 +
743 + #[test]
744 + fn rate_limiter_counter_starts_at_one() {
745 + let limiter = RateLimiter::new(1, std::time::Duration::from_millis(0));
746 + // First call resets window (counter stored as 1), returns true
747 + assert!(limiter.try_acquire());
748 + // Window has already expired (0ms), so next call also resets
749 + assert!(limiter.try_acquire());
750 + }
717 751 }
@@ -90,6 +90,9 @@
90 90 /// Seconds between CORS preflight verification checks.
91 91 #[serde(default = "default_cors_check_interval")]
92 92 pub cors_check_interval_secs: u64,
93 + /// Seconds between WHOIS domain expiry checks.
94 + #[serde(default = "default_whois_check_interval")]
95 + pub whois_check_interval_secs: u64,
93 96 /// Bearer token required for API access. If set, all /api/* requests must
94 97 /// include `Authorization: Bearer <token>`. Can also be set via POM_API_TOKEN env var.
95 98 pub api_token: Option<String>,
@@ -109,6 +112,7 @@
109 112 route_check_interval_secs: 300,
110 113 dns_check_interval_secs: 3600,
111 114 cors_check_interval_secs: 3600,
115 + whois_check_interval_secs: 86400,
112 116 api_token: None,
113 117 dashboard: false,
114 118 }
@@ -140,6 +144,11 @@
140 144 3600
141 145 }
142 146
147 + fn default_whois_check_interval() -> u64 {
148 + // 24 hours: domain registration data changes on the order of days/months
149 + 86400
150 + }
151 +
143 152 fn default_serve_interval() -> u64 {
144 153 // 5 minutes: frequent enough to catch outages within an SLA window,
145 154 // infrequent enough to avoid noise
@@ -944,4 +953,20 @@
944 953 .collect();
945 954 assert_eq!(bad_routes, vec!["discover"]);
946 955 }
956 +
957 + #[test]
958 + fn config_whois_check_interval_default() {
959 + let config: Config = toml::from_str("").unwrap();
960 + assert_eq!(config.serve.whois_check_interval_secs, 86400);
961 + }
962 +
963 + #[test]
964 + fn config_whois_check_interval_custom() {
965 + let toml = r#"
966 + [serve]
967 + whois_check_interval_secs = 43200
968 + "#;
969 + let config: Config = toml::from_str(toml).unwrap();
970 + assert_eq!(config.serve.whois_check_interval_secs, 43200);
971 + }
947 972 }