44//! Core responsibility: map each `WeakPoint` from the scanner output into a
55//! `Finding` struct with appropriate:
66//! - Fleet category (e.g., "static-analysis/unsafe-code")
7- //! - Rule ID (canonical PA001–PA023 pattern for dedup with Hypatia findings)
7+ //! - Rule ID (canonical PA001–PA025 pattern for dedup with Hypatia findings)
88//! - Triangle tier (Eliminate / Substitute / Control)
99//! - Confidence score (honest assessment of detection accuracy)
1010//! - Fixability (whether automated remediation is possible)
@@ -261,6 +261,30 @@ pub fn category_mapping(category: &str) -> Option<CategoryMapping> {
261261 default_confidence : 0.85 ,
262262 fixability : Fixability :: Yes ,
263263 } ) ,
264+ // PA024 — input boundary: unchecked deserialization of CBOR/MessagePack (serde_cbor,
265+ // ciborium, rmp_serde in Rust), JSON.parse without try-catch (JavaScript), JSON3.read
266+ // without error handling (Julia). Confidence 0.72 — per-file heuristics have false
267+ // positives when error handling is in the call site rather than the same file.
268+ "InputBoundary" => Some ( CategoryMapping {
269+ fleet_category : "static-analysis/input-boundary" ,
270+ rule_id : "PA024" ,
271+ rule_name : "Unguarded input boundary" ,
272+ triangle_tier : TriangleTier :: Control ,
273+ default_confidence : 0.72 ,
274+ fixability : Fixability :: Partial ,
275+ } ) ,
276+ // PA025 — mutation gap: test suites with no mutation-test tooling or no assertion
277+ // diversity. Rust projects without cargo-mutants config, Elixir without StreamData/
278+ // ExUnitProperties, Julia @testset with only type-check assertions. Confidence 0.80 —
279+ // absent tooling is factual; assertion diversity heuristic has modest FP rate.
280+ "MutationGap" => Some ( CategoryMapping {
281+ fleet_category : "static-analysis/mutation-gap" ,
282+ rule_id : "PA025" ,
283+ rule_name : "Mutation coverage gap" ,
284+ triangle_tier : TriangleTier :: Substitute ,
285+ default_confidence : 0.80 ,
286+ fixability : Fixability :: Partial ,
287+ } ) ,
264288 _ => None ,
265289 }
266290}
@@ -604,6 +628,7 @@ mod tests {
604628 "UnsafeTypeCoercion" , "UncheckedAllocation" , "UnboundedLoop" , "BlockingIO" ,
605629 "DeadlockPotential" , "DynamicCodeExecution" , "InsecureProtocol" ,
606630 "InfiniteRecursion" , "ProofDrift" , "CryptoMisuse" , "SupplyChain" ,
631+ "InputBoundary" , "MutationGap" ,
607632 ] ;
608633
609634 let mut rule_ids = std:: collections:: HashSet :: new ( ) ;
@@ -616,6 +641,6 @@ mod tests {
616641 cat
617642 ) ;
618643 }
619- assert_eq ! ( rule_ids. len( ) , 23 ) ;
644+ assert_eq ! ( rule_ids. len( ) , 25 ) ;
620645 }
621646}
0 commit comments