|
| 1 | +use compact_security_detectors_sdk::{ |
| 2 | + ast::{ |
| 3 | + declaration::Declaration, definition::Definition, expression::Expression, |
| 4 | + node_type::NodeType, ty::Type, |
| 5 | + }, |
| 6 | + codebase::{Codebase, SealedState}, |
| 7 | + detector::{CompactDetector, DetectorOpaque, DetectorReportTemplate, DetectorResult}, |
| 8 | +}; |
| 9 | +use std::collections::HashMap; |
| 10 | + |
| 11 | +compact_security_detectors_sdk::detector! { |
| 12 | + |
| 13 | + #[type_name = ArrayLoopBoundCheck] |
| 14 | + fn array_loop_bound_check( |
| 15 | + codebase: &Codebase<SealedState>, |
| 16 | + ) -> Option<Vec<DetectorResult>> { |
| 17 | + let mut errors = Vec::new(); |
| 18 | + for for_stmt in codebase.list_for_statement_nodes() { |
| 19 | + let index_access_expressions = codebase.get_children_cmp(for_stmt.id, |n| { |
| 20 | + matches!(n, NodeType::Expression(Expression::IndexAccess(_))) |
| 21 | + }); |
| 22 | + let upper_bound = for_stmt.upper_bound_nat(); |
| 23 | + if upper_bound.is_none() { |
| 24 | + continue; |
| 25 | + } |
| 26 | + let upper_bound = upper_bound.unwrap(); |
| 27 | + |
| 28 | + for index_access in index_access_expressions { |
| 29 | + if let NodeType::Expression(Expression::IndexAccess(index_access)) = index_access { |
| 30 | + let arr_type = codebase.get_symbol_type_by_id(index_access.base.id()); |
| 31 | + if let Some(Type::Vector(t_vec)) = arr_type { |
| 32 | + if t_vec.size_nat().unwrap_or(0) >= upper_bound { |
| 33 | + let parent = codebase.get_parent_container(index_access.id); |
| 34 | + let mut parent_type = "circuit"; |
| 35 | + let parent_name = match parent { |
| 36 | + Some(NodeType::Definition(Definition::Circuit(c))) => c.name(), |
| 37 | + Some(NodeType::Declaration(Declaration::Constructor(_))) => { |
| 38 | + parent_type = "constructor"; |
| 39 | + String::default() |
| 40 | + } |
| 41 | + _ => String::from("Unknown"), |
| 42 | + }; |
| 43 | + errors.push( |
| 44 | + DetectorResult { |
| 45 | + file_path: codebase.find_node_file(index_access.id).unwrap().fname, |
| 46 | + offset_start: index_access.location.offset_start, |
| 47 | + offset_end: index_access.location.offset_end, |
| 48 | + extra: { |
| 49 | + let mut map = HashMap::new(); |
| 50 | + map.insert("ARRAY_INDEX_ACCESS".to_string(), index_access.location.source.clone()); |
| 51 | + map.insert("PARENT_NAME".to_string(), parent_name); |
| 52 | + map.insert("PARENT_TYPE".to_string(), parent_type.to_string()); |
| 53 | + Some(map) |
| 54 | + }, |
| 55 | + }, |
| 56 | + ); |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + if errors.is_empty() { |
| 63 | + None |
| 64 | + } else { |
| 65 | + Some(errors) |
| 66 | + } |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +impl DetectorReportTemplate for ArrayLoopBoundCheck { |
| 71 | + fn id(&self) -> String { |
| 72 | + String::from("array-loop-bound-check") |
| 73 | + } |
| 74 | + fn uid(&self) -> String { |
| 75 | + String::from("3fTuAe") |
| 76 | + } |
| 77 | + fn description(&self) -> String { |
| 78 | + String::from("Detects potential out-of-bounds array index accesses within loops, which can cause runtime errors or unexpected behavior.") |
| 79 | + } |
| 80 | + fn severity(&self) -> String { |
| 81 | + String::from("medium") |
| 82 | + } |
| 83 | + fn tags(&self) -> Vec<String> { |
| 84 | + vec![ |
| 85 | + String::from("audit"), |
| 86 | + String::from("reportable"), |
| 87 | + String::from("compact"), |
| 88 | + ] |
| 89 | + } |
| 90 | + fn title_single_instance(&self) -> String { |
| 91 | + String::from("Potential Out-of-Bounds Array Index Access Detected") |
| 92 | + } |
| 93 | + fn title_multiple_instance(&self) -> String { |
| 94 | + String::from( |
| 95 | + "The following potential out-of-bounds array index accesses were found in the code:", |
| 96 | + ) |
| 97 | + } |
| 98 | + fn opening(&self) -> String { |
| 99 | + String::from("Accessing array elements outside their valid index range can lead to runtime errors, security vulnerabilities, or unpredictable program behavior. This issue typically occurs when a loop iterates beyond the array's defined bounds, resulting in unsafe memory access.") |
| 100 | + } |
| 101 | + fn body_single_file_single_instance(&self) -> String { |
| 102 | + String::from("In `$file_name`, a potential out-of-bounds array index access was detected in the `$PARENT_NAME` $PARENT_TYPE on line $instance_line. The array access statement `$ARRAY_INDEX_ACCESS` may exceed the array's valid index range.") |
| 103 | + } |
| 104 | + fn body_single_file_multiple_instance(&self) -> String { |
| 105 | + String::from("In `$file_name`, multiple potential out-of-bounds array index accesses were detected. Review each instance below to ensure array accesses remain within valid bounds.") |
| 106 | + } |
| 107 | + fn body_multiple_file_multiple_instance(&self) -> String { |
| 108 | + String::from("Across $total_files files, multiple potential out-of-bounds array index accesses were detected. Review each instance below to ensure array accesses remain within valid bounds.") |
| 109 | + } |
| 110 | + fn body_list_item_single_file(&self) -> String { |
| 111 | + { |
| 112 | + "{body_list_item}".to_string() |
| 113 | + } |
| 114 | + } |
| 115 | + fn body_list_item_multiple_file(&self) -> String { |
| 116 | + { |
| 117 | + "{body_list_item}".to_string() |
| 118 | + } |
| 119 | + } |
| 120 | + fn closing(&self) -> String { |
| 121 | + String::from("To resolve this issue, ensure that all array index accesses within loops are properly bounded and do not exceed the array's size. Failing to address this may result in runtime exceptions, data corruption, or exploitable vulnerabilities.") |
| 122 | + } |
| 123 | + fn template(&self) -> String { |
| 124 | + String::from( |
| 125 | + r#"template: |
| 126 | + title: Potential Out-of-Bounds Array Index Access Detected |
| 127 | + opening: Accessing array elements outside their valid index range can lead to runtime errors, security vulnerabilities, or unpredictable program behavior. This issue typically occurs when a loop iterates beyond the array's defined bounds, resulting in unsafe memory access. |
| 128 | + body-single-file-single-instance: | |
| 129 | + In `$file_name`, a potential out-of-bounds array index access was detected in the `$PARENT_NAME` $PARENT_TYPE on line $instance_line. The array access statement `$ARRAY_INDEX_ACCESS` may exceed the array's valid index range. |
| 130 | + body-single-file-multiple-instance: | |
| 131 | + In `$file_name`, multiple potential out-of-bounds array index accesses were detected. Review each instance below to ensure array accesses remain within valid bounds. |
| 132 | + body-multiple-file-multiple-instance: | |
| 133 | + Across $total_files files, multiple potential out-of-bounds array index accesses were detected. Review each instance below to ensure array accesses remain within valid bounds. |
| 134 | + body-list-item-intro: 'The following potential out-of-bounds array index accesses were found in the code:' |
| 135 | + body-list-item-always: '- The `$ARRAY_INDEX_ACCESS` statement in the `$PARENT_NAME` $PARENT_TYPE on line $instance_line of [`$file_name`]($instance_line_link)' |
| 136 | + closing: | |
| 137 | + To resolve this issue, ensure that all array index accesses within loops are properly bounded and do not exceed the array's size. Failing to address this may result in runtime exceptions, data corruption, or exploitable vulnerabilities. |
| 138 | +"#, |
| 139 | + ) |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +#[no_mangle] |
| 144 | +pub extern "C" fn external_detector() -> *mut DetectorOpaque { |
| 145 | + let detector: CompactDetector = Box::new(ArrayLoopBoundCheck); |
| 146 | + Box::into_raw(detector) as *mut DetectorOpaque |
| 147 | +} |
0 commit comments