diff --git a/whitepaper/outreach/wave7-mail-dns-storage-vpn-rtos-survey.md b/whitepaper/outreach/wave7-mail-dns-storage-vpn-rtos-survey.md new file mode 100644 index 000000000..2bf4b54f6 --- /dev/null +++ b/whitepaper/outreach/wave7-mail-dns-storage-vpn-rtos-survey.md @@ -0,0 +1,99 @@ +# Wave 7 — Mail, DNS, Storage, VPN, Audio, RTOS, Postgres ecosystem + +**Survey date:** 2026-04-25 +**Tool:** unmoad (9 active MOAD detectors, HIGH+ severity filter) +**Scope:** 29 projects across mail (maddy, neomutt), DNS (pdns, knot, nsd), VPN/routing (wireguard-go, libreswan, openbgpd-portable, gobgp), distributed storage (garage, longhorn-engine, openebs, glusterfs), time-series databases (timescaledb, questdb, m3), audio servers (pipewire, jack2, mpv), real-time OS (nuttx, RIOT), Go-on-microcontroller (tinygo), document/key-value stores (couchdb, surrealdb, KeyDB), and Postgres ecosystem tooling (patroni, pgbackrest, pgpool2, lxc). + +--- + +## Summary + +Wave 7 totals 4,116 HIGH+ findings across 29 projects. Five new clean-scan honor roll entries. **No flagship CWE-407 patches ship this pass** — the strong M1 candidates either resolve to bounded fixed config tables (compile-time small N), to data structures already optimal (HashSet, ObjHashSet, RoaringTreemap), or to query-shape constants too small to cross the wall-clock threshold. + +This wave behaves like the docs-pipeline survey: mature C/C++/Rust infrastructure with already-optimized hot paths. Honest "investigated, no patch this pass" outcome. + +## Clean-scan honor roll — 5 new entries + +| Project | Lang | Role | Notes | +|---------|------|------|-------| +| **wireguard-go** | Go | Userspace WireGuard implementation | Zero HIGH+ across all 9 detectors. Tight crypto codebase. | +| **longhorn-engine** | Go | Rancher Longhorn block storage engine | Zero HIGH+. Distributed-storage core, well-disciplined. | +| **tinygo** | Go | LLVM-based Go compiler for microcontrollers | 5 M4 findings, all `token`/`Msg` in compiler error strings — credential-keyword false positives. | +| **maddy** | Go | Modular mail server | 4 M4 findings, all error/log strings naming protocol fields ("api_token", "password is expired") — string-literal false positives. | +| **openbgpd-portable** | C | OpenBSD BGP daemon (portable) | 2 M6 findings, both TCP-MD5 signing for BGP per RFC 2385 — protocol-mandated, not a defect. | + +Honor roll now stands at **30 projects** validated zero-real-finding under MOAD scanning. + +## Per-target findings + +| Project | Lang | Total | M1 | M3 | M4 | M5 | M6 | M7 | M9 | M11 | Triage | +|---------|------|------:|---:|---:|---:|---:|---:|---:|---:|----:|--------| +| questdb | Java/Rust | 1840 | 344 | 962 | 35 | 142 | - | 357 | - | - | M1 hits in `qdbr/parquet_write/simd.rs` are bloom-filter `&[u64].contains` — the bloom array IS the data structure; replacing with HashSet would defeat SIMD layout. M3 cluster: `vec_docs.set` and similar — scanner FP on `.set(` literal. | +| surrealdb | Rust | 370 | 155 | - | 48 | 4 | - | 160 | - | 3 | HNSW `pending_docs.contains(doc_id)` — `pending_docs` is `RoaringTreemap` with O(1)-ish containment. NOT a defect. `computed_deps.rs:217` cycle-safety-net Vec.contains is real but only fires on cycle (caught at DEFINE time). | +| glusterfs | C | 320 | 294 | 12 | 13 | 1 | - | - | - | - | 90% strcmp-in-loop on fixed xlator/option tables. `extras/profiler/glusterfs-profiler` Python script hits — diagnostic tool, not hot path. | +| RIOT | C | 251 | 156 | 4 | 49 | - | 6 | 28 | 5 | 3 | Embedded RTOS — most M1 hits are static driver tables. | +| garage | Rust | 224 | 172 | - | 13 | 1 | 1 | 34 | - | 3 | `rpc_helper.rs:593` `nodes.contains(n)` inside replication-factor loop (typically ≤5) — bounded constant. `redoc.standalone.js` is vendored. | +| m3 | Go | 279 | 217 | 16 | 7 | 1 | - | 35 | - | 3 | `swagger-ui.js` vendored hits dominate. Native code mostly fixed-table strcmp. | +| mpv | C | 181 | 177 | - | - | - | - | 4 | - | - | Most M1 in `defaults.js` (player JS) are command-prefix scans on bounded option list. | +| nuttx | C | 169 | 145 | - | 13 | - | - | 7 | 1 | 3 | Asm `arch_strcmp.S` matched as linear-search-in-loop — these ARE the strcmp primitive. | +| pgpool2 | C | 151 | 94 | - | 44 | - | 13 | - | - | - | `pgindent` Perl tool grep-in-loop. M4 cluster: log strings with "password" literal in error templates. | +| pdns | C++ | 110 | 67 | 1 | 2 | 4 | 3 | 31 | - | 2 | `d3.v3.js` vendored. M7 cluster worth a deeper look (recursor flatland defects). | +| knot | C | 107 | 93 | - | 7 | - | 1 | 5 | - | 1 | DNS server — fixed RR-type tables drive most M1. | +| KeyDB | C | 96 | 84 | 1 | 7 | - | - | 2 | - | 2 | `redis-cli.c` argv parsing strcmp — fixed CLI option table. | +| timescaledb | C | 79 | 70 | - | 2 | - | - | 7 | - | - | PostgreSQL extension — `bgw_jobs.c` and similar mostly fixed config lookups. | +| neomutt | C | 76 | 29 | - | 43 | - | 4 | - | - | - | Mail client — M4 cluster is account/credential field NAMES in config-key strings. | +| patroni | Python | 69 | 3 | 49 | 7 | 2 | 3 | - | - | 5 | M3 cluster is `event.set()`/`self.set('key', val)` — scanner FP on literal `.set(`. | +| nsd | C | 61 | 26 | - | 35 | - | - | - | - | - | Authoritative DNS server — strcmp on RR/option tables. | +| couchdb | Erlang/JS | 59 | 27 | 3 | 7 | 2 | 16 | 4 | - | - | M6 weak-hash hits in legacy migration code. | +| lxc | C | 57 | 54 | - | 1 | - | - | 2 | - | - | Container manager — fixed cgroup/capability tables. | +| libreswan | C | 31 | 14 | 6 | 9 | - | - | - | 2 | - | IPsec daemon — fixed crypto algorithm tables. | +| pipewire | C | 30 | 21 | - | 3 | - | - | 6 | - | - | Audio server — fixed format/property tables. | +| jack2 | C/Python | 16 | 9 | - | - | - | 3 | - | - | 4 | `waflib/Tools/c_config.py` build-system Python — load-time. | +| pgbackrest | C | 15 | 15 | - | - | - | - | - | - | - | All 15 are strcmp on `parseRuleCommand`/`parseRuleOption` — fixed compile-time rule tables. | +| gobgp | Go | 11 | 10 | - | 1 | - | - | - | - | - | `slices.Contains(req.UUIDs, v)` worth investigation but UUID list bounded by API call shape. | +| openebs | Go | 9 | 4 | - | - | - | - | 5 | - | - | Container storage operator — small surface. | +| **wireguard-go** | Go | 0 | - | - | - | - | - | - | - | - | **clean** | +| **longhorn-engine** | Go | 0 | - | - | - | - | - | - | - | - | **clean** | +| **tinygo** | Go | 5 | - | - | 5 | - | - | - | - | - | **clean** (FPs only) | +| **maddy** | Go | 4 | - | - | 4 | - | - | - | - | - | **clean** (FPs only) | +| **openbgpd-portable** | C | 2 | - | - | - | - | 2 | - | - | - | **clean** (RFC-mandated TCP-MD5) | + +## Investigations that did not patch-ship + +### questdb `QueryModel.recordViews` — borderline real defect + +`io.questdb.griffin.model.QueryModel.recordViews()` (line 1521 + 1532) iterates view definitions and dedupes by `referencedViews.contains(viewDefinition)` where `referencedViews` is `ObjList` — a linear-scan list. Classic O(N²) dedup pattern. + +Per-query cost is bounded by the query's chain depth: for typical analytical queries touching ≤20 views, the constant is invisible. For deeply chained materialized-view queries (50+ views) the cost becomes 2500 ops per call, called twice per query. Real but borderline. + +**Not patch-shipped this pass.** The fix is mechanical (add a parallel `ObjHashSet` for membership check, keep ObjList for ordered iteration), but the wall-clock impact at realistic query shapes does not cross the CWE-407 bar. Logged here so future scanner runs surface it again. + +### surrealdb HNSW `pending_docs.contains` + +Fully investigated. `pending_docs` is `RoaringTreemap` (Rust roaring crate) — already O(log n) or better depending on density. NOT a CWE-407 defect; the scanner does not yet model RoaringTreemap as a containment-optimal type. + +### gobgp `slices.Contains(req.UUIDs, v)` + +`req.UUIDs` length is bounded by API request shape (typically 1-100 UUIDs per gRPC call). Per-iteration cost on the inner loop dominates only for synthetic requests far above realistic API usage. Logged as low-priority, not patch-shipped. + +### patroni M3 cluster (49 hits) + +All 49 hits are `event.set()` (threading.Event signal flag — no leak) or `self.set('key', value)` on DCS clients (etcd/raft/k8s clients). Scanner false-positive on literal `.set(` regex match. Worth tightening the M3 detector to require ContextVar import context. + +## Triage backlog + +1. **questdb M3 cluster (962 hits)** — large enough to deserve its own pass. Most likely scanner FP on `.set(` literal in setter methods, but worth a detector-level tightening before re-scanning. +2. **questdb M7 cluster (357 hits)** — Flatland defect surface in SIMD/parquet code. Need to model the SIMD layout before claiming complexity-class change. +3. **pdns M7 cluster (31 hits)** — recursor candidates worth a focused pass. +4. **patroni M3 detector tightening** — add ContextVar/`from contextvars import` context check before flagging `.set(` on identifiers. +5. **scanner enhancement** — Roaring/Bitmap/HashSet type awareness in M1 detector to suppress these false positives at source. + +## Method + +Same as Waves 3-6: shallow clone, `unmoad -s high -f json`, filter test/vendor/docs noise, manual triage of the strongest source-only candidates per project. Five projects added to the clean-scan honor roll. No new UNDF IDs assigned this wave (no patches shipped). + +## References + +- `unmoad` detection engine: `git.unturf.com/engineering/unmoad.com` +- Earlier surveys: `/test-harness-survey/` (Wave 3), `/wave4-linter-ci-survey/`, `/wave5-cicd-iac-survey/`, `/wave6-docgen-webfw-tui-survey/`, `/docs-pipeline-survey/` +- Clean-scan honor roll cumulative: 30 projects across waves 3-7