Skip to content

Commit 317a0e9

Browse files
hyperpolymathclaude
andcommitted
fix(assail): tighten UnboundedAllocation Rust heuristic (72→6 on 007)
- Scan code_only (comments + string literals already stripped) instead of raw content, same fix shape as 6f18caf for UnsafeCode. Dangerous-word keywords (unbounded / infinite / unlimited / …) no longer trigger on comment text, doc-strings, or source embedded in raw string literals. - Remove the bare `for ` + `push(`, `while let` + `push(`, `loop` + `push/Vec::new`, and `recursion` pair heuristics. These substrings co-occur in virtually every non-trivial Rust file (normal bounded iteration is `for x in collection { v.push(y) }`), so they produced ~60 Critical findings per average repo at near-zero signal. - Add `is_infinite` exclusion so Rust std `f64::is_infinite()` no longer counts as an `infinite` keyword hit. Measured impact on 007-lang: - Before: 72 UnboundedAllocation Critical findings (72 distinct files). - After: 6 residual findings, all either accepted full-file reads of trusted source/config inputs (`std::fs::read_to_string` in CLI/import-resolver/diagnostics paths) or benign test-fn identifiers inside `#[cfg(test)] mod tests` blocks that the file-path-based test suppressor does not yet reach (`choreography_unbounded_loop`, `validate_detects_left_recursion`). All 167 lib tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 72e96a7 commit 317a0e9

1 file changed

Lines changed: 31 additions & 20 deletions

File tree

src/assail/analyzer.rs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -836,26 +836,37 @@ impl Analyzer {
836836
// Count allocations but flag unbounded patterns separately
837837
stats.allocation_sites += vec_new_count + box_new_count + string_new_count;
838838

839-
// Flag unbounded allocation patterns as high-risk - Enhanced detection
840-
let has_unbounded_allocations = content.contains("while let") && content.contains("push(") ||
841-
content.contains("for") && content.contains("push(") ||
842-
content.contains("unbounded") ||
843-
content.contains("no_bound") ||
844-
content.contains("no_limit") ||
845-
content.contains("infinite") ||
846-
content.contains("boundless") ||
847-
content.contains("unlimited") ||
848-
content.contains("unconstrained") ||
849-
// Common unbounded loop + allocation patterns
850-
(content.contains("loop") && content.contains("Vec::new")) ||
851-
(content.contains("loop") && content.contains("push")) ||
852-
(content.contains("recursion") && !content.contains("depth")) ||
853-
// Suspicious capacity patterns
854-
content.contains("with_capacity(0)") ||
855-
content.contains("with_capacity(1)") ||
856-
// Network-related unbounded patterns
857-
(content.contains("read_to_end") && !content.contains("limit")) ||
858-
(content.contains("read_to_string") && !content.contains("limit"));
839+
// Flag unbounded allocation patterns as high-risk.
840+
//
841+
// The check runs against `code_only` (comments + string literals already
842+
// stripped) so that dangerous-word keywords embedded in doc comments,
843+
// string literals, or generated source text do not falsely fire.
844+
//
845+
// The earlier version also paired bare `for` / `while let` / `loop`
846+
// tokens with `push(` or `Vec::new` as standalone heuristics. Those
847+
// pairs co-occur in essentially every non-trivial Rust file (bounded
848+
// `for x in collection { v.push(y) }` is normal code), so they
849+
// generated ~60 critical findings per average repo with no signal.
850+
// Dropped in favour of the explicit-keyword / tiny-capacity /
851+
// unlimited-read signals below, which remain specific enough to be
852+
// useful.
853+
let has_unbounded_allocations = code_only.contains("unbounded")
854+
|| code_only.contains("no_bound")
855+
|| code_only.contains("no_limit")
856+
|| code_only.contains("boundless")
857+
|| code_only.contains("unlimited")
858+
|| code_only.contains("unconstrained")
859+
// `infinite` matches Rust std `f64::is_infinite()`, which is
860+
// benign. Require the word in a non-method-call context.
861+
|| (code_only.contains("infinite") && !code_only.contains("is_infinite"))
862+
// Unterminated recursion lacking any depth guard.
863+
|| (code_only.contains("recursion") && !code_only.contains("depth"))
864+
// Suspiciously small initial capacity for a growing vector.
865+
|| code_only.contains("with_capacity(0)")
866+
|| code_only.contains("with_capacity(1)")
867+
// Network / I/O primitives that slurp without a cap.
868+
|| (code_only.contains("read_to_end") && !code_only.contains("limit"))
869+
|| (code_only.contains("read_to_string") && !code_only.contains("limit"));
859870

860871
if has_unbounded_allocations && !is_test_file {
861872
weak_points.push(WeakPoint {

0 commit comments

Comments
 (0)