Files

7.5 KiB
Raw Permalink Blame History

Bad-Pattern Audit — full-tree snapshot

Generated by scripts/audit-bad-patterns.sh against every .rs file under src/. The pattern matrix is the one defined in docs/BAD_PATTERNS.md (sections AP, 131 regexes).

Scope Files Patterns scanned Patterns with hits Total hit lines Strict-section hits (A/B/C/L/M/N/O)
My 100 authored modules (/tmp/my_files.txt) 100 131 0 0 0
Whole codebase (find src -name '*.rs') 486 131 53 3218 1197
$ scripts/audit-bad-patterns.sh --strict --files /tmp/my_files.txt
[...]
  Total hit lines        : 0
  Strict (A/B/C/L/M/N/O) : 0
$ echo $?
0

What this round of clean-up fixed

Compared to the previous snapshot (4252 / 2175), the codebase-wide totals fell by 1034 lines (24%) and the strict-section subset by 978 lines (45%). The drops are partly real fixes and partly tighter regexes that no longer flag value-providing fallbacks as panics.

Wave What was done Files touched Hit-line drop
1 src/spool.rs: Result<_, String>anyhow::Result<_>. Both call sites in src/ws.rs updated to format the error via Display. 2 10
3 Bare .send().await? / .text().await? chains gain with_context() so mass-scan failures attribute to URL + verb. Added anyhow::Context import where missing. 23 46
4 DoS http_flood.rs now goes through crate::utils::network::build_http_client_with(...); new pool_max_idle_per_host field on HttpClientOpts so the framework helper covers the high-concurrency case. 2 1
6 42 let body = <expr>.text().await.unwrap_or_default() rewritten as explicit match arms that log via mprintln! (or eprintln! when the file doesn't use the framework macro), preserving the empty-string fallback for downstream string searches but no longer hiding the decode error. 26 42
8 unreachable!() in src/native/obfuscator_engine.rs removed (was guarding an exhaustive match arm that couldn't be reached anyway). 2 .expect("ASCII hex") panic-vectors in payloadgen / obfuscator converted to with_context()?. 1 .expect("ALL_METHOD_IDS must be non-empty") in obfuscator's RNG selection replaced with a typed Some/None match returning a safe fallback. 3 4
Tightened audit regexes Pattern \.unwrap\.unwrap\(\), \.expect_err\.expect_err\(, etc. — no longer counts .unwrap_or(default) as a panic. Comment lines (// / ///) skipped. ~970 false-positive removals

After all waves, every wave-target pattern is zero in the codebase:

Pattern Before After
.unwrap() 0 0
.expect( 4 0
panic!(), unreachable!(), todo!(), unimplemented!() 1 0
Result<_, String> (in spool.rs) 10 0
Bare .send().await? (no context) 14 0
Bare .text().await? (no context) 29 0
let _ = x.text().await.unwrap_or_default() 42 0
reqwest::Client::builder() outside the framework helper 1 0

Per-section table (whole codebase, post-fix)

Section Hits Notes
A. Panicking error handling 44 41 unwrap_or_default() (almost all on serde_json::to_vec(json!(...)) of literal struct — impossible to fail; the rest are Option::unwrap_or_default() value-providing fallbacks) + 3 assert! / assert_eq! inside #[test] blocks.
B. Silent error swallowing 1100 Long-running scanner / brute-force loop pattern (try one host, continue on failure). For new modules the catalogue bans the anonymous form (B1B5); for pre-existing scanners the mass-scan helper already aggregates per-target failure counts so the unattributed Err(_) is acceptable.
C. Lint suppression 23 All 23 are #[allow(dead_code)] on pub framework helpers consumed only via the API/MCP/WS auto-dispatchers; each one carries a comment line explaining the cross-layer use.
D. Panic vectors (index/slice) 848 arr[N] 597, &buf[..n] 164. Concentrated in protocol parsers where the length is checked one line above.
E. Numeric / unsafe 1014 843 numeric as casts in protocol parsers (RDP, PQ ratchet, packet builders), 22 unsafe { } blocks (concentrated in src/native/* for FFI), 12 pointer casts in the same FFI files. No transmute, no bare unsafe fn outside crate::native::*.
F. Async / blocking 91 DoS / scanner / brute-force modules where blocking-I/O is intentional, plus build / config / setup paths that legitimately call sync APIs.
G. Logging 0 clean
H. HTTP layer 2 Both are inside src/utils/network.rs itself: line 477 is a doc comment, line 501 is the framework's authoritative build_http_client_with.
I. Iterator glitches 0 clean
J. Style / secrets 70 41 "admin","admin" and 25 "root","root" are credential brute-force seed lists — not embedded secrets. 1 Box<dyn, 2 TODO/FIXME/HACK, 1 len() > 0.
L. Crypto 23 All by-design (Postgres MD5 auth, MySQL SHA-1 auth, VNC DES, scanner cipher enumeration, NetBIOS xid). Documented in BAD_PATTERNS.md "Codebase-wide observations".
M. SQL & command injection 0 clean
N. UB / concurrency 9 4 std::mem::zeroed() inside unsafe { } blocks for libc::sockaddr_* / libc::rlimit FFI structs; 5 Result<…, String> (down from 10 — the spool.rs ones are gone, the remaining are in non-publishable t/ test code).
O. Performance 0 clean
P. API hygiene 0 clean

Why the strict-mode total is still 1197

The strict gate considers sections A, B, C, L, M, N, O. After the fixes above, the breakdown is:

A  (44) 41 unwrap_or_default + 3 test asserts
B  (1100) Err(_) + if let Ok(...)+let _ = pre-existing in scanners
C  (23) #[allow(dead_code)] with explanation comments
L  (23) protocol-required crypto
M  (0)
N  (9) FFI-internal mem::zeroed + leftover Stringly-typed errors
O  (0)

Mass-fixing the B bucket (the bulk: 1100 lines) would change scanner behavior — those Err(_) => { /* keep going */ } arms are intentional "try one host, skip on failure, continue" loops. The catalogue's strict mode is enforced on new modules only; pre-existing scanners that follow the legacy convention pass through unchanged. To bring those older modules in line, do them per-module with care to preserve the "skip-on-failure" semantics while reporting per-target outcomes. That's multi-day per-module review work, not mechanical replacement, and it's out of scope here.

Per-module strict gate (new modules) — clean

$ scripts/audit-bad-patterns.sh --strict --files /tmp/my_files.txt
RUSTSPLOIT BAD-PATTERN AUDIT
100 file(s) under audit
[...]
Patterns scanned       : 131
Patterns with hits     : 0
Total hit lines        : 0
Strict (A/B/C/L/M/N/O) : 0

How to re-run

# Whole codebase, informational
scripts/audit-bad-patterns.sh

# One section
scripts/audit-bad-patterns.sh --section A

# Strict gate on the modules I authored (or any file list)
scripts/audit-bad-patterns.sh --strict --files /tmp/my_files.txt

# CI gate (run by reviewer on PR file list)
git diff --name-only origin/main...HEAD | grep '\.rs$' > /tmp/changed.txt
scripts/audit-bad-patterns.sh --strict --files /tmp/changed.txt

The --strict flag exits non-zero if any pattern in sections A, B, C, L, M, N, or O has a hit on the audited file set. Sections D, E, F, H, J cover code-quality items that need human review (a &buf[..n] slice right after a length-checked read is fine; a brand-new one isn't).