├── README.md ├── etc ├── L3X_SAST_Report.html ├── L3X_SAST_Report_Rust.html ├── Solidity-Ethereum_L3X_SAST_Report.html ├── de.png ├── exec.png ├── patterns-eth-solidity.md ├── patterns-rust.md ├── patterns-sol.md └── report.gif └── l3x ├── .gitignore ├── Cargo.toml └── src ├── gpt_validator.rs ├── main.rs ├── report_generator.rs └── vulnerability_checks.rs /README.md: -------------------------------------------------------------------------------- 1 | # L3X - AI-driven Static Analyzer 2 | 3 | L3X detects vulnerabilities in Rust and Solidity code based on patterns and AI code analysis. Various LLMs act as validators for vulnerabilities detected by patterns and validate each other's results in AI code analysis. Vulnerabilities are confirmed when they receive confirmation from a majority of validators. As a result, a report with validated vulnerabilities and detected safe patterns is generated. 4 | 5 | ## Supported Languages and Smart Contracts: 6 | 7 | - General Rust code 8 | - Rust-based Solana smart contracts 9 | - Solidity-based Ethereum smart contracts 10 | 11 | ## Design 12 | 13 | ![design](/etc/de.png) 14 | 15 | ## How It Works 16 | 17 | - **Vulnerability Patterns Code Analysis:** Check the rules: 18 | - [Rust-General](etc/patterns-rust.md) 19 | - [Rust-Solana](etc/patterns-sol.md) 20 | - [Solidity-Ethereum](etc/patterns-eth-solidity.md) 21 | - **AI Code Analysis:** Analysis of smart contract code for vulnerabilities using Language Model validators (LLMs). 22 | - **LLM Validators:** Potential vulnerabilities detected are passed on to multiple LLM validators for confirmation. Three different models are utilized: GPT-4, Gemini, and Claude 2. 23 | - **Validation Process:** Each validator independently assesses potential vulnerabilities. Detected vulnerabilities undergo a validation check. 24 | - **Decision Making:** 25 | - **Valid:** A vulnerability is confirmed as valid when at least two LLM validators agree. 26 | - **False Positive:** A finding is marked as a false positive when identified as such by at least two validators. 27 | - **Cross-Validation:** Each LLM performs a cross-validation check during the AI Code Analysis phase. 28 | - **Report Generation:** A report is generated that compiles all confirmed vulnerabilities, with duplicates removed. 29 | 30 | ## Current limitations 31 | 32 | > Currently it's MVP 33 | 34 | 1. Vulnerabilities detects only based on vuln patterns 35 | 2. GPT-3.5/4 act as validators for vulnerabilities detected by patterns 36 | 37 | ## LLM supported 38 | 39 | - GPT-3.5 40 | - GPT-4 41 | - Claude (Soon) 42 | - Gemini (Planned) 43 | 44 | ## Set API Keys 45 | 46 | Set the openai api key as an environment variable OPENAI_KEY in your operating system 47 | 48 | Linux/Mac 49 | 50 | ```bash 51 | export OPENAI_KEY=sk-ApiKeyExample 52 | ``` 53 | 54 | Windows 55 | 56 | ```bash 57 | set OPENAI_KEY=sk-ApiKeyExample 58 | ``` 59 | 60 | If you want to specify particular OpenAI org or project, set up `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables. 61 | 62 | ## How to Use ❓ 63 | 64 | 1. Build 65 | ```bash 66 | cargo build 67 | ``` 68 | 2. Compile 69 | ```bash 70 | cargo run 71 | ``` 72 | 3. Run 73 | 74 | - By default, only critical and high severity findings are validated. To validate all findings, use the ```--all-severities``` flag. 75 | - To skip vulnerability validation and generate the report without validation, use the ```--no-validation``` flag. 76 | - To specify the OpenAI model to use for vulnerability validation (default is gpt-3.5-turbo), use the ```--model``` flag followed by the model name (e.g., --model=gpt-4). 77 | 78 | ```bash 79 | l3x smart-contracts-folder-to-analyse [--all-severities] [--no-validation] [--model=MODEL] 80 | ``` 81 | 82 | ## Usage Example 🏁 83 | 84 | ![exec!](etc/exec.png) 85 | 86 | ![gif!](etc/report.gif) 87 | 88 | [Report example - Rust](https://github.com/VulnPlanet/l3x/blob/main/etc/L3X_SAST_Report_Rust.html) 89 | 90 | [Report example - Solana](https://github.com/VulnPlanet/l3x/blob/main/etc/L3X_SAST_Report.html) 91 | 92 | [Report example - Ethereum](https://github.com/VulnPlanet/l3x/blob/main/etc/Solidity-Ethereum_L3X_SAST_Report.html) 93 | 94 | ## Roadmap 🗓️ 95 | 96 | - ~~Design~~ 97 | - ~~MVP concept~~ 98 | - ~~Rust support~~ 99 | - ~~Solana support~~ 100 | - ~~Solidity support~~ 101 | - ~~GPT-4 Integration~~ 102 | - Claude Integration 🔜 103 | - Gemini Integration 104 | - AI Code Analysis 105 | - AI Code Analysis - Cross-Validation 106 | - Refactor 107 | - Accuracy Comparison 108 | 109 | -------------------------------------------------------------------------------- /etc/L3X_SAST_Report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vulnerability Report 7 | 8 | 41 | 42 | 43 |
44 |

L3X - Static Application Security Testing (SAST) Report

45 |

Technology: Solana
Languange: Rust

46 |

Check more on: VulnPlanet
Contribute: GitHub

47 |
48 |
49 |

Summary

50 |
51 |

By Severity

52 | 53 |
54 |
55 |

False Positive Rate

56 | 57 |
58 |
59 |
60 |

Vulnerabilities

61 |

🟢 GPT 3.5 - Valid or Not possible to determine

62 |

🔴 GPT 3.5 - False Positive

63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 566 | 567 |
IDTitleStatusSeverityFileDescriptionDetails
VULN008Account Signer Check🟢 GPT 3.5Hightest-code2/program/src/lib.rsEnsure the expected signer account has actually signed to prevent unauthorized account modifications.The vulnerability titled 'Account Signer Check' flagged at line number 36 is valid. 81 | 82 | In the provided code, the function `next_account_info` is used to obtain the next account from the iterator `accounts_iter`. However, the function does not verify if the obtained account is signed by the caller. It is crucial to ensure that the account is signed by the caller to prevent unauthorized modifications to the account's data. 83 | 84 | To fix this vulnerability, you should add a signer check for the obtained account. This can be done by modifying the code as follows: 85 | 86 | ```rust 87 | // Get the account that stores greeting count information. 88 | let accounts_iter = &mut accounts.iter(); 89 | let account = next_account_info(accounts_iter)?; 90 | 91 | // Check if the account is signed by the caller 92 | if !account.is_signer { 93 | return Err(ProgramError::MissingRequiredSignature); 94 | } 95 | ``` 96 | 97 | By adding the `if !account.is_signer` check, the program will verify that the obtained account is signed by the caller. If the account is not signed, the function will return `Err(ProgramError::MissingRequiredSignature)`. 98 | 99 | With this fix, the vulnerability will be mitigated, ensuring that only signed accounts can be modified by the program.
VULN010Account Owner or Program ID Check🟢 GPT 3.5Hightest-code2/program/src/lib.rsVerify the owner of state accounts to prevent fake data injection by malicious programs.The vulnerability flagged by the SAST tool is valid. 108 | 109 | The flagged line of code is `next_account_info(accounts_iter)?;`. 110 | 111 | The vulnerability is titled 'Account Owner or Program ID Check'. The severity is 'High'. This vulnerability occurs when the program assumes that the next account info in the iterator belongs to the program itself. This assumption is incorrect because the program does not verify the owner of the next account info before accessing it. 112 | 113 | To fix this vulnerability, the program should explicitly check if the next account info belongs to the program or not. This can be done by comparing the owner of the account with the program ID using the `AccountInfo::is_program` method. The fixed code would look like: 114 | 115 | ```rust 116 | let accounts_iter = &mut accounts.iter(); 117 | let account = next_account_info(accounts_iter)?; 118 | 119 | // The account must be owned by the program in order for the 120 | // program to write to it. If that is not the case then the 121 | // program has been invoked incorrectly and we report as much. 122 | if !account.is_program(program_id) { 123 | return Err(ProgramError::IncorrectProgramId); 124 | } 125 | ``` 126 | 127 | By adding the `if !account.is_program(program_id)` check, the program ensures that it only accesses and modifies accounts that are owned by the program itself, avoiding any potential security vulnerabilities.
VULN011Account Initialized Check🟢 GPT 3.5Hightest-code2/program/src/lib.rsPrevent re-initialization of already initialized accounts.Based on the provided code and information, it is not possible to determine the validity of the detected vulnerability without additional information about the specific rule or analyzer used by the Static Application Security Testing (SAST) tool. 136 | 137 | However, considering the flagged line of code `try_from_slice(`, it is possible that the SAST tool is highlighting the potential vulnerability related to the deserialization of data using the `try_from_slice` method from the `GreetingAccount` struct. 138 | 139 | To validate the vulnerability, it would be helpful to understand the rule or analyzer used by the SAST tool and the specific reasoning behind flagging this line of code. For example, the tool might be warning about potential buffer overflows or insufficient bounds checking when deserializing the account data. 140 | 141 | To suggest a possible fix, you can ensure that the deserialization process is done safely and with proper bounds checking. Here are some steps you can take to address this: 142 | 143 | 1. Validate the length of the input data before deserialization to avoid potential buffer overflows. 144 | 2. Implement proper error handling and validation when deserializing the data to handle any potential failures. 145 | 3. Consider using a more secure deserialization method, such as using libraries or functions with built-in safety checks, to prevent vulnerabilities like deserialization attacks. 146 | 147 | It is recommended to consult the documentation of the `borsh` library being used for deserialization to ensure the safe usage of the `try_from_slice` method and to understand any specific security considerations or best practices.
VULN002Loss of Precision🟢 GPT 3.5Hightest-code2/program/src/checked.rsThe use of try_round_u64() for rounding up may lead to loss of precision.Based on the information provided, the vulnerability labeled 'Loss of Precision' with severity 'High' at line number 25 is valid. 156 | 157 | The vulnerability occurs at the following line of code: 158 | 159 | ```rust 160 | let rounded_price = price.try_round_u64().unwrap(); // Loss of precision 161 | ``` 162 | 163 | The try_round_u64() method attempts to round the floating-point number 'price' to an unsigned 64-bit integer. However, this conversion may result in loss of precision, as the floating-point number may have decimal places that cannot be accurately represented in an integer. 164 | 165 | To fix this vulnerability, you can use a different approach for converting the floating-point number to an integer based on your specific requirements. Some possible solutions include: 166 | 167 | 1. Truncation: Use the `as u64` cast to truncate the floating-point number to a 64-bit integer without rounding, if rounding is not important in your context. For example: 168 | 169 | ```rust 170 | let rounded_price = price as u64; 171 | ``` 172 | 173 | 2. Rounding: If you need to round the number, consider using a proper rounding algorithm or library (e.g., the `round` method from the `num` crate) to ensure accurate rounding. For example: 174 | 175 | ```rust 176 | let rounded_price = price.round() as u64; 177 | ``` 178 | 179 | It's important to assess the specific requirements and potential implications of rounding or truncating the value to choose the appropriate approach for your use case.
VULN006Error Not Handled🟢 GPT 3.5Hightest-code2/program/src/checked.rsFunction calls that might return `Err` are not checked for errors.Based on the provided information and code context, the reported vulnerability titled 'Error Not Handled' at line number 32 is valid. The vulnerability refers to the fact that the error returned by the `transfer` function from the `"spl_token"` crate is not being properly handled. This can lead to unexpected behavior or crashes if the transfer fails. 188 | 189 | To fix this vulnerability, it is necessary to properly handle the potential error returned by the `transfer` function. One possible fix is to use the `?` operator to propagate the error up the call stack and handle it in the caller function. This can be done by changing the code as follows: 190 | 191 | ```rust 192 | let transfer_instruction = spl_token::instruction::transfer( 193 | _program_id, 194 | account.key, 195 | account.key, 196 | &account.key, 197 | &[], 198 | 1000, 199 | )?; // Now propagating the error using the `?` operator 200 | ``` 201 | 202 | By using the `?` operator, the error will be returned as a `ProgramResult` and can be handled appropriately in the `process_instruction` function or any parent function that calls it. 203 | 204 | Please note that there might be other error handling strategies depending on the specific requirements of your application.
VULN008Account Signer Check🟢 GPT 3.5Hightest-code2/program/src/checked.rsEnsure the expected signer account has actually signed to prevent unauthorized account modifications.Based on the provided information, the flagged vulnerability titled 'Account Signer Check' at line number 29 appears to be valid and not a false positive. 213 | 214 | The vulnerability arises from the use of the `next_account_info` function without performing a signer check. In Solana, it is important to verify that the account being accessed or manipulated is signed by the appropriate authority. Failing to do so may lead to unauthorized or malicious actions. 215 | 216 | To fix this vulnerability, you should add a signer check before using the account information. Here's an example of how it can be done: 217 | 218 | ```rust 219 | let account_info_iter = &mut accounts.iter(); 220 | let account = next_account_info(account_info_iter)?; 221 | if !account.is_signer { 222 | return Err(ProgramError::MissingRequiredSignature); 223 | } 224 | ``` 225 | 226 | This code first checks if the `account` is a signer by accessing the `is_signer` field. If it is not a signer, an `Err` is returned with the appropriate error code (`MissingRequiredSignature`). Adjust the error code based on your specific needs. 227 | 228 | By incorporating this fix, you ensure that the account being accessed has proper authorization, mitigating the vulnerability.
VULN009Account Writable Check🟢 GPT 3.5Hightest-code2/program/src/checked.rsEnsure state accounts are checked as writable to prevent unauthorized modifications.Based on the information provided and the full code context, the flagged vulnerability titled 'Account Writable Check' with severity 'High' at line number 29 in the Rust code file appears to be a valid vulnerability. 237 | 238 | The vulnerability is related to the lack of a signer check for the account obtained from `next_account_info` function. The code does not verify whether the account is signed by a valid signer, which can be a security risk. It is important to ensure that the account being accessed, modified, or used for any critical operations is properly authenticated. 239 | 240 | To fix this vulnerability, you should add a signer check before using the account obtained from `next_account_info`. This can be done using the `is_signer` method available on the `AccountInfo` struct. 241 | 242 | Here's an example of how you can add the signer check: 243 | 244 | ```rust 245 | let account_info_iter = &mut accounts.iter(); 246 | let account = next_account_info(account_info_iter)?; 247 | if !account.is_signer { 248 | return Err(ProgramError::MissingRequiredSignature); 249 | } 250 | // Rest of the code 251 | ``` 252 | 253 | In this example, the code checks if the `account` is a signer by accessing the `is_signer` field of `AccountInfo`. If it is not a signer, the code returns an error (in this case, `ProgramError::MissingRequiredSignature`). Otherwise, the code can proceed with the rest of the operations. 254 | 255 | Adding this signer check ensures that only authorized accounts are used, thereby mitigating potential security risks.
VULN010Account Owner or Program ID Check🟢 GPT 3.5Hightest-code2/program/src/checked.rsVerify the owner of state accounts to prevent fake data injection by malicious programs.Based on the provided information and code context, the vulnerability flagged by the SAST tool for the line: 264 | 265 | next_account_info(account_info_iter)? 266 | 267 | appears to be valid. The vulnerability is labeled as 'Account Owner or Program ID Check' with severity 'High'. 268 | 269 | In the given code, the "next_account_info" function is called without checking whether the account is signed, which could lead to security issues. It is essential to verify the signer of the account to ensure that the instructions modifying the account are authorized by the owner. 270 | 271 | To fix this vulnerability, you should add a signer check before using the account. You can modify the code as follows: 272 | 273 | ```rust 274 | let account_info_iter = &mut accounts.iter(); 275 | let account = next_account_info(account_info_iter)?; 276 | if !account.is_signer { 277 | return Err(ProgramError::MissingRequiredSignature); 278 | } 279 | ``` 280 | 281 | This code checks whether the account is a signer, and if not, it returns a `ProgramError::MissingRequiredSignature`. This ensures that only signed accounts are used for modifying account data, preventing unauthorized modifications.
VULN022Arbitrary CPI - Anchor🟢 GPT 3.5Hightest-code2/program/src/checked.rsUnverified target program id in CPI can lead to arbitrary code execution.Based on the information provided, the vulnerability titled 'Arbitrary CPI - Anchor' at line number 43 in the Rust code file is valid. The line of code flagged: `solana_program::program::invoke(&transfer_instruction, accounts);` is a potential security issue. 290 | 291 | To fix this vulnerability, you should ensure that the `invoke` function is used with caution and that the external program being invoked is trusted and the call is properly validated and sanitized. Additionally, you should implement appropriate error handling and handle any potential errors returned from the `invoke` function.
RUST001Misuse of Unsafe Code🟢 GPT 3.5Hightest-code2/program/src/checked.rsUnsafe blocks may lead to undefined behavior and memory safety violations if not used carefully. Ensure justification and proper auditing.Based on the information provided and the full code context, the flagged vulnerability titled 'Misuse of Unsafe Code' at line number 46 is valid. 300 | 301 | The vulnerability arises from using unsafe code to dereference a raw pointer without proper validation or handling. Dereferencing a raw pointer can lead to undefined behavior, such as accessing invalid memory or causing segmentation faults. 302 | 303 | To fix this vulnerability, it is recommended to avoid using unsafe code when possible and use safe alternatives provided by the Rust language. If there is a legitimate need for using unsafe code, it should be accompanied by rigorous validation and proper error handling to prevent any potential issues.
VULN001Integer Overflow or Underflow🟢 GPT 3.5Hightest-code2/client/src/client.rsPerforming arithmetic operation without checking for overflow or underflow.The potential vulnerability flagged by the SAST tool appears to be valid. 312 | 313 | The flagged line of code is: 314 | 315 | ```rust 316 | let transaction_fee = fee_calculator.lamports_per_signature * 100; 317 | ``` 318 | 319 | This line of code performs a multiplication operation between `fee_calculator.lamports_per_signature` and the constant value `100`. The potential issue here is that if `fee_calculator.lamports_per_signature` is a large value, the result of the multiplication operation could overflow the maximum value that the data type can hold. 320 | 321 | To fix this vulnerability, you can use checked arithmetic to ensure that the multiplication operation does not result in an overflow. The `checked_mul` function can be used for this purpose. Here's an example of how to fix the code: 322 | 323 | ```rust 324 | let transaction_fee = fee_calculator.lamports_per_signature.checked_mul(100) 325 | .ok_or_else(|| Error::OverflowError)?; 326 | ``` 327 | 328 | In this code, `checked_mul` returns an `Option` that either contains the result of the multiplication or `None` if an overflow occurs. Using `.ok_or_else(|| Error::OverflowError)?` converts the `Option` into a `Result` and raises an `OverflowError` if an overflow occurs. 329 | 330 | By using checked arithmetic, you can prevent potential integer overflow or underflow vulnerabilities in your code.
VULN003Inaccurate Calculation Results🟢 GPT 3.5Hightest-code2/client/src/utils.rsReliance on saturating arithmetic operations without considering precision loss.Based on the information provided, it is not clear if the flagged vulnerability is valid or a false positive. The code snippet `let over_fee = paid_amount.saturating_sub(actual_amount);` appears to calculate the difference between `paid_amount` and `actual_amount` using the `saturating_sub` method. However, without understanding the context and the values of `paid_amount` and `actual_amount`, it is difficult to determine if there is an issue with inaccurate calculation results. 339 | 340 | To validate the vulnerability, you would need to analyze the values assigned to `paid_amount` and `actual_amount` and review the rest of the code to see if there are any potential issues with the calculation, data types, or input values. 341 | 342 | If the vulnerability is confirmed to be valid, a possible fix would depend on the desired behavior of the calculation. You may need to review the logic and ensure that the calculation accurately represents the expected result.
VULN011Account Initialized Check🟢 GPT 3.5Hightest-code2/client/src/utils.rsPrevent re-initialization of already initialized accounts.Based on the information provided, it is difficult to determine whether the identified vulnerability is valid or a false positive. To make a more accurate assessment, the code block containing the `try_from_slice()` function needs to be analyzed in more detail. 351 | 352 | However, assuming that the SAST tool has correctly identified the potential vulnerability, the possible fix would depend on the specific issue being flagged. Without further details about the vulnerability, it is challenging to provide an appropriate fix. 353 | 354 | To validate the vulnerability and suggest a fix, further information about the specific issue detected by the SAST tool is required.
RUST002Improper Error Handling🟢 GPT 3.5Mediumtest-code2/program/src/checked.rsOveruse of `unwrap()` or `expect()` can lead to panics. Prefer using error handling mechanisms like `match` or `if let`.Based on the provided code and context, the flagged vulnerability titled 'Improper Error Handling' at line number 25 is valid. The use of the `.unwrap()` method can result in a panic if an error occurs. This can lead to unexpected program termination and potential security risks. 363 | 364 | To fix this vulnerability, you should handle the error appropriately instead of relying on the `.unwrap()` method. There are several ways to handle errors in Rust, including using `match` expressions and `Result` values. Here's an example of handling the error using a `match` expression: 365 | 366 | ```rust 367 | let account = match next_account_info(account_info_iter) { 368 | Ok(account) => account, 369 | Err(err) => return Err(err.into()), // Handle the error here 370 | }; 371 | ``` 372 | 373 | In this example, the `next_account_info` function returns a `Result` value. By using a `match` expression, the error is properly handled, and you can return the error from the function if necessary. 374 | 375 | By replacing `.unwrap()` with proper error handling, you ensure that the program handles errors gracefully and avoids unexpected termination or security vulnerabilities.
RUST007Missing Boundary Checks🟢 GPT 3.5Mediumtest-code2/client/src/client.rsAccessing arrays or vectors without boundary checks can lead to panics or buffer overflows.Based on the provided code, it is not possible to determine whether the flagged vulnerability "Missing Boundary Checks" is valid or a false positive without additional information. The code snippet for line number 28 is not included, so it is not possible to analyze the context and identify the potential boundary check issue. 384 | 385 | To validate the vulnerability, please provide the code snippet for line number 28.
RUST007Missing Boundary Checks🟢 GPT 3.5Mediumtest-code2/client/src/lib.rsAccessing arrays or vectors without boundary checks can lead to panics or buffer overflows.Based on the provided information and the full code context, it appears that the flagged vulnerability "Missing Boundary Checks" at line number 11 is valid. 394 | 395 | The code in question is the `collateral_to_liquidity` function, which takes a `u64` value `collateral_amount` and attempts to perform a division operation on it. However, there are no boundary checks implemented to handle potential division by zero scenarios. 396 | 397 | A possible fix to address this vulnerability would be to add a check before performing the division operation to ensure that the divisor (`self.0`) is not zero. If the divisor is zero, the function could return an appropriate error or handle the scenario in a way that aligns with the desired behavior of the application. 398 | 399 | Here's an example of how the code could be modified to include the boundary check: 400 | 401 | ```rust 402 | pub fn collateral_to_liquidity(&self, collateral_amount: u64) -> Result { 403 | if self.0 == 0 { 404 | // Handle division by zero scenario 405 | return Err(Error::InvalidConfig("Divisor is zero".to_string())); 406 | } 407 | 408 | Decimal::from(collateral_amount) 409 | .try_div(self.0)? 410 | .try_floor_u64() 411 | } 412 | ``` 413 | 414 | By adding the check `if self.0 == 0`, the function can identify and handle the scenario when the divisor is zero and prevent a potential division by zero vulnerability.
RUST002Improper Error Handling🟢 GPT 3.5Mediumtest-code2/client/src/main.rsOveruse of `unwrap()` or `expect()` can lead to panics. Prefer using error handling mechanisms like `match` or `if let`.The flagged vulnerability, 'Improper Error Handling', likely refers to the use of the `.unwrap()` method in the Rust code. This method panics and potentially crashes the program if an error occurs. The severity of this vulnerability is rated as 'Medium'. 423 | 424 | To confirm if this vulnerability is valid or a false positive, we need to consider the specific context and requirements of the code. In this case, it depends on the expected behavior of the program when an error occurs. 425 | 426 | If the program is intended to handle errors gracefully and continue execution, then the use of `.unwrap()` is indeed an improper error handling practice. In such cases, it is recommended to use proper error handling techniques such as `match` or `Result` to handle potential errors and provide appropriate fallback actions. 427 | 428 | On the other hand, if the program expects an error at this specific point to be fatal, crashing the program might be an intentional behavior. In such cases, the use of `.unwrap()` could be considered appropriate. 429 | 430 | To suggest a possible fix, assuming that the goal is to handle errors gracefully and continue execution, you can replace the `.unwrap()` calls with proper error handling code. For example, you can use `match` or `Result` to handle the returned `Result` values and handle the potential errors based on your program's requirements. 431 | 432 | Here's an example of how you can modify the code to use `match` for error handling: 433 | 434 | ```rust 435 | use zeke_contract as zc; 436 | 437 | fn main() { 438 | let args = std::env::args().collect::>(); 439 | if args.len() != 2 { 440 | eprintln!( 441 | "usage: {} ", 442 | args[0] 443 | ); 444 | std::process::exit(-1); 445 | } 446 | let keypair_path = &args[1]; 447 | 448 | let connection = match zc::client::establish_connection() { 449 | Ok(connection) => connection, 450 | Err(error) => { 451 | eprintln!("Failed to establish connection: {}", error); 452 | std::process::exit(-1); 453 | } 454 | }; 455 | println!( 456 | "Connected to remote solana node running version ({}).", 457 | match connection.get_version() { 458 | Ok(version) => version, 459 | Err(error) => { 460 | eprintln!("Failed to get version: {}", error); 461 | std::process::exit(-1); 462 | } 463 | } 464 | ); 465 | 466 | // Remaining code follows the same error handling pattern 467 | // ... 468 | } 469 | ``` 470 | 471 | With this modified code, you handle errors gracefully and avoid the panics caused by `.unwrap()`. The error messages are printed to the standard error output, and the program exits with a non-zero exit code to indicate an error condition. You can customize the error handling code based on your specific requirements and error handling strategy.
RUST007Missing Boundary Checks🟢 GPT 3.5Mediumtest-code2/client/src/main.rsAccessing arrays or vectors without boundary checks can lead to panics or buffer overflows.Based on the information provided, it is not possible to accurately determine whether the flagged vulnerability is valid or a false positive. To determine the validity of the vulnerability, more information about the specific SAST tool, its rule set, and the context in which the code is being used is needed. 480 | 481 | In general, a "Missing Boundary Checks" vulnerability refers to situations where input data is not properly validated or sanitized, potentially leading to buffer overflow or other memory-related vulnerabilities. To address this type of vulnerability, it is important to ensure that any user-controlled input is properly validated and that boundaries are checked to prevent access beyond the allocated memory. 482 | 483 | To validate this specific vulnerability, you should review the SAST tool's documentation or rule set to understand the specific criteria that led to the detection. You can also review the code and analyze whether there are any potential risks of buffer overflow or memory access violations at line number 8 or any other relevant parts of the code. 484 | 485 | If the vulnerability is confirmed to be valid, a possible fix would involve implementing proper boundary checks and input validation to ensure that the code does not access memory outside the allowed range. The specific fix will depend on the context and intended behavior of the code. 486 | 487 | It is also recommended to consult with experienced developers or conduct a thorough code review to ensure the security of the application.
VULN009Account Writable Check🔴 GPT 3.5Hightest-code2/program/src/lib.rsEnsure state accounts are checked as writable to prevent unauthorized modifications.The vulnerability flagged by the Static Application Security Testing (SAST) tool appears to be a false positive in this case. 496 | 497 | The flagged line of code `next_account_info(` is part of the Solana SDK library, and it is used to retrieve the next account info from the iterator `accounts_iter`. It is not inherently vulnerable itself, but rather a utility function implementing safe iteration over account information. 498 | 499 | Since the provided code does not demonstrate any direct security vulnerability or insecure coding practice, the flagged vulnerability can be ignored. 500 | 501 | No fix is required for this false positive.
RUST004Concurrency Issues and Data Races🔴 GPT 3.5Hightest-code2/program/src/checked.rsImproper handling of threads and synchronization can lead to data races, deadlocks, and other concurrency issues.The vulnerability flagged by the SAST tool as 'Concurrency Issues and Data Races' at line number 56 is indeed a false positive. The line of code `std::thread::spawn(move || { ... })` represents a simple threading example and does not introduce any real concurrency issues or data races. 510 | 511 | The code snippet creates a new thread that executes the provided closure. The closure passed to `std::thread::spawn` is an anonymous function that contains a print statement. This usage of threading does not involve any shared mutable state, synchronized access, or any other code that could potentially introduce concurrency issues or data races. 512 | 513 | Since this is a false positive, no fix is required for this particular vulnerability.
RUST007Missing Boundary Checks🔴 GPT 3.5Mediumtest-code2/program/src/lib.rsAccessing arrays or vectors without boundary checks can lead to panics or buffer overflows.The flagged vulnerability of 'Missing Boundary Checks' at line number 31 is a false positive. The line of code `[AccountInfo]` is not a vulnerability, but rather a comment indicating the type of the `accounts` parameter. 522 | 523 | There is no vulnerability in the provided code.
SOL030Log Injection🔴 GPT 3.5Mediumtest-code2/program/src/checked.rsInjection vulnerabilities in program logs can lead to misleading or harmful information being logged.The detected vulnerability titled 'Log Injection' with severity 'Medium' in the Rust code is a false positive. The line of code flagged is `msg!("{}", user_input)`. However, this line of code is not vulnerable to log injection because the `user_input` variable is a controlled input and does not originate from an untrusted source. Therefore, it does not pose a risk of log injection vulnerability. 532 | 533 | No fix is required for this false positive.
RUST007Missing Boundary Checks🔴 GPT 3.5Mediumtest-code2/program/src/checked.rsAccessing arrays or vectors without boundary checks can lead to panics or buffer overflows.The flagged vulnerability 'Missing Boundary Checks' at line number 14 is a false positive. The line of code ` [AccountInfo]` is not a vulnerability, but rather a section header in the code file used to import the `AccountInfo` struct from the `solana_program` library. 542 | 543 | Therefore, no fix is required for this false positive vulnerability.
RUST007Missing Boundary Checks🔴 GPT 3.5Mediumtest-code2/client/src/utils.rsAccessing arrays or vectors without boundary checks can lead to panics or buffer overflows.Based on the information provided, the flagged line of code "[u8]" does not pose a vulnerability. It is a type annotation indicating that a variable or function is expected to return a slice of u8 (bytes). 552 | 553 | Therefore, the flagged vulnerability is a false positive, and no fix is required for the code snippet provided.
RUST009Blocking I/O in Asynchronous Code🔴 GPT 3.5Mediumtest-code2/client/src/utils.rsPerforming blocking I/O operations in async contexts can lead to thread starvation and reduced scalability.Based on the provided information and code context, the vulnerability titled 'Blocking I/O in Asynchronous Code' at line number 30 (`std::fs`) appears to be a false positive. 562 | 563 | In the given code, the `std::fs` module is not used for performing blocking I/O operations. Instead, it is used in the function `get_config()` to read the Solana yaml config file synchronously. Since blocking I/O operations are acceptable in this scenario, the flagged vulnerability is not valid. 564 | 565 | No fix is necessary for this false positive. However, it is always recommended to review the code and ensure that blocking I/O operations are used appropriately in asynchronous code when avoidable.
568 |
569 |
570 |

Safe Patterns Overview

571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 |
Pattern IDTitleSafe Pattern
VULN010Account Owner or Program ID Checkif\s+\w+\.owner\s*!=
VULN002Loss of Precision\.try_floor_u64\(\s*\)
587 |
588 | 637 | 638 | -------------------------------------------------------------------------------- /etc/L3X_SAST_Report_Rust.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vulnerability Report 7 | 8 | 41 | 42 | 43 |
44 |

L3X - Static Application Security Testing (SAST) Report

45 |

Technology: Rust

46 |

Check more on: VulnPlanet
Contribute: GitHub

47 |
48 |
49 |

Summary

50 |
51 |

By Severity

52 | 53 |
54 |
55 |

False Positive Rate

56 | 57 |
58 |
59 |
60 |

Vulnerabilities

61 |

🟢 GPT 3.5 - Valid or Not possible to determine

62 |

🔴 GPT 3.5 - False Positive

63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 200 | 201 |
IDTitleStatusSeverityFileDescriptionDetails
RUST012[Rust-General] Integer Overflow🟢 GPT 3.5Hightestcode/test.rsArithmetic operations that may overflow, leading to incorrect calculations or vulnerabilities.This is a valid vulnerability. The flagged code `c = a + b;` performs an addition operation without checking for integer overflow. In Rust, integer overflow is handled differently in debug and release modes. In debug mode, an overflow will cause a panic, whereas in release mode, the result will wrap around. 81 | 82 | To fix this vulnerability, you can use the checked arithmetic operations provided by Rust. The checked addition operation returns an `Option` that contains the sum if no overflow occurs, or `None` if an overflow occurs. You can use pattern matching to handle the result of the addition operation. Here's an example fix: 83 | 84 | ```rust 85 | fn arithmetic_overflow_examples() { 86 | let a: u8 = 255; 87 | let b: u8 = 1; 88 | let c = a.checked_add(b); 89 | 90 | match c { 91 | Some(result) => println!("No overflow: {}", result), 92 | None => println!("Overflow occurred"), 93 | } 94 | 95 | let large_num = std::u32::MAX; 96 | let another_large_num = 1; 97 | let result = large_num.checked_mul(another_large_num); 98 | 99 | match result { 100 | Some(result) => println!("No overflow: {}", result), 101 | None => println!("Overflow occurred"), 102 | } 103 | } 104 | ``` 105 | 106 | By using the checked addition and checked multiplication operations, you can detect overflow and handle it accordingly.
RUST020[Rust-General] Unsafe Dereferencing🟢 GPT 3.5Hightestcode/test.rsDereferencing pointers without ensuring they point to valid memory, leading to potential segmentation faults or undefined behavior.This is a valid vulnerability detected by the SAST tool. The vulnerability is titled '[Rust-General] Unsafe Dereferencing' and has a severity level of 'High'. 115 | 116 | The line of code flagged is: 117 | 118 | ```rust 119 | println!("Dereferencing a raw pointer: {}", *y); 120 | ``` 121 | 122 | This vulnerability occurs because `y` is a dangling pointer, which means it refers to memory that has been deallocated. Dereferencing such a pointer can lead to undefined behavior, potentially causing crashes or security vulnerabilities. 123 | 124 | To fix this vulnerability, you can avoid dereferencing the dangling pointer. You should make sure that the pointer is pointing to a valid memory location before dereferencing it. One way to ensure this is by using `std::ptr::null` or `std::ptr::null_mut` to represent a null pointer in situations where the value may become deallocated. Then, before dereferencing the pointer, you can check if it is null using an `if` statement to avoid undefined behavior. 125 | 126 | Here's an example of how you can modify the code to avoid the vulnerability: 127 | 128 | ```rust 129 | // RUST020 - Unsafe Dereferencing 130 | if !y.is_null() { 131 | println!("Dereferencing a raw pointer: {}", *y); 132 | } 133 | ``` 134 | 135 | By performing the null check, you prevent the program from attempting to dereference a dangling pointer and avoid the potential vulnerability.
RUST006[Rust-General] Memory Leaks🟢 GPT 3.5Mediumtestcode/test.rsImproper use of memory allocation that could result in memory leaks.This is a valid vulnerability. The potential Rust vulnerability titled '[Rust-General] Memory Leaks' flagged by the SAST tool is valid because it identifies a memory leak issue. 144 | 145 | The flagged line of code is: 146 | ``` 147 | Box::new( 148 | ``` 149 | The potential memory leak occurs when a Box is created but not assigned to any variable. This results in a memory allocation that is never freed. To fix this issue, you should assign the Box to a variable or drop it immediately if it is not needed. 150 | 151 | Here's an updated version of the code with the fix: 152 | ```rust 153 | use std::mem; 154 | use std::ptr; 155 | 156 | fn main() { 157 | unsafe_code_examples(); 158 | arithmetic_overflow_examples(); 159 | } 160 | 161 | unsafe fn unsafe_code_examples() { 162 | // RUST011 - Use After Free 163 | let x = Box::new(42); 164 | let y = Box::into_raw(x); // Allocation is freed here, y is now a dangling pointer 165 | drop(Box::from_raw(y)); // Use after free 166 | 167 | // RUST020 - Unsafe Dereferencing 168 | println!("Dereferencing a raw pointer: {}", *y); 169 | 170 | // RUST003 - Dangling Pointers 171 | let z: *const i32; 172 | { 173 | let temp_var = 33; 174 | z = &temp_var as *const i32; // z becomes a dangling pointer here 175 | } 176 | println!("Dangling pointer access: {:?}", z); 177 | 178 | // Directly manipulating memory without proper checks can lead to undefined behavior 179 | let mut array = [1, 2, 3, 4, 5]; 180 | let array_ptr: *mut i32 = array.as_mut_ptr(); 181 | ptr::write(array_ptr.offset(5), 999); // RUST004 - Buffer Overflows 182 | } 183 | 184 | fn arithmetic_overflow_examples() { 185 | // RUST012 - Integer Overflow 186 | let a: u8 = 255; 187 | let b: u8 = 1; 188 | let c = a + b; // This will overflow in debug mode, or wrap in release mode 189 | println!("Overflow example: {}", c); 190 | 191 | // Demonstrating unchecked arithmetic operations 192 | let large_num = std::u32::MAX; 193 | let another_large_num = 1; 194 | let result = large_num * another_large_num; // Potential for RUST012 - Integer Overflow 195 | println!("Unchecked multiplication might overflow: {}", result); 196 | } 197 | ``` 198 | 199 | In this fix, the Box is assigned to the variable `x` to ensure ownership and proper memory management.
202 |
203 |
204 |

Safe Patterns Overview

205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 |
Pattern IDTitleSafe Pattern
213 |
214 | 263 | 264 | -------------------------------------------------------------------------------- /etc/Solidity-Ethereum_L3X_SAST_Report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vulnerability Report 7 | 8 | 41 | 42 | 43 |
44 |

L3X - Static Application Security Testing (SAST) Report

45 |

Technology: Solidity-Ethereum

46 |

Check more on: VulnPlanet
Contribute: GitHub

47 |
48 |
49 |

Summary

50 |
51 |

By Severity

52 | 53 |
54 |
55 |

False Positive Rate

56 | 57 |
58 |
59 |
60 |

Vulnerabilities

61 |

🟢 GPT 3.5 - Valid or Not possible to determine

62 |

🔴 GPT 3.5 - False Positive

63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 187 | 188 |
IDTitleStatusSeverityFileDescriptionDetails
SOLIDITY023Reentrancy Vulnerabilities🟢 GPT 3.5High/Users/ym/Downloads/not-so-smart-contracts-master/reentrancy/Reentrancy.solDetection of the reentrancy bug involving Ether.This is a valid vulnerability known as a reentrancy vulnerability. In the "withdrawBalance" function, the line of code flagged is `if( ! (msg.sender.call.value(userBalance[msg.sender])() ) )`. This line of code allows for a potential attack where an attacker can repeatedly call the "withdrawBalance" function before the state variable `userBalance[msg.sender]` is updated, causing the attacker to receive multiple withdrawals from the contract. 81 | 82 | To fix this vulnerability, you can follow the approach used in the "withdrawBalance_fixed" function. The fix involves changing the state variable `userBalance[msg.sender]` before making the external call. Here's the modified code: 83 | 84 | ```solidity 85 | function withdrawBalance(){ 86 | uint amount = userBalance[msg.sender]; 87 | userBalance[msg.sender] = 0; 88 | if( ! (msg.sender.call.value(amount)() ) ){ 89 | throw; 90 | } 91 | } 92 | ``` 93 | 94 | By changing the state variable before the external call, you prevent the reentrancy attack by ensuring that subsequent calls to the "withdrawBalance" function will only withdraw zero balances.
SOLIDITY026Unchecked Transfer🟢 GPT 3.5High/Users/ym/Downloads/not-so-smart-contracts-master/reentrancy/Reentrancy.solThe return value of an external transfer/transferFrom call is not checked.This is a valid vulnerability known as "Unchecked Transfer" in Solidity. The vulnerability occurs at line number 38, which is: 103 | 104 | .transfer(userBalance[msg.sender]); 105 | 106 | The problem with this line is that it uses the .transfer() function, which is prone to reentrancy attacks. An attacker may call a malicious contract's fallback function repeatedly, causing the transfer to be executed multiple times and potentially draining the contract's balance. 107 | 108 | To fix this vulnerability, you should consider using the recommended withdrawal pattern. One possible fix is shown in the code below: 109 | 110 | function withdrawBalance_fixed() { 111 | uint amount = userBalance[msg.sender]; 112 | require(amount > 0); // Check if the user has a positive balance 113 | userBalance[msg.sender] = 0; 114 | msg.sender.transfer(amount); 115 | } 116 | 117 | In this fix, we first check if the user has a positive balance before proceeding with the transfer. This prevents the reentrancy attack since the user's balance is set to zero before the transfer occurs. Additionally, we use the .transfer() function instead of .call.value() to avoid potential reentrancy. 118 | 119 | By making this change, you can mitigate the potential vulnerability and ensure that withdrawals are done securely.
SOLIDITY023Reentrancy Vulnerabilities🟢 GPT 3.5High/Users/ym/Downloads/not-so-smart-contracts-master/reentrancy/ReentrancyExploit.solDetection of the reentrancy bug involving Ether.This is a valid vulnerability known as "Reentrancy Vulnerabilities." The vulnerability allows an attacker to maliciously re-enter a function before the previous execution is completed, leading to unexpected behavior and potential security breaches. 128 | 129 | To fix this vulnerability, you should implement the "checks-effects-interactions" pattern. In the susceptible code, the line flagged is: 130 | 131 | ``` 132 | require(vulnerable_contract.call.value(msg.value)(bytes4(sha3("addToBalance()")))); 133 | ``` 134 | 135 | To address this vulnerability, move the interaction with the vulnerable contract to the end of the function after all checks have been performed. Here's an updated version: 136 | 137 | ``` 138 | function deposit(address _vulnerable_contract) public payable { 139 | vulnerable_contract = _vulnerable_contract; 140 | // perform necessary checks first 141 | require(vulnerable_contract.call.value(msg.value)(bytes4(sha3("addToBalance()")))); 142 | } 143 | ``` 144 | 145 | By rearranging the code in this way, you ensure that the vulnerable contract is called after all other operations are completed, minimizing the risk of reentrancy attacks.
SOLIDITY045Unchecked Send🟢 GPT 3.5Medium/Users/ym/Downloads/not-so-smart-contracts-master/reentrancy/theRun.solThe return value of a send is not checked.This is a valid vulnerability. 154 | 155 | The code uses the `.send()` method to transfer Ether to addresses stored in the `players` array without checking the return value. This can lead to potential vulnerabilities because the `.send()` method does not throw an exception when the transfer fails and instead returns a boolean value indicating success or failure. 156 | 157 | To fix this vulnerability, you should use the `.transfer()` method instead of `.send()`. The `.transfer()` method is a safeguarded way to transfer Ether that automatically throws an exception if the transfer fails, reverting any changes made during the execution of the contract. You should replace the line of code flagged (`players[Payout_id].addr.send(Last_Payout);`) with `players[Payout_id].addr.transfer(Last_Payout);`. 158 | 159 | Here is the updated line of code: 160 | 161 | ``` 162 | players[Payout_id].addr.transfer(Last_Payout); 163 | ``` 164 | 165 | By using `.transfer()` instead of `.send()`, you ensure that any failed transfers will be immediately reverted, preventing potential vulnerabilities.
SOLIDITY034Contracts that Lock Ether🟢 GPT 3.5Medium/Users/ym/Downloads/not-so-smart-contracts-master/reentrancy/Reentrancy.solContract with a payable function, but without a withdrawal capacity.This is a valid vulnerability in the given code. 174 | 175 | The issue is that the function `addToBalance()` allows Ether to be added to a user's balance without any limit. This can be problematic if an external attacker or malicious contract repeatedly calls this function to lock up the contract's balance. This vulnerability is commonly referred to as the "Contracts that Lock Ether" vulnerability. 176 | 177 | To fix this vulnerability, you should implement a check within the `addToBalance()` function to limit the amount of Ether that can be added per transaction. For example, you can add a modifier that checks the value of `msg.value` and throws an exception if it exceeds a certain limit: 178 | 179 | ```solidity 180 | function addToBalance() payable { 181 | require(msg.value <= 1 ether); // Limit the amount of Ether that can be added 182 | userBalance[msg.sender] += msg.value; 183 | } 184 | ``` 185 | 186 | By limiting the amount of Ether that can be added, you prevent the contract's balance from being locked up by an attacker.
189 |
190 |
191 |

Safe Patterns Overview

192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 |
Pattern IDTitleSafe Pattern
200 |
201 | 250 | 251 | -------------------------------------------------------------------------------- /etc/de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-trust/static-analyzer/f1d1c344b6634b01f6e7206d8ef392e57305fef8/etc/de.png -------------------------------------------------------------------------------- /etc/exec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-trust/static-analyzer/f1d1c344b6634b01f6e7206d8ef392e57305fef8/etc/exec.png -------------------------------------------------------------------------------- /etc/patterns-eth-solidity.md: -------------------------------------------------------------------------------- 1 | | ID | Title | Severity | Description | Suggested Fix | 2 | |---------------|-----------------------------------------------|----------|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------| 3 | | SOLIDITY001 | Storage ABIEncoderV2 Array | High | solc versions 0.4.7-0.5.9 contain a compiler bug leading to incorrect ABI encoder usage. | Use a compiler >= 0.5.10. | 4 | | SOLIDITY002 | Arbitrary from in transferFrom | High | Detects when msg.sender is not used as from in transferFrom. | Use msg.sender as from in transferFrom. | 5 | | SOLIDITY003 | Modifying storage array by value | High | Detect arrays passed to a function that expects reference to a storage array. | Ensure the correct usage of memory and storage in the function parameters. | 6 | | SOLIDITY004 | ABI encodePacked Collision | High | Detect collision due to dynamic type usages in abi.encodePacked. | Do not use more than one dynamic type in abi.encodePacked(). Use abi.encode(), preferably. | 7 | | SOLIDITY005 | Incorrect shift in assembly | High | Detects if the values in a shift operation are reversed. | Swap the order of parameters. | 8 | | SOLIDITY006 | Multiple constructor schemes | High | Detect multiple constructor definitions in the same contract. | Only declare one constructor, using the new scheme constructor(...) instead of function (...). | 9 | | SOLIDITY008 | Public mappings with nested variables | High | Public mapping with nested structures returned incorrect values prior to Solidity 0.5. | Do not use public mapping with nested structures. | 10 | | SOLIDITY009 | RTLO Character | High | Use of right-to-left-override character (U+202E) can manipulate contract logic. | Special control characters must not be allowed. | 11 | | SOLIDITY010 | State Variable Shadowing | High | Detection of state variables shadowed. | Remove the state variable shadowing. | 12 | | SOLIDITY011 | Suicidal | High | Unprotected call to a function executing selfdestruct/suicide. | Protect access to all sensitive functions. | 13 | | SOLIDITY012 | Uninitialized State Variables | High | Uninitialized state variables. | Initialize all the variables. If a variable is meant to be initialized to zero, explicitly set it to zero. | 14 | | SOLIDITY013 | Uninitialized Storage Variables | High | An uninitialized storage variable will act as a reference to the first state variable, and can override it. | Initialize all storage variables. | 15 | | SOLIDITY014 | Unprotected Upgradeable Contract | High | Detects logic contract that can be destructed. | Add a constructor to ensure initialize cannot be called on the logic contract. | 16 | | SOLIDITY015 | Arbitrary from in TransferFrom Used with Permit | High | Detects when msg.sender is not used as from in transferFrom and permit is used. | Ensure that the underlying ERC20 token correctly implements a permit function. | 17 | | SOLIDITY016 | Functions that Send Ether to Arbitrary Destinations | High | Unprotected call to a function sending Ether to an arbitrary address. | Ensure that an arbitrary user cannot withdraw unauthorized funds. | 18 | | SOLIDITY017 | Array Length Assignment | High | Detects the direct assignment of an array's length. | Do not allow array lengths to be directly set; add values as needed. | 19 | | SOLIDITY018 | Controlled Delegatecall | High | Delegatecall or callcode to an address controlled by the user. | Avoid using delegatecall. Use only trusted destinations. | 20 | | SOLIDITY019 | Payable Functions Using Delegatecall Inside a Loop | High | Detect the use of delegatecall inside a loop in a payable function. | Carefully check that the function called by delegatecall is not payable. | 21 | | SOLIDITY020 | Incorrect Exponentiation | High | Detect use of bitwise xor ^ instead of exponential **. | Use the correct operator ** for exponentiation. | 22 | | SOLIDITY021 | Incorrect Return in Assembly | High | Detect if return in an assembly block halts unexpectedly the execution. | Use the leave statement. | 23 | | SOLIDITY022 | msg.value Inside a Loop | High | Detect the use of msg.value inside a loop. | Provide explicit amounts alongside the receivers array, matching msg.value. | 24 | | SOLIDITY023 | Reentrancy Vulnerabilities | High | Detection of the reentrancy bug involving Ether. | Apply the check-effects-interactions pattern. | 25 | | SOLIDITY024 | Return Instead of Leave in Assembly | High | Detect if a return is used where a leave should be used. | Use the leave statement. | 26 | | SOLIDITY025 | Storage Signed Integer Array | High | solc versions 0.4.7-0.5.9 contain a compiler bug leading to incorrect values in signed integer arrays. | Use a compiler version >= 0.5.10. | 27 | | SOLIDITY026 | Unchecked Transfer | High | The return value of an external transfer/transferFrom call is not checked. | Use SafeERC20, or ensure that the transfer/transferFrom return value is checked. | 28 | | SOLIDITY027 | Weak PRNG | High | Weak PRNG due to a modulo on block.timestamp, now, or blockhash. | Do not use block.timestamp, now, or blockhash as a source of randomness. | 29 | | SOLIDITY029 | Domain Separator Collision | Medium | An ERC20 token function signature collides with EIP-2612's DOMAIN_SEPARATOR(). | Remove or rename the function that collides with DOMAIN_SEPARATOR(). | 30 | | SOLIDITY030 | Dangerous Enum Conversion | Medium | Detect out-of-range enum conversion. | Check the enum conversion range. | 31 | | SOLIDITY031 | Incorrect ERC20 Interface | Medium | Incorrect return values for ERC20 functions. | Set the appropriate return values for ERC20 functions. | 32 | | SOLIDITY032 | Incorrect ERC721 Interface | Medium | Incorrect return values for ERC721 functions. | Set the appropriate return values for ERC721 functions. | 33 | | SOLIDITY033 | Dangerous Strict Equalities | Medium | Use of strict equalities that can be easily manipulated by an attacker. | Avoid using strict equality for Ether or token amounts. | 34 | | SOLIDITY034 | Contracts that Lock Ether | Medium | Contract with a payable function, but without a withdrawal capacity. | Add a withdrawal function or remove the payable attribute. | 35 | | SOLIDITY035 | Deletion on Mapping Containing a Structure | Medium | A deletion in a structure containing a mapping will not delete the mapping. | Use a lock mechanism instead of a deletion for structures containing a mapping. | 36 | | SOLIDITY036 | State Variable Shadowing from Abstract Contracts | Medium | Detection of state variables shadowed from abstract contracts. | Remove the state variable shadowing. | 37 | | SOLIDITY037 | Misuse of a Boolean Constant | Medium | Detects the misuse of a Boolean constant. | Verify and simplify the condition. | 38 | | SOLIDITY040 | Divide Before Multiply | Medium | Performing division before multiplication can lead to precision loss. | Consider ordering multiplication before division. | 39 | | SOLIDITY041 | Reentrancy Vulnerabilities (No Ether) | Medium | Detection of reentrancy bug that does not involve Ether. | Apply the check-effects-interactions pattern. | 40 | | SOLIDITY042 | Reused Base Constructors | Medium | Detects if the same base constructor is called with arguments from two different locations. | Remove the duplicate constructor call. | 41 | | SOLIDITY043 | Dangerous Usage of tx.origin | Medium | tx.origin-based protection can be abused by a malicious contract. | Do not use tx.origin for authorization. | 42 | | SOLIDITY044 | Unchecked Low-Level Calls | Medium | The return value of a low-level call is not checked. | Ensure that the return value of a low-level call is checked or logged. | 43 | | SOLIDITY045 | Unchecked Send | Medium | The return value of a send is not checked. | Ensure that the return value of send is checked or logged. | 44 | | SOLIDITY047 | Return Bomb | Low | A low-level callee may consume all caller's gas unexpectedly. | Avoid unlimited implicit decoding of returndata. | 45 | | SOLIDITY048 | Block Timestamp | Low | Dangerous usage of block.timestamp, which can be manipulated by miners. | Avoid relying on block.timestamp for critical logic or randomness. | 46 | -------------------------------------------------------------------------------- /etc/patterns-rust.md: -------------------------------------------------------------------------------- 1 | | ID | Language | Title | Severity | Description | Suggested Fix | Context | 2 | |--------|----------|------------------------------------------|----------|---------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------| 3 | | RUST001| Rust | [Rust] Unsafe code block | High | Unsafe code blocks can lead to undefined behavior if not used properly. | Ensure that unsafe code is necessary and properly reviewed. Use safe abstractions when possible. | Whenever using unsafe blocks in Rust code | 4 | | RUST002| Rust | [Rust] Unhandled error | Medium | Unwrapping a Result or Option without proper error handling can lead to panic. | Use match or if let to handle the Result or Option properly, or use ? to propagate the error. | When using unwrap() on Result or Option types | 5 | | RUST003| Rust | [Rust] Unchecked arithmetic | Medium | Arithmetic operations that can overflow or underflow without being checked. | Use checked arithmetic methods like checked_add, checked_sub, checked_mul, and checked_div. | When performing arithmetic operations that may overflow or underflow | 6 | | RUST004| Rust | [Rust] Insecure random number generator| High | Using an insecure random number generator for security-sensitive operations. | Use a cryptographically secure random number generator like rand::ThreadRng or ring::rand::SystemRandom. | When generating random numbers for security-sensitive purposes | 7 | | RUST005| Rust | [Rust] Uninitialized memory | High | Using uninitialized memory can lead to undefined behavior. | Initialize memory properly or use mem::MaybeUninit for delayed initialization. | When working with uninitialized memory | 8 | | RUST006| Rust | [Rust] Use of mem::transmute | High | Using mem::transmute can lead to undefined behavior and violate type safety. | Avoid using mem::transmute and use safe type conversions or as keyword for primitive types. | When using mem::transmute to reinterpret memory | 9 | | RUST007| Rust | [Rust] Use of std::process::Command | Medium | Using std::process::Command without properly sanitizing user input can lead to command injection vulnerabilities. | Properly sanitize and validate user input before passing it to std::process::Command. Consider using safe wrappers or libraries. | When executing external commands or processes | 10 | | RUST008| Rust | [Rust] Use of std::fs::File with unwrap() | Medium | Using unwrap() with std::fs::File can lead to panics if the file operation fails. | Use ? operator to propagate the error or handle it explicitly with match or if let. | When opening files using std::fs::File | 11 | | RUST009| Rust | [Rust] Deserialization of untrusted data | High | Deserializing untrusted data without proper validation can lead to security vulnerabilities. | Implement custom deserialization logic with proper validation and sanitization of untrusted data. Consider using safe deserialization libraries. | When deserializing data from untrusted sources using serde or other libraries | 12 | | RUST010| Rust | [Rust] Use of std::net::TcpListener with unwrap() | Medium | Using unwrap() with std::net::TcpListener can lead to panics if the binding operation fails. | Use ? operator to propagate the error or handle it explicitly with match or if let. | When binding to a network address using std::net::TcpListener'. From the beginning to end! | 13 | -------------------------------------------------------------------------------- /etc/patterns-sol.md: -------------------------------------------------------------------------------- 1 | | ID | Title | Severity | Description | Fix | 2 | |-----------|-------------------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 3 | | RUST001 | Unsafe code block | High | Unsafe code blocks can lead to undefined behavior if not used properly. | Ensure that unsafe code is necessary and properly reviewed. Use safe abstractions when possible. | 4 | | RUST002 | Unhandled error | Medium | Unwrapping a Result or Option without proper error handling can lead to panic. | Use match or if let to handle the Result or Option properly, or use `?` to propagate the error. | 5 | | RUST003 | Unchecked arithmetic | Medium | Arithmetic operations that can overflow or underflow without being checked. | Use checked arithmetic methods like `checked_add`, `checked_sub`, `checked_mul`, and `checked_div`. | 6 | | RUST004 | Insecure random number generator | High | Using an insecure random number generator for security-sensitive operations. | Use a cryptographically secure random number generator like `rand::ThreadRng` or `ring::rand::SystemRandom`. | 7 | | RUST005 | Uninitialized memory | High | Using uninitialized memory can lead to undefined behavior. | Initialize memory properly or use `mem::MaybeUninit` for delayed initialization. | 8 | | RUST006 | Use of mem::transmute | High | Using `mem::transmute` can lead to undefined behavior and violate type safety. | Avoid using `mem::transmute` and use safe type conversions or `as` keyword for primitive types. | 9 | | RUST007 | Use of std::process::Command | Medium | Using `std::process::Command` without properly sanitizing user input can lead to command injection vulnerabilities. | Properly sanitize and validate user input before passing it to `std::process::Command`. Consider using safe wrappers or libraries. | 10 | | RUST008 | Use of std::fs::File with unwrap() | Medium | Using `unwrap()` with `std::fs::File` can lead to panics if the file operation fails. | Use `?` operator to propagate the error or handle it explicitly with match or if let. | 11 | | RUST009 | Deserialization of untrusted data | High | Deserializing untrusted data without proper validation can lead to security vulnerabilities. | Implement custom deserialization logic with proper validation and sanitization of untrusted data. Consider using safe deserialization libraries. | 12 | | RUST010 | Use of std::net::TcpListener with unwrap() | Medium | Using `unwrap()` with `std::net::TcpListener` can lead to panics if the binding operation fails. | Use `?` operator to propagate the error or handle it explicitly with match or if let. | 13 | | VULN001 | Integer Overflow or Underflow | High | Performing arithmetic operation without checking for overflow or underflow. | Use `checked_add`, `checked_sub`, `checked_mul`, or `checked_div` to safely perform arithmetic operations. | 14 | | VULN002 | Loss of Precision | High | The use of try_round_u64() for rounding up may lead to loss of precision. | Use try_floor_u64() to prevent potential loss of precision. | 15 | | VULN003 | Inaccurate Calculation Results | High | Reliance on saturating arithmetic operations without considering precision loss. | Consider using `checked_add`, `checked_sub`, `checked_mul`, or `checked_div` to handle arithmetic operations explicitly and avoid precision loss. | 16 | | VULN007 | Missing Check for the Permission of Caller | Low | Missing verification of caller permissions before sensitive operations. | Implement and invoke a permission check function to verify the caller's authority. | 17 | | VULN008 | Account Signer Check | High | Ensure the expected signer account has actually signed to prevent unauthorized account modifications. | Verify `is_signer` is true for transactions requiring signatures. | 18 | | VULN009 | Account Writable Check | High | Ensure state accounts are checked as writable to prevent unauthorized modifications. | Verify `is_writable` is true for accounts that should be modified. | 19 | | VULN010 | Account Owner or Program ID Check | High | Verify the owner of state accounts to prevent fake data injection by malicious programs. | Check the account's owner matches the expected program ID. | 20 | | VULN011 | Account Initialized Check | High | Prevent re-initialization of already initialized accounts. | Ensure account's `is_initialized` flag is checked before initializing. | 21 | | VULN017 | Signer Authorization - Anchor | High | Signer check is missing, which could lead to unauthorized execution. | Add a check to verify if the caller is a signer. | 22 | | VULN018 | Account Data Matching - Anchor | High | Missing verification of token ownership or mint authority in SPL Token accounts. | Verify token ownership matches the expected authority before proceeding. | 23 | | VULN019 | Owner Checks - Anchor | High | Missing checks on the owner field in the metadata of an Account or on the Account itself. | Ensure the owner of the account is verified against the expected program ID. | 24 | | VULN020 | Type Cosplay - Anchor | High | Risks of different accounts impersonating each other by sharing identical data structures. | Add discriminant checks to differentiate account types securely. | 25 | | VULN021 | Check Initialize - Anchor | High | Data should only be initialized once; missing checks can lead to reinitialization. | Use a flag to ensure data is initialized only once. | 26 | | VULN022 | Arbitrary CPI - Anchor | High | Unverified target program id in CPI can lead to arbitrary code execution. | Ensure the target program id is verified against expected program id. | 27 | | VULN023 | Duplicate Mutable Accounts - Anchor | High | Passing the same mutable account multiple times may result in unintended data overwriting. | Add checks to ensure that mutable accounts passed are distinct. | 28 | | VULN024 | Bump Seed Canonicalization - Anchor | High | Improper validation of bump seeds can lead to security vulnerabilities. | Use find_program_address for bump seed canonicalization and validate against expected seeds. | 29 | | VULN025 | PDA Sharing - Anchor | High | Sharing PDA across multiple roles without proper permission separation may lead to unauthorized access. | Ensure PDAs used across roles have distinct seeds and permissions. | 30 | | VULN026 | Closing Accounts - Anchor | High | Improper closing of accounts may leave them vulnerable to misuse. | Ensure accounts are properly closed by transferring lamports and marking with a discriminator. | 31 | | VULN027 | Sysvar System Account Not Checked | High | Sysvar system account is accessed without verifying its legitimacy, exposing the contract to potential manipulation or attacks. | Before deserializing information from a sysvar account, verify that the incoming address matches the expected sysvar ID. | 32 | | VULN028 | PDA Account Misuse Without Proper Verification | High | The PDA account is utilized without validating the caller's and beneficiary's accounts, allowing unauthorized actions. | Implement checks to verify the depositor's signature and ensure the deposit_account cannot be forged. | 33 | | VULN029 | Unchecked Account Deserialization | High | Failing to check if an account is of the expected type before deserializing can lead to incorrect assumptions about state. | Ensure accounts are of the expected type before deserialization. | 34 | | VULN031 | CPI to Unauthorized Programs | High | Invoking unauthorized or risky external programs can expose the contract to vulnerabilities present in those programs. | Whitelist external programs that can be invoked, and perform thorough security reviews on them. | 35 | | Rust-Solana001 | Misuse of Unsafe Code | High | Unsafe blocks may lead to undefined behavior and memory safety violations if not used carefully. | Minimize the use of unsafe by leveraging safe Rust-Solana abstractions and validate all unsafe blocks for safety guarantees. | 36 | | Rust-Solana002 | Improper Error Handling | Medium | Overuse of unwrap() or expect() can lead to panics. | Prefer using error handling mechanisms like match or if let. Replace unwrap() and expect() with proper error handling to prevent unexpected panics in production code. | 37 | | Rust-Solana003 | Overuse of Panics for Control Flow | Medium | Using panics for control flow makes code hard to follow and can lead to unexpected termination. | Use Result types for error handling and reserve panics for unrecoverable errors only. | 38 | | Rust-Solana004 | Concurrency Issues and Data Races | High | Improper handling of threads and synchronization can lead to data races, deadlocks, and other concurrency issues. | Use Rust-Solana's concurrency primitives correctly, prefer std::sync module's types like Mutex, RwLock, and leverage the rayon crate for data parallelism. | 39 | | Rust-Solana005 | Potential Memory Leaks | Low | Cyclic references or improper use of smart pointers can lead to memory leaks. | Use Weak pointers to break cycles and audit memory usage regularly. | 40 | | Rust-Solana006 | Potential DoS Vulnerabilities | High | Allocations based on untrusted input sizes can lead to DoS via memory exhaustion. | Validate input sizes before allocations and use bounded collections. Consider rate-limiting or other mitigation strategies. | 41 | | Rust-Solana007 | Missing Boundary Checks | Medium | Accessing arrays or vectors without boundary checks can lead to panics or buffer overflows. | Use .get() or .get_mut() for safe access with bounds checking, and handle the Option result appropriately. | 42 | | Rust-Solana008 | Unnecessary Cloning of Large Data Structures | Low | Cloning large data structures can lead to performance issues due to excessive memory use. | Prefer borrowing or using reference-counted types like Rc or Arc to share data without deep copying. | 43 | | Rust-Solana009 | Blocking I/O in Asynchronous Code | Medium | Performing blocking I/O operations in async contexts can lead to thread starvation and reduced scalability. | Use asynchronous equivalents for file and network operations within async functions. | 44 | | Rust-Solana010 | Misuse of Arc> | Medium | Incorrect use of Arc> can lead to deadlocks or inefficient locking mechanisms. | Ensure that locks are held for the minimum duration necessary, and consider other synchronization primitives like RwLock if applicable. | 45 | | Rust-Solana011 | Improper Implementation of Drop Trait | Medium | Incorrect custom implementations of the Drop trait can lead to resource leaks or panic safety issues. | Implement the Drop trait carefully, ensuring that errors are handled gracefully and resources are properly released. | 46 | | Rust-Solana012 | Usage of mem::uninitialized and mem::zeroed | High | Using mem::uninitialized or mem::zeroed can lead to undefined behavior if the type has any non-zero or complex initialization requirements. | Prefer using safe initialization patterns and avoid these functions for types with non-trivial initialization requirements. | 47 | -------------------------------------------------------------------------------- /etc/report.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-trust/static-analyzer/f1d1c344b6634b01f6e7206d8ef392e57305fef8/etc/report.gif -------------------------------------------------------------------------------- /l3x/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | -------------------------------------------------------------------------------- /l3x/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "l3x" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde = { version = "1.0", features = ["derive"] } 8 | serde_json = "1.0" 9 | reqwest = { version = "0.11", features = ["json"] } 10 | tokio = { version = "1", features = ["full"] } 11 | regex = "1" 12 | colored = "2.0" 13 | walkdir = "2.3" 14 | clap = "3.0" 15 | itertools = "0.10.1" -------------------------------------------------------------------------------- /l3x/src/gpt_validator.rs: -------------------------------------------------------------------------------- 1 | use reqwest::Client; 2 | use serde::{Deserialize, Serialize}; 3 | use std::error::Error; 4 | 5 | pub struct OpenAICreds { 6 | pub api_key: String, 7 | pub org_id: Option, 8 | pub project_id: Option, 9 | } 10 | 11 | #[derive(Serialize)] 12 | struct ChatRequest { 13 | model: String, 14 | messages: Vec, 15 | } 16 | 17 | #[derive(Serialize)] 18 | struct Message { 19 | role: String, 20 | content: String, 21 | } 22 | 23 | #[derive(Deserialize)] 24 | struct ChatResponse { 25 | choices: Vec, 26 | } 27 | 28 | #[derive(Deserialize)] 29 | struct Choice { 30 | message: MessageContent, 31 | } 32 | 33 | #[derive(Deserialize)] 34 | struct MessageContent { 35 | content: String, 36 | } 37 | 38 | pub async fn validate_vulnerabilities_with_gpt( 39 | openai_creds: &OpenAICreds, 40 | findings_by_file: &[(usize, String, String, String)], 41 | file_content: &str, 42 | language: &str, 43 | validate_all_severities: bool, 44 | model: &str, 45 | ) -> Result<(String, String), Box> { 46 | let client = Client::new(); 47 | 48 | let mut findings_list = String::new(); 49 | for (line_number, vulnerability_id, severity, _) in findings_by_file { 50 | if validate_all_severities || severity == "Critical" || severity == "High" { 51 | findings_list.push_str(&format!("line {}: {}\n", line_number, vulnerability_id)); 52 | } 53 | } 54 | 55 | let prompt = match language { 56 | "Rust" => format!( 57 | "A SAST tool detects potential Rust vulnerabilities in the following file:\n\nSource code:\n{}\n\nFindings list:\n{}\n\nAre these valid vulnerabilities or false positives? Provide an explanation.", 58 | file_content, findings_list 59 | ), 60 | "Solidity-Ethereum" => format!( 61 | "A SAST tool detects potential Solidity vulnerabilities in the following file:\n\nSource code:\n{}\n\nFindings list:\n{}\n\nAre these valid vulnerabilities or false positives? Provide an explanation.", 62 | file_content, findings_list 63 | ), 64 | _ => return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Unsupported language"))), 65 | }; 66 | 67 | let chat_request = ChatRequest { 68 | model: model.to_string(), 69 | messages: vec![Message { 70 | role: "user".to_string(), 71 | content: prompt, 72 | }], 73 | }; 74 | 75 | let mut response = client 76 | .post("https://api.openai.com/v1/chat/completions") 77 | .header("Content-Type", "application/json") 78 | .header("Authorization", format!("Bearer {}", openai_creds.api_key)); 79 | if let Some(org) = &openai_creds.org_id { 80 | response = response.header("OpenAI-Organization", org); 81 | } 82 | if let Some(project) = &openai_creds.project_id { 83 | response = response.header("OpenAI-Project", project); 84 | } 85 | let response = response.json(&chat_request).send().await?; 86 | 87 | if response.status().is_success() { 88 | let chat_response = response.json::().await?; 89 | let text = chat_response 90 | .choices 91 | .get(0) 92 | .map_or_else(|| "", |choice| &choice.message.content); 93 | 94 | let status = analyze_response_text(&text); 95 | 96 | Ok((status.to_string(), "".to_string())) 97 | } else { 98 | Err(Box::new(std::io::Error::new( 99 | std::io::ErrorKind::Other, 100 | "Failed to get a valid response from OpenAI", 101 | ))) 102 | } 103 | } 104 | 105 | fn analyze_response_text(text: &str) -> &str { 106 | if text.contains("not a vulnerability") 107 | || text.contains("is not a valid vulnerability") 108 | || text 109 | .to_lowercase() 110 | .contains("appears to be a false positive") 111 | || text.to_lowercase().contains("is no vulnerability present") 112 | || text.to_lowercase().contains("is a false positive") 113 | || text.to_lowercase().contains("likely a false positive") 114 | || text.to_lowercase().contains("may be a false positive") 115 | || text.to_lowercase().contains("seems to be a false positive") 116 | || text.to_lowercase().contains("most likely a false positive") 117 | || text 118 | .to_lowercase() 119 | .contains("does not contain a vulnerability") 120 | || text 121 | .to_lowercase() 122 | .contains("not appear to have a potential vulnerability") 123 | || text 124 | .to_lowercase() 125 | .contains("does not seem to have any obvious vulnerability") 126 | || text 127 | .to_lowercase() 128 | .contains("does not introduce a vulnerability") 129 | || text 130 | .to_lowercase() 131 | .contains("not suggest any security issues") 132 | || text 133 | .to_lowercase() 134 | .contains("does not appear to be vulnerable") 135 | || text 136 | .to_lowercase() 137 | .contains("does not appear to have any clear vulnerability") 138 | || text 139 | .to_lowercase() 140 | .contains("does not appear to have any potential vulnerability") 141 | || text.to_lowercase().contains("is not valid in this case") 142 | || text.to_lowercase().contains("does not appear to be valid") 143 | || text 144 | .to_lowercase() 145 | .contains("does not appear to contain any potential vulnerability") 146 | || text.is_empty() 147 | { 148 | "False positive" 149 | } else { 150 | "Valid" 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /l3x/src/main.rs: -------------------------------------------------------------------------------- 1 | mod gpt_validator; 2 | mod vulnerability_checks; 3 | use crate::vulnerability_checks::VulnerabilityCheck; 4 | mod report_generator; 5 | use gpt_validator::OpenAICreds; 6 | use report_generator::{ 7 | FinalReport, SafePatternDetail, SecurityAnalysisSummary, VulnerabilityResult, 8 | }; 9 | 10 | use clap::{App, Arg}; 11 | use regex::Regex; 12 | use std::{collections::HashMap, error::Error, fs}; 13 | use walkdir::WalkDir; 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<(), Box> { 17 | let matches = App::new("AI-driven Smart Contract Static Analyzer") 18 | .version("0.3") 19 | .author("YevhSec") 20 | .about("L3X detects vulnerabilities in Smart Contracts based on patterns and AI code analysis. Currently supports Solana based on Rust and Ethereum based on Solidity.") 21 | .arg(Arg::with_name("folder_path") 22 | .help("The path to the folder to scan") 23 | .required(true) 24 | .index(1)) 25 | .arg(Arg::with_name("all_severities") 26 | .long("all-severities") 27 | .help("Validate findings of all severities, not just critical and high")) 28 | .arg(Arg::with_name("model") 29 | .long("model") 30 | .value_name("MODEL") 31 | .help("OpenAI model GPT-3.5 or GPT-4 to use for vulnerability validation (default is gpt-3.5-turbo)") 32 | .takes_value(true)) 33 | .arg(Arg::with_name("no_validation") 34 | .long("no-validation") 35 | .help("Skip vulnerability validation")) 36 | .get_matches(); 37 | 38 | let folder_path = matches.value_of("folder_path").unwrap(); 39 | let openai_creds = gpt_validator::OpenAICreds { 40 | api_key: std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY must be set"), 41 | org_id: std::env::var("OPENAI_ORG_ID").ok(), 42 | project_id: std::env::var("OPENAI_PROJECT_ID").ok(), 43 | }; 44 | let validate_all_severities = matches.is_present("all_severities"); 45 | let model = matches.value_of("model").unwrap_or("gpt-3.5-turbo"); 46 | let no_validation = matches.is_present("no_validation"); 47 | 48 | let vulnerability_checks = vulnerability_checks::initialize_vulnerability_checks(); 49 | let results_by_language = analyze_folder( 50 | folder_path, 51 | &openai_creds, 52 | &vulnerability_checks[..], 53 | validate_all_severities, 54 | model, 55 | no_validation, 56 | ) 57 | .await?; 58 | 59 | for (language, (files_list, vulnerabilities_details, safe_patterns_map)) in 60 | results_by_language 61 | { 62 | let safe_patterns_overview: Vec = safe_patterns_map 63 | .into_iter() 64 | .map(|(_, detail)| detail) 65 | .collect(); 66 | 67 | let report = FinalReport { 68 | security_analysis_summary: SecurityAnalysisSummary { 69 | checked_files: files_list.len(), 70 | files_list, 71 | security_issues_found: vulnerabilities_details.len(), 72 | }, 73 | vulnerabilities_details, 74 | safe_patterns_overview, 75 | model: if no_validation { "-".to_string() } else { model.to_string() }, 76 | }; 77 | 78 | let html_content = report_generator::generate_html_report(&report, &language); 79 | fs::write(format!("{}_L3X_SAST_Report.html", language), html_content) 80 | .expect("Unable to write HTML report"); 81 | } 82 | 83 | Ok(()) 84 | } 85 | 86 | async fn analyze_folder( 87 | folder_path: &str, 88 | openai_creds: &OpenAICreds, 89 | checks: &[VulnerabilityCheck], 90 | validate_all_severities: bool, 91 | model: &str, 92 | no_validation: bool, 93 | ) -> Result< 94 | HashMap< 95 | String, 96 | ( 97 | Vec, 98 | Vec, 99 | HashMap, 100 | ), 101 | >, 102 | Box, 103 | > { 104 | let mut results_by_language: HashMap< 105 | String, 106 | ( 107 | Vec, 108 | Vec, 109 | HashMap, 110 | ), 111 | > = HashMap::new(); 112 | 113 | for entry in WalkDir::new(folder_path) 114 | .into_iter() 115 | .filter_map(|e| e.ok()) 116 | .filter(|e| { 117 | let ext = e.path().extension().and_then(|e| e.to_str()).unwrap_or(""); 118 | ext == "rs" || ext == "sol" 119 | }) 120 | { 121 | let path = entry.path(); 122 | let language = match path.extension().and_then(|e| e.to_str()) { 123 | Some("rs") => "Rust", 124 | Some("sol") => "Solidity-Ethereum", 125 | _ => continue, 126 | }; 127 | 128 | let file_content = fs::read_to_string(path)?; 129 | let (files_list, vulnerabilities_details, safe_patterns_overview) = results_by_language 130 | .entry(language.to_string()) 131 | .or_insert_with(|| (Vec::new(), Vec::new(), HashMap::new())); 132 | 133 | files_list.push(path.to_string_lossy().to_string()); 134 | 135 | // Group findings per file 136 | let mut findings_by_file = Vec::new(); 137 | 138 | for (line_number, line) in file_content.lines().enumerate() { 139 | for check in checks.iter().filter(|c| c.language == language) { 140 | let pattern_regex = Regex::new(&check.pattern)?; 141 | let safe_pattern_regex = check 142 | .safe_pattern 143 | .as_ref() 144 | .and_then(|sp| Regex::new(sp).ok()); 145 | 146 | if pattern_regex.is_match(line) { 147 | findings_by_file.push(( 148 | line_number + 1, 149 | check.id.clone(), 150 | check.severity.clone(), 151 | check.suggested_fix.clone(), 152 | )); 153 | } 154 | 155 | if let Some(safe_regex) = &safe_pattern_regex { 156 | if safe_regex.is_match(line) { 157 | let entry = safe_patterns_overview 158 | .entry(check.id.clone()) 159 | .or_insert_with(|| SafePatternDetail { 160 | pattern_id: check.id.clone(), 161 | title: check.title.clone(), 162 | safe_pattern: check.safe_pattern.clone().unwrap_or_default(), 163 | occurrences: 0, 164 | files: vec![], 165 | }); 166 | 167 | entry.occurrences += 1; 168 | if !entry.files.contains(&path.to_string_lossy().to_string()) { 169 | entry.files.push(path.to_string_lossy().to_string()); 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | let status = if no_validation { 177 | "-".to_string() 178 | } else { 179 | let (status, _) = gpt_validator::validate_vulnerabilities_with_gpt( 180 | openai_creds, 181 | &findings_by_file, 182 | &file_content, 183 | language, 184 | validate_all_severities, 185 | model, 186 | ) 187 | .await?; 188 | status 189 | }; 190 | 191 | for (line_number, vulnerability_id, severity, suggested_fix) in findings_by_file { 192 | vulnerabilities_details.push(VulnerabilityResult { 193 | vulnerability_id: vulnerability_id.clone(), 194 | file: path.to_string_lossy().to_string(), 195 | line_number, 196 | title: checks 197 | .iter() 198 | .find(|c| c.id == vulnerability_id) 199 | .unwrap() 200 | .title 201 | .clone(), 202 | severity, 203 | status: status.clone(), 204 | description: checks 205 | .iter() 206 | .find(|c| c.id == vulnerability_id) 207 | .unwrap() 208 | .description 209 | .clone(), 210 | fix: suggested_fix, 211 | persistence_of_safe_pattern: "No".to_string(), 212 | safe_pattern: None, 213 | }); 214 | } 215 | } 216 | 217 | Ok(results_by_language) 218 | } 219 | -------------------------------------------------------------------------------- /l3x/src/report_generator.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use serde::{Serialize, Deserialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug)] 5 | pub struct FinalReport { 6 | pub security_analysis_summary: SecurityAnalysisSummary, 7 | pub vulnerabilities_details: Vec, 8 | pub safe_patterns_overview: Vec, 9 | pub model: String, 10 | } 11 | 12 | #[derive(Serialize, Deserialize, Debug)] 13 | pub struct SecurityAnalysisSummary { 14 | pub checked_files: usize, 15 | pub files_list: Vec, 16 | pub security_issues_found: usize, 17 | } 18 | 19 | #[derive(Serialize, Deserialize, Debug, Clone)] 20 | pub struct VulnerabilityResult { 21 | pub vulnerability_id: String, 22 | pub file: String, 23 | pub line_number: usize, 24 | pub title: String, 25 | pub severity: String, 26 | pub status: String, 27 | pub description: String, 28 | pub fix: String, 29 | pub persistence_of_safe_pattern: String, 30 | pub safe_pattern: Option, 31 | } 32 | 33 | #[derive(Serialize, Deserialize, Debug, Default)] 34 | pub struct SafePatternDetail { 35 | pub pattern_id: String, 36 | pub title: String, 37 | pub safe_pattern: String, 38 | pub occurrences: usize, 39 | pub files: Vec, 40 | } 41 | 42 | pub fn generate_html_report(report: &FinalReport, language: &str) -> String { 43 | let severity_order = |severity: &str| match severity { 44 | "Critical" => 1, 45 | "High" => 2, 46 | "Medium" => 3, 47 | "Low" => 4, 48 | _ => 5, 49 | }; 50 | 51 | let (mut valid_vulnerabilities, mut invalid_vulnerabilities): (Vec<_>, Vec<_>) = report.vulnerabilities_details 52 | .iter() 53 | .partition(|v| v.status == "Valid"); 54 | 55 | valid_vulnerabilities.sort_by(|a, b| severity_order(&a.severity).cmp(&severity_order(&b.severity))); 56 | invalid_vulnerabilities.sort_by(|a, b| severity_order(&a.severity).cmp(&severity_order(&b.severity))); 57 | 58 | let sorted_vulnerabilities = valid_vulnerabilities.into_iter().chain(invalid_vulnerabilities.into_iter()); 59 | 60 | let vulnerabilities_html = sorted_vulnerabilities.map(|v| { 61 | let status_icon = if v.status == "Valid" { 62 | "🟢 GPT 3.5/4" 63 | } else if v.status == "False positive" { 64 | "🔴 GPT 3.5/4" 65 | } else { 66 | "-" 67 | }; 68 | 69 | format!( 70 | " 71 | {} 72 | {} 73 | {} 74 | {} 75 | {} 76 | {}: {} 77 | {} 78 | {} 79 | ", 80 | v.vulnerability_id, v.title, status_icon, v.severity, v.file, "Line", v.line_number, v.description, v.fix 81 | ) 82 | }).collect::(); 83 | 84 | let safe_patterns_html = report.safe_patterns_overview.iter().map(|p| { 85 | format!( 86 | " 87 | {} 88 | {} 89 | {} 90 | ", 91 | p.pattern_id, p.title, p.safe_pattern 92 | ) 93 | }).collect::(); 94 | 95 | let mut severity_count = HashMap::new(); 96 | let mut total_valid = 0; 97 | let mut total_invalid = 0; 98 | 99 | for v in &report.vulnerabilities_details { 100 | if v.status == "Valid" { 101 | *severity_count.entry(&v.severity).or_insert(0) += 1; 102 | total_valid += 1; 103 | } else if v.status == "False positive" { 104 | total_invalid += 1; 105 | } 106 | } 107 | 108 | let severity_count_json = serde_json::to_string(&severity_count).unwrap(); 109 | 110 | format!( 111 | " 112 | 113 | 114 | 115 | 116 | Vulnerability Report 117 | 118 | 151 | 152 | 153 |
154 |

L3X - Static Application Security Testing (SAST) Report

155 |

Technology: {language}

156 |

Validation model: {report_model}

157 |

Check more on: VulnPlanet
Contribute: GitHub

158 |
159 |
160 |

Summary

161 |
162 |

By Severity

163 | 164 |
165 |
166 |

False Positive Rate

167 | 168 |
169 |
170 |
171 |

Vulnerabilities

172 |

🟢 GPT 3.5/4.0 - Valid or Not possible to determine

173 |

🔴 GPT 3.5/4.0 - False Positive

174 |

- - No validation

175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | {vulnerabilities_html} 187 |
IDTitleStatusSeverityFileLine numberDescriptionDetails
188 |
189 |
190 |

Safe Patterns Overview

191 | 192 | 193 | 194 | 195 | 196 | 197 | {safe_patterns_html} 198 |
Pattern IDTitleSafe Pattern
199 |
200 | 249 | 250 | ", 251 | language = language, 252 | report_model = report.model, 253 | vulnerabilities_html = vulnerabilities_html, 254 | safe_patterns_html = safe_patterns_html, 255 | severity_count_json = severity_count_json, 256 | total_valid = total_valid, 257 | total_invalid = total_invalid 258 | ) 259 | } 260 | -------------------------------------------------------------------------------- /l3x/src/vulnerability_checks.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct VulnerabilityCheck { 5 | pub id: String, 6 | pub title: String, 7 | pub severity: String, 8 | pub pattern: String, 9 | pub safe_pattern: Option, 10 | pub description: String, 11 | pub suggested_fix: String, 12 | pub language: String, 13 | } 14 | 15 | pub fn initialize_vulnerability_checks() -> Vec { 16 | vec![ 17 | VulnerabilityCheck { 18 | id: "RUST001".to_string(), 19 | language: "Rust".to_string(), 20 | title: "[Rust] Unsafe code block".to_string(), 21 | severity: "High".to_string(), 22 | pattern: r"unsafe\s*\{".to_string(), 23 | safe_pattern: None, 24 | description: "Unsafe code blocks can lead to undefined behavior if not used properly.".to_string(), 25 | suggested_fix: "Ensure that unsafe code is necessary and properly reviewed. Use safe abstractions when possible.".to_string(), 26 | }, 27 | VulnerabilityCheck { 28 | id: "RUST002".to_string(), 29 | language: "Rust".to_string(), 30 | title: "[Rust] Unhandled error".to_string(), 31 | severity: "Medium".to_string(), 32 | pattern: r"\.unwrap\(\)".to_string(), 33 | safe_pattern: Some(r"if\s+let\s+Ok\(\w+\)\s*=|if\s+let\s+Some\(\w+\)\s*=".to_string()), 34 | description: "Unwrapping a Result or Option without proper error handling can lead to panic.".to_string(), 35 | suggested_fix: "Use `match` or `if let` to handle the `Result` or `Option` properly, or use `?` to propagate the error.".to_string(), 36 | }, 37 | VulnerabilityCheck { 38 | id: "RUST003".to_string(), 39 | language: "Rust".to_string(), 40 | title: "[Rust] Unchecked arithmetic".to_string(), 41 | severity: "Medium".to_string(), 42 | pattern: r"(\+|\-|\*|\/)".to_string(), 43 | safe_pattern: Some(r"\.checked_add\(|\.checked_sub\(|\.checked_mul\(|\.checked_div\(".to_string()), 44 | description: "Arithmetic operations that can overflow or underflow without being checked.".to_string(), 45 | suggested_fix: "Use checked arithmetic methods like `checked_add`, `checked_sub`, `checked_mul`, and `checked_div`.".to_string(), 46 | }, 47 | VulnerabilityCheck { 48 | id: "RUST004".to_string(), 49 | language: "Rust".to_string(), 50 | title: "[Rust] Insecure random number generator".to_string(), 51 | severity: "High".to_string(), 52 | pattern: r"rand::thread_rng\(\)".to_string(), 53 | safe_pattern: Some(r"rand::ThreadRng::new\(\)".to_string()), 54 | description: "Using an insecure random number generator for security-sensitive operations.".to_string(), 55 | suggested_fix: "Use a cryptographically secure random number generator like `rand::ThreadRng` or `ring::rand::SystemRandom`.".to_string(), 56 | }, 57 | VulnerabilityCheck { 58 | id: "RUST005".to_string(), 59 | language: "Rust".to_string(), 60 | title: "[Rust] Uninitialized memory".to_string(), 61 | severity: "High".to_string(), 62 | pattern: r"mem::uninitialized\(\)".to_string(), 63 | safe_pattern: Some(r"mem::MaybeUninit".to_string()), 64 | description: "Using uninitialized memory can lead to undefined behavior.".to_string(), 65 | suggested_fix: "Initialize memory properly or use `mem::MaybeUninit` for delayed initialization.".to_string(), 66 | }, 67 | VulnerabilityCheck { 68 | id: "RUST006".to_string(), 69 | language: "Rust".to_string(), 70 | title: "[Rust] Use of `mem::transmute`".to_string(), 71 | severity: "High".to_string(), 72 | pattern: r"mem::transmute".to_string(), 73 | safe_pattern: None, 74 | description: "Using `mem::transmute` can lead to undefined behavior and violate type safety.".to_string(), 75 | suggested_fix: "Avoid using `mem::transmute` and use safe type conversions or `as` keyword for primitive types.".to_string(), 76 | }, 77 | VulnerabilityCheck { 78 | id: "RUST007".to_string(), 79 | language: "Rust".to_string(), 80 | title: "[Rust] Use of `std::process::Command`".to_string(), 81 | severity: "Medium".to_string(), 82 | pattern: r"std::process::Command".to_string(), 83 | safe_pattern: Some(r"std::process::Command::new\(...\).*sanitize\(\)".to_string()), 84 | description: "Using `std::process::Command` without properly sanitizing user input can lead to command injection vulnerabilities.".to_string(), 85 | suggested_fix: "Properly sanitize and validate user input before passing it to `std::process::Command`. Consider using safe wrappers or libraries.".to_string(), 86 | }, 87 | VulnerabilityCheck { 88 | id: "RUST008".to_string(), 89 | language: "Rust".to_string(), 90 | title: "[Rust] Use of `std::fs::File` with `unwrap()`".to_string(), 91 | severity: "Medium".to_string(), 92 | pattern: r"std::fs::File::open\(...\)\.unwrap\(\)".to_string(), 93 | safe_pattern: Some(r"std::fs::File::open\(...\)\?".to_string()), 94 | description: "Using `unwrap()` with `std::fs::File` can lead to panics if the file operation fails.".to_string(), 95 | suggested_fix: "Use `?` operator to propagate the error or handle it explicitly with `match` or `if let`.".to_string(), 96 | }, 97 | VulnerabilityCheck { 98 | id: "RUST009".to_string(), 99 | language: "Rust".to_string(), 100 | title: "[Rust] Deserialization of untrusted data".to_string(), 101 | severity: "High".to_string(), 102 | pattern: r"serde::Deserialize".to_string(), 103 | safe_pattern: None, 104 | description: "Deserializing untrusted data without proper validation can lead to security vulnerabilities.".to_string(), 105 | suggested_fix: "Implement custom deserialization logic with proper validation and sanitization of untrusted data. Consider using safe deserialization libraries.".to_string(), 106 | }, 107 | VulnerabilityCheck { 108 | id: "RUST010".to_string(), 109 | language: "Rust".to_string(), 110 | title: "[Rust] Use of `std::net::TcpListener` with `unwrap()`".to_string(), 111 | severity: "Medium".to_string(), 112 | pattern: r"std::net::TcpListener::bind\(...\)\.unwrap\(\)".to_string(), 113 | safe_pattern: Some(r"std::net::TcpListener::bind\(...\)\?".to_string()), 114 | description: "Using `unwrap()` with `std::net::TcpListener` can lead to panics if the binding operation fails.".to_string(), 115 | suggested_fix: "Use `?` operator to propagate the error or handle it explicitly with `match` or `if let`.".to_string(), 116 | }, 117 | VulnerabilityCheck { 118 | id: "VULN001".to_string(), 119 | language: "Rust".to_string(), 120 | title: "[Rust-Solana] Integer Overflow or Underflow".to_string(), 121 | severity: "High".to_string(), 122 | pattern: r"(u64|u128)\s*\.\s*(checked_add|checked_sub|checked_mul|checked_div)\s*\(".to_string(), 123 | safe_pattern: None, 124 | description: "Performing arithmetic operation without checking for overflow or underflow.".to_string(), 125 | suggested_fix: "Use `checked_add`, `checked_sub`, `checked_mul`, or `checked_div` to safely perform arithmetic operations.".to_string(), 126 | }, 127 | VulnerabilityCheck { 128 | id: "VULN002".to_string(), 129 | language: "Rust".to_string(), 130 | title: "[Rust-Solana] Loss of Precision".to_string(), 131 | severity: "High".to_string(), 132 | pattern: r"\.try_round_u64\(\s*\)".to_string(), 133 | safe_pattern: Some(r"\.try_floor_u64\(\s*\)".to_string()), 134 | description: "The use of try_round_u64() for rounding up may lead to loss of precision.".to_string(), 135 | suggested_fix: "Use try_floor_u64() to prevent potential loss of precision.".to_string(), 136 | }, 137 | VulnerabilityCheck { 138 | id: "VULN003".to_string(), 139 | language: "Rust".to_string(), 140 | title: "[Rust-Solana] Inaccurate Calculation Results".to_string(), 141 | severity: "High".to_string(), 142 | pattern: r"(u64|u128)\s*\.\s*saturating_(add|sub|mul)\s*\(".to_string(), 143 | safe_pattern: Some(r"\.checked_(add|sub|mul|div)\(".to_string()), 144 | description: "Reliance on saturating arithmetic operations without considering precision loss.".to_string(), 145 | suggested_fix: "Consider using `checked_add`, `checked_sub`, `checked_mul`, or `checked_div` to handle arithmetic operations explicitly and avoid precision loss.".to_string(), 146 | }, 147 | VulnerabilityCheck { 148 | id: "VULN007".to_string(), 149 | language: "Rust".to_string(), 150 | title: "[Rust-Solana] Missing Check for the Permission of Caller".to_string(), 151 | severity: "Low".to_string(), 152 | pattern: r"fn\s+\w+_privileged\s*\(\s*accounts\s*:\s*&\[AccountInfo\]\s*\)\s*->\s*ProgramResult\s*\{".to_string(), 153 | safe_pattern: Some(r"require_is_authorized_signer\(".to_string()), 154 | description: "Missing verification of caller permissions before sensitive operations.".to_string(), 155 | suggested_fix: "Implement and invoke a permission check function to verify the caller's authority.".to_string(), 156 | }, 157 | VulnerabilityCheck { 158 | id: "VULN008".to_string(), 159 | language: "Rust".to_string(), 160 | title: "[Rust-Solana] Account Signer Check".to_string(), 161 | severity: "High".to_string(), 162 | pattern: r"next_account_info\s*\(".to_string(), 163 | safe_pattern: Some(r"if\s+!\w+\.is_signer".to_string()), 164 | description: "Ensure the expected signer account has actually signed to prevent unauthorized account modifications.".to_string(), 165 | suggested_fix: "Verify `is_signer` is true for transactions requiring signatures.".to_string(), 166 | }, 167 | VulnerabilityCheck { 168 | id: "VULN009".to_string(), 169 | language: "Rust".to_string(), 170 | title: "[Rust-Solana] Account Writable Check".to_string(), 171 | severity: "High".to_string(), 172 | pattern: r"next_account_info\s*\(".to_string(), 173 | safe_pattern: Some(r"if\s+!\w+\.is_writable".to_string()), 174 | description: "Ensure state accounts are checked as writable to prevent unauthorized modifications.".to_string(), 175 | suggested_fix: "Verify `is_writable` is true for accounts that should be modified.".to_string(), 176 | }, 177 | VulnerabilityCheck { 178 | id: "VULN010".to_string(), 179 | language: "Rust".to_string(), 180 | title: "[Rust-Solana] Account Owner or Program ID Check".to_string(), 181 | severity: "High".to_string(), 182 | pattern: r"next_account_info\s*\(".to_string(), 183 | safe_pattern: Some(r"if\s+\w+\.owner\s*!=".to_string()), 184 | description: "Verify the owner of state accounts to prevent fake data injection by malicious programs.".to_string(), 185 | suggested_fix: "Check the account's owner matches the expected program ID.".to_string(), 186 | }, 187 | VulnerabilityCheck { 188 | id: "VULN011".to_string(), 189 | language: "Rust".to_string(), 190 | title: "[Rust-Solana] Account Initialized Check".to_string(), 191 | severity: "High".to_string(), 192 | pattern: r"try_from_slice\s*\(".to_string(), 193 | safe_pattern: Some(r"if\s+\w+\.is_initialized".to_string()), 194 | description: "Prevent re-initialization of already initialized accounts.".to_string(), 195 | suggested_fix: "Ensure account's `is_initialized` flag is checked before initializing.".to_string(), 196 | }, 197 | VulnerabilityCheck { 198 | id: "VULN017".to_string(), 199 | language: "Rust".to_string(), 200 | title: "[Rust-Solana] Signer Authorization - Anchor".to_string(), 201 | severity: "High".to_string(), 202 | pattern: r"pub\s+fn\s+\w+\s*\(ctx:\s*Context<\w+>\)\s*->\s*ProgramResult\s*\{".to_string(), 203 | safe_pattern: Some(r"if\s+!\w+\.is_signer\s*\{".to_string()), 204 | description: "Signer check is missing, which could lead to unauthorized execution.".to_string(), 205 | suggested_fix: "Add a check to verify if the caller is a signer.".to_string(), 206 | }, 207 | VulnerabilityCheck { 208 | id: "VULN018".to_string(), 209 | language: "Rust".to_string(), 210 | title: "[Rust-Solana] Account Data Matching - Anchor".to_string(), 211 | severity: "High".to_string(), 212 | pattern: r"SplTokenAccount::unpack\(&ctx.accounts.\w+.data.borrow\(\)\)".to_string(), 213 | safe_pattern: Some(r"if\s+ctx\.accounts\.\w+\.key\s*!=\s*&\w+\.owner\s*\{".to_string()), 214 | description: "Missing verification of token ownership or mint authority in SPL Token accounts.".to_string(), 215 | suggested_fix: "Verify token ownership matches the expected authority before proceeding.".to_string(), 216 | }, 217 | VulnerabilityCheck { 218 | id: "VULN019".to_string(), 219 | language: "Rust".to_string(), 220 | title: "[Rust-Solana] Owner Checks - Anchor".to_string(), 221 | severity: "High".to_string(), 222 | pattern: r"SplTokenAccount::unpack\(&ctx.accounts.\w+.data.borrow\(\)\)".to_string(), 223 | safe_pattern: Some(r"if\s+ctx.accounts.\w+.owner\s*!=\s*&spl_token::ID\s*\{".to_string()), 224 | description: "Missing checks on the owner field in the metadata of an Account or on the Account itself.".to_string(), 225 | suggested_fix: "Ensure the owner of the account is verified against the expected program ID.".to_string(), 226 | }, 227 | VulnerabilityCheck { 228 | id: "VULN020".to_string(), 229 | language: "Rust".to_string(), 230 | title: "[Rust-Solana] Type Cosplay - Anchor".to_string(), 231 | severity: "High".to_string(), 232 | pattern: r"User::try_from_slice\(&ctx.accounts.\w+.data.borrow\(\)\)".to_string(), 233 | safe_pattern: Some(r"if\s+\w+.discriminant\s*!=\s*AccountDiscriminant::\w+\s*\{".to_string()), 234 | description: "Risks of different accounts impersonating each other by sharing identical data structures.".to_string(), 235 | suggested_fix: "Add discriminant checks to differentiate account types securely.".to_string(), 236 | }, 237 | VulnerabilityCheck { 238 | id: "VULN021".to_string(), 239 | language: "Rust".to_string(), 240 | title: "[Rust-Solana] Check Initialize - Anchor".to_string(), 241 | severity: "High".to_string(), 242 | pattern: r"User::try_from_slice\(&ctx.accounts.\w+.data.borrow\(\)\)".to_string(), 243 | safe_pattern: Some(r"if\s+\w+.discriminator\s*==\s*true\s*\{".to_string()), 244 | description: "Data should only be initialized once; missing checks can lead to reinitialization.".to_string(), 245 | suggested_fix: "Use a flag to ensure data is initialized only once.".to_string(), 246 | }, 247 | VulnerabilityCheck { 248 | id: "VULN022".to_string(), 249 | language: "Rust".to_string(), 250 | title: "[Rust-Solana] Arbitrary CPI - Anchor".to_string(), 251 | severity: "High".to_string(), 252 | pattern: r"solana_program::program::invoke\(".to_string(), 253 | safe_pattern: Some(r"if &spl_token::ID != ctx.accounts.token_program.key \{".to_string()), 254 | description: "Unverified target program id in CPI can lead to arbitrary code execution.".to_string(), 255 | suggested_fix: "Ensure the target program id is verified against expected program id.".to_string(), 256 | }, 257 | VulnerabilityCheck { 258 | id: "VULN023".to_string(), 259 | language: "Rust".to_string(), 260 | title: "[Rust-Solana] Duplicate Mutable Accounts - Anchor".to_string(), 261 | severity: "High".to_string(), 262 | pattern: r"let\s+user_a\s+=\s+&mut\s+ctx.accounts.user_a;.*let\s+user_b\s+=\s+&mut\s+ctx.accounts.user_b;".to_string(), 263 | safe_pattern: Some(r"if ctx.accounts.user_a.key\(\) == ctx.accounts.user_b.key\(\) \{".to_string()), 264 | description: "Passing the same mutable account multiple times may result in unintended data overwriting.".to_string(), 265 | suggested_fix: "Add checks to ensure that mutable accounts passed are distinct.".to_string(), 266 | }, 267 | VulnerabilityCheck { 268 | id: "VULN024".to_string(), 269 | language: "Rust".to_string(), 270 | title: "[Rust-Solana] Bump Seed Canonicalization - Anchor".to_string(), 271 | severity: "High".to_string(), 272 | pattern: r"Pubkey::create_program_address\(&\[".to_string(), 273 | safe_pattern: Some(r"let \(address, expected_bump\) = Pubkey::find_program_address\(&\[".to_string()), 274 | description: "Improper validation of bump seeds can lead to security vulnerabilities.".to_string(), 275 | suggested_fix: "Use `find_program_address` for bump seed canonicalization and validate against expected seeds.".to_string(), 276 | }, 277 | VulnerabilityCheck { 278 | id: "VULN025".to_string(), 279 | language: "Rust".to_string(), 280 | title: "[Rust-Solana] PDA Sharing - Anchor".to_string(), 281 | severity: "High".to_string(), 282 | pattern: r"token::transfer\(ctx.accounts.transfer_ctx\(\).with_signer\(&\[\w+\]\),".to_string(), 283 | safe_pattern: Some(r"let seeds = &\[\w+.withdraw_destination.as_ref\(\), &\[\w+.bump\]\];".to_string()), 284 | description: "Sharing PDA across multiple roles without proper permission separation may lead to unauthorized access.".to_string(), 285 | suggested_fix: "Ensure PDAs used across roles have distinct seeds and permissions.".to_string(), 286 | }, 287 | VulnerabilityCheck { 288 | id: "VULN026".to_string(), 289 | language: "Rust".to_string(), 290 | title: "[Rust-Solana] Closing Accounts - Anchor".to_string(), 291 | severity: "High".to_string(), 292 | pattern: r"\*\*ctx.accounts.account.to_account_info\(\).lamports.borrow_mut\(\) = 0;".to_string(), 293 | safe_pattern: Some(r"let mut data = account.try_borrow_mut_data\(\)?;.*CLOSED_ACCOUNT_DISCRIMINATOR".to_string()), 294 | description: "Improper closing of accounts may leave them vulnerable to misuse.".to_string(), 295 | suggested_fix: "Ensure accounts are properly closed by transferring lamports and marking with a discriminator.".to_string(), 296 | }, 297 | VulnerabilityCheck { 298 | id: "VULN027".to_string(), 299 | language: "Rust".to_string(), 300 | title: "[Rust-Solana] Sysvar System Account Not Checked".to_string(), 301 | severity: "High".to_string(), 302 | pattern: r"solana_program::sysvar::instructions::load_current_index\(\s*&accs\.\w+\.try_borrow_mut_data\(\)\?\s*\)".to_string(), 303 | safe_pattern: Some(r"if \*accs\.\w+\.key != solana_program::sysvar::instructions::id\(\) \{".to_string()), 304 | description: "Sysvar system account is accessed without verifying its legitimacy, exposing the contract to potential manipulation or attacks.".to_string(), 305 | suggested_fix: "Before deserializing information from a sysvar account, verify that the incoming address matches the expected sysvar ID.".to_string(), 306 | }, 307 | VulnerabilityCheck { 308 | id: "VULN028".to_string(), 309 | language: "Rust".to_string(), 310 | title: "[Rust-Solana] PDA Account Misuse Without Proper Verification".to_string(), 311 | severity: "High".to_string(), 312 | pattern: r"CpiContext::new\(\s*self.token_program.clone\(\),\s*Transfer\s*\{.*?authority: self.market_authority.clone\(\),.*?\}\s*\)".to_string(), 313 | safe_pattern: Some(r"CpiContext::new\(.*Burn\s*\{.*?authority: self.depositor.clone\(\),.*?\}\s*\)".to_string()), 314 | description: "The PDA account is utilized without validating the caller's and beneficiary's accounts, allowing unauthorized actions such as burning other users' Tokens and transferring proceeds to an attacker's account.".to_string(), 315 | suggested_fix: "Implement checks to verify the depositor's signature and ensure the deposit_account cannot be forged by validating the derived address generated by seeds from reserve.key and depositor.key.".to_string(), 316 | }, 317 | VulnerabilityCheck { 318 | id: "VULN029".to_string(), 319 | language: "Rust".to_string(), 320 | title: "[Rust-Solana] Unchecked Account Deserialization".to_string(), 321 | severity: "High".to_string(), 322 | pattern: r"try_from_slice\(&ctx.accounts.\w+.data.borrow\(\)\?\)".to_string(), 323 | safe_pattern: Some(r"if ctx.accounts.\w+.owner == &expected_program_id { ... }".to_string()), 324 | description: "Failing to check if an account is of the expected type before deserializing can lead to incorrect assumptions about state.".to_string(), 325 | suggested_fix: "Ensure accounts are of the expected type before deserialization.".to_string(), 326 | }, 327 | VulnerabilityCheck { 328 | id: "VULN031".to_string(), 329 | language: "Rust".to_string(), 330 | title: "[Rust-Solana] CPI to Unauthorized Programs".to_string(), 331 | severity: "High".to_string(), 332 | pattern: r"invoke\(\[.*?\], &[.*?]\)".to_string(), 333 | safe_pattern: Some(r"if &authorized_programs.contains(&program_id) { ... }".to_string()), 334 | description: "Invoking unauthorized or risky external programs can expose the contract to vulnerabilities present in those programs.".to_string(), 335 | suggested_fix: "Whitelist external programs that can be invoked, and perform thorough security reviews on them.".to_string(), 336 | }, 337 | VulnerabilityCheck { 338 | id: "Rust-Solana001".to_string(), 339 | language: "Rust".to_string(), 340 | title: "[Rust-Solana] Misuse of Unsafe Code".to_string(), 341 | severity: "High".to_string(), 342 | pattern: r"unsafe\s*\{".to_string(), 343 | safe_pattern: None, 344 | description: "Unsafe blocks may lead to undefined behavior and memory safety violations if not used carefully. Ensure justification and proper auditing.".to_string(), 345 | suggested_fix: "Minimize the use of `unsafe` by leveraging safe Rust-Solana abstractions and validate all `unsafe` blocks for safety guarantees.".to_string(), 346 | }, 347 | VulnerabilityCheck { 348 | id: "Rust-Solana002".to_string(), 349 | language: "Rust".to_string(), 350 | title: "[Rust-Solana] Improper Error Handling".to_string(), 351 | severity: "Medium".to_string(), 352 | pattern: r"\.unwrap\(\)|\.expect\(".to_string(), 353 | safe_pattern: None, 354 | description: "Overuse of `unwrap()` or `expect()` can lead to panics. Prefer using error handling mechanisms like `match` or `if let`.".to_string(), 355 | suggested_fix: "Replace `unwrap()` and `expect()` with proper error handling to prevent unexpected panics in production code.".to_string(), 356 | }, 357 | VulnerabilityCheck { 358 | id: "Rust-Solana003".to_string(), 359 | language: "Rust".to_string(), 360 | title: "[Rust-Solana] Overuse of Panics for Control Flow".to_string(), 361 | severity: "Medium".to_string(), 362 | pattern: r"panic!\(".to_string(), 363 | safe_pattern: None, 364 | description: "Using panics for control flow makes code hard to follow and can lead to unexpected termination.".to_string(), 365 | suggested_fix: "Use Result types for error handling and reserve panics for unrecoverable errors only.".to_string(), 366 | }, 367 | VulnerabilityCheck { 368 | id: "Rust-Solana004".to_string(), 369 | language: "Rust".to_string(), 370 | title: "[Rust-Solana] Concurrency Issues and Data Races".to_string(), 371 | severity: "High".to_string(), 372 | pattern: r"std::thread|std::sync".to_string(), 373 | safe_pattern: None, 374 | description: "Improper handling of threads and synchronization can lead to data races, deadlocks, and other concurrency issues.".to_string(), 375 | suggested_fix: "Use Rust-Solana's concurrency primitives correctly, prefer `std::sync` module's types like Mutex, RwLock, and leverage the `rayon` crate for data parallelism.".to_string(), 376 | }, 377 | VulnerabilityCheck { 378 | id: "Rust-Solana005".to_string(), 379 | language: "Rust".to_string(), 380 | title: "[Rust-Solana] Potential Memory Leaks".to_string(), 381 | severity: "Low".to_string(), 382 | pattern: r"Rc<|Arc<|Box<".to_string(), 383 | safe_pattern: None, 384 | description: "Cyclic references or improper use of smart pointers can lead to memory leaks.".to_string(), 385 | suggested_fix: "Use `Weak` pointers to break cycles and audit memory usage regularly.".to_string(), 386 | }, 387 | VulnerabilityCheck { 388 | id: "Rust-Solana006".to_string(), 389 | language: "Rust".to_string(), 390 | title: "[Rust-Solana] Potential DoS Vulnerabilities".to_string(), 391 | severity: "High".to_string(), 392 | pattern: r"\.clone\(|Vec::with_capacity\(|String::with_capacity\(".to_string(), 393 | safe_pattern: None, 394 | description: "Allocations based on untrusted input sizes can lead to DoS via memory exhaustion.".to_string(), 395 | suggested_fix: "Validate input sizes before allocations and use bounded collections. Consider rate-limiting or other mitigation strategies.".to_string(), 396 | }, 397 | VulnerabilityCheck { 398 | id: "Rust-Solana007".to_string(), 399 | language: "Rust".to_string(), 400 | title: "[Rust-Solana] Missing Boundary Checks".to_string(), 401 | severity: "Medium".to_string(), 402 | pattern: r"\[\w+\]".to_string(), 403 | safe_pattern: Some(r"\.get\(\s*\w+\s*\)\?|\.get_mut\(\s*\w+\s*\)\?".to_string()), 404 | description: "Accessing arrays or vectors without boundary checks can lead to panics or buffer overflows.".to_string(), 405 | suggested_fix: "Use `.get()` or `.get_mut()` for safe access with bounds checking, and handle the Option result appropriately.".to_string(), 406 | }, 407 | VulnerabilityCheck { 408 | id: "Rust-Solana008".to_string(), 409 | language: "Rust".to_string(), 410 | title: "[Rust-Solana] Unnecessary Cloning of Large Data Structures".to_string(), 411 | severity: "Low".to_string(), 412 | pattern: r"\.clone\(".to_string(), 413 | safe_pattern: None, 414 | description: "Cloning large data structures can lead to performance issues due to excessive memory use.".to_string(), 415 | suggested_fix: "Prefer borrowing or using reference-counted types like `Rc` or `Arc` to share data without deep copying.".to_string(), 416 | }, 417 | VulnerabilityCheck { 418 | id: "Rust-Solana009".to_string(), 419 | language: "Rust".to_string(), 420 | title: "[Rust-Solana] Blocking I/O in Asynchronous Code".to_string(), 421 | severity: "Medium".to_string(), 422 | pattern: r"std::fs|std::net".to_string(), 423 | safe_pattern: Some(r"tokio::fs::\w+|tokio::net::\w+".to_string()), 424 | description: "Performing blocking I/O operations in async contexts can lead to thread starvation and reduced scalability.".to_string(), 425 | suggested_fix: "Use asynchronous equivalents for file and network operations within async functions.".to_string(), 426 | }, 427 | VulnerabilityCheck { 428 | id: "Rust-Solana010".to_string(), 429 | language: "Rust".to_string(), 430 | title: "[Rust-Solana] Misuse of Arc>".to_string(), 431 | severity: "Medium".to_string(), 432 | pattern: r"Arc::new\(\s*Mutex::new\(\s*.*?\s*\)\s*\)".to_string(), 433 | safe_pattern: None, 434 | description: "Incorrect use of Arc> can lead to deadlocks or inefficient locking mechanisms.".to_string(), 435 | suggested_fix: "Ensure that locks are held for the minimum duration necessary, and consider other synchronization primitives like RwLock if applicable.".to_string(), 436 | }, 437 | VulnerabilityCheck { 438 | id: "Rust-Solana011".to_string(), 439 | language: "Rust".to_string(), 440 | title: "[Rust-Solana] Improper Implementation of Drop Trait".to_string(), 441 | severity: "Medium".to_string(), 442 | pattern: r"impl\s+Drop\s+for\s+.*?\s*\{.*\}".to_string(), 443 | safe_pattern: None, 444 | description: "Incorrect custom implementations of the Drop trait can lead to resource leaks or panic safety issues.".to_string(), 445 | suggested_fix: "Implement the Drop trait carefully, ensuring that errors are handled gracefully and resources are properly released.".to_string(), 446 | }, 447 | VulnerabilityCheck { 448 | id: "Rust-Solana012".to_string(), 449 | language: "Rust".to_string(), 450 | title: "[Rust-Solana] Usage of mem::uninitialized and mem::zeroed".to_string(), 451 | severity: "High".to_string(), 452 | pattern: r"mem::uninitialized\(\)|mem::zeroed\(\)".to_string(), 453 | safe_pattern: None, 454 | description: "Using mem::uninitialized or mem::zeroed can lead to undefined behavior if the type has any non-zero or complex initialization requirements.".to_string(), 455 | suggested_fix: "Prefer using safe initialization patterns and avoid these functions for types with non-trivial initialization requirements.".to_string(), 456 | }, 457 | VulnerabilityCheck { 458 | id: "SOLIDITY001".to_string(), 459 | language: "Solidity-Ethereum".to_string(), 460 | title: "Storage ABIEncoderV2 Array".to_string(), 461 | severity: "High".to_string(), 462 | pattern: r"abi\.encode\(\w+\)".to_string(), 463 | safe_pattern: None, 464 | description: "solc versions 0.4.7-0.5.9 contain a compiler bug leading to incorrect ABI encoder usage.".to_string(), 465 | suggested_fix: "Use a compiler >= 0.5.10.".to_string(), 466 | }, 467 | VulnerabilityCheck { 468 | id: "SOLIDITY002".to_string(), 469 | language: "Solidity-Ethereum".to_string(), 470 | title: "Arbitrary from in transferFrom".to_string(), 471 | severity: "High".to_string(), 472 | pattern: r"transferFrom\(\s*[^,]+,\s*msg.sender".to_string(), 473 | safe_pattern: Some(r"transferFrom\(\s*msg.sender".to_string()), 474 | description: "Detect when msg.sender is not used as from in transferFrom.".to_string(), 475 | suggested_fix: "Use msg.sender as from in transferFrom.".to_string(), 476 | }, 477 | VulnerabilityCheck { 478 | id: "SOLIDITY003".to_string(), 479 | language: "Solidity-Ethereum".to_string(), 480 | title: "Modifying storage array by value".to_string(), 481 | severity: "High".to_string(), 482 | pattern: r"function\s+\w+\s*\(\s*uint\[\d+\]\s+\w+\s*\)\s*internal".to_string(), 483 | safe_pattern: Some(r"function\s+\w+\s*\(\s*uint\[\d+\]\s+storage\s+\w+\s*\)\s*internal".to_string()), 484 | description: "Detect arrays passed to a function that expects reference to a storage array.".to_string(), 485 | suggested_fix: "Ensure the correct usage of memory and storage in the function parameters. Make all the locations explicit.".to_string(), 486 | }, 487 | VulnerabilityCheck { 488 | id: "SOLIDITY004".to_string(), 489 | language: "Solidity-Ethereum".to_string(), 490 | title: "ABI encodePacked Collision".to_string(), 491 | severity: "High".to_string(), 492 | pattern: r"abi\.encodePacked\(\s*[^,]+\s*,\s*[^,]+".to_string(), 493 | safe_pattern: None, 494 | description: "Detect collision due to dynamic type usages in abi.encodePacked.".to_string(), 495 | suggested_fix: "Do not use more than one dynamic type in abi.encodePacked(). Use abi.encode(), preferably.".to_string(), 496 | }, 497 | VulnerabilityCheck { 498 | id: "SOLIDITY005".to_string(), 499 | language: "Solidity-Ethereum".to_string(), 500 | title: "Incorrect shift in assembly".to_string(), 501 | severity: "High".to_string(), 502 | pattern: r"assembly\s*\{\s*shr\(\w+,\s*\d+\)\s*\}".to_string(), 503 | safe_pattern: Some(r"assembly\s*\{\s*shr\(\d+,\s*\w+\)\s*\}".to_string()), 504 | description: "Detect if the values in a shift operation are reversed.".to_string(), 505 | suggested_fix: "Swap the order of parameters.".to_string(), 506 | }, 507 | VulnerabilityCheck { 508 | id: "SOLIDITY006".to_string(), 509 | language: "Solidity-Ethereum".to_string(), 510 | title: "Multiple constructor schemes".to_string(), 511 | severity: "High".to_string(), 512 | pattern: r"constructor\(\)\s*public\s*\{.*\}\s*function\s+\w+\(\)\s*public".to_string(), 513 | safe_pattern: None, 514 | description: "Detect multiple constructor definitions in the same contract (using new and old schemes).".to_string(), 515 | suggested_fix: "Only declare one constructor, preferably using the new scheme constructor(...) instead of function (...).".to_string(), 516 | }, 517 | VulnerabilityCheck { 518 | id: "SOLIDITY008".to_string(), 519 | language: "Solidity-Ethereum".to_string(), 520 | title: "Public Mappings with Nested Variables".to_string(), 521 | severity: "High".to_string(), 522 | pattern: r"mapping\s*\(\w+\s*=>\s*\w+\[\]\)\s*public".to_string(), 523 | safe_pattern: None, 524 | description: "Prior to Solidity 0.5, a public mapping with nested structures returned incorrect values.".to_string(), 525 | suggested_fix: "Do not use public mapping with nested structures.".to_string(), 526 | }, 527 | VulnerabilityCheck { 528 | id: "SOLIDITY009".to_string(), 529 | language: "Solidity-Ethereum".to_string(), 530 | title: "Right-to-Left-Override Character".to_string(), 531 | severity: "High".to_string(), 532 | pattern: r"U\+202E".to_string(), 533 | safe_pattern: None, 534 | description: "An attacker can manipulate the logic of the contract by using a right-to-left-override character.".to_string(), 535 | suggested_fix: "Special control characters must not be allowed.".to_string(), 536 | }, 537 | VulnerabilityCheck { 538 | id: "SOLIDITY010".to_string(), 539 | language: "Solidity-Ethereum".to_string(), 540 | title: "State Variable Shadowing".to_string(), 541 | severity: "High".to_string(), 542 | pattern: r"contract\s+\w+\s*is\s+\w+\s*\{.*address\s+owner;".to_string(), 543 | safe_pattern: None, 544 | description: "Detection of state variables shadowed.".to_string(), 545 | suggested_fix: "Remove the state variable shadowing.".to_string(), 546 | }, 547 | VulnerabilityCheck { 548 | id: "SOLIDITY011".to_string(), 549 | language: "Solidity-Ethereum".to_string(), 550 | title: "Suicidal".to_string(), 551 | severity: "High".to_string(), 552 | pattern: r"function\s+kill\(\)\s*public\s*\{\s*selfdestruct\(".to_string(), 553 | safe_pattern: None, 554 | description: "Unprotected call to a function executing selfdestruct/suicide.".to_string(), 555 | suggested_fix: "Protect access to all sensitive functions.".to_string(), 556 | }, 557 | VulnerabilityCheck { 558 | id: "SOLIDITY012".to_string(), 559 | language: "Solidity-Ethereum".to_string(), 560 | title: "Uninitialized State Variables".to_string(), 561 | severity: "High".to_string(), 562 | pattern: r"contract\s+\w+\s*\{.*address\s+\w+;.*\}\s*function\s+\w+\(\)\s*payable\s*public\s*\{".to_string(), 563 | safe_pattern: None, 564 | description: "Uninitialized state variables.".to_string(), 565 | suggested_fix: "Initialize all the variables. If a variable is meant to be initialized to zero, explicitly set it to zero to improve code readability.".to_string(), 566 | }, 567 | VulnerabilityCheck { 568 | id: "SOLIDITY013".to_string(), 569 | language: "Solidity-Ethereum".to_string(), 570 | title: "Uninitialized Storage Variables".to_string(), 571 | severity: "High".to_string(), 572 | pattern: r"contract\s+\w+\s*\{.*function\s+\w+\(\)\s*\{.*struct\s+\w+\s*\w+;.*\w+\.\w+\s*=\s*0x0;".to_string(), 573 | safe_pattern: None, 574 | description: "An uninitialized storage variable will act as a reference to the first state variable, and can override a critical variable.".to_string(), 575 | suggested_fix: "Initialize all storage variables.".to_string(), 576 | }, 577 | VulnerabilityCheck { 578 | id: "SOLIDITY014".to_string(), 579 | language: "Solidity-Ethereum".to_string(), 580 | title: "Unprotected Upgradeable Contract".to_string(), 581 | severity: "High".to_string(), 582 | pattern: r"contract\s+\w+\s*is\s+Initializable\s*\{.*function\s+initialize\(\)\s*external\s*initializer\{".to_string(), 583 | safe_pattern: None, 584 | description: "Detects logic contract that can be destructed.".to_string(), 585 | suggested_fix: "Add a constructor to ensure initialize cannot be called on the logic contract.".to_string(), 586 | }, 587 | VulnerabilityCheck { 588 | id: "SOLIDITY015".to_string(), 589 | language: "Solidity-Ethereum".to_string(), 590 | title: "Arbitrary from in TransferFrom Used with Permit".to_string(), 591 | severity: "High".to_string(), 592 | pattern: r"function\s+\w+\(\s*address\s+from,\s*uint256\s+value.*\)\s*public\s*\{\s*erc20\.permit\(".to_string(), 593 | safe_pattern: None, 594 | description: "Detect when msg.sender is not used as from in transferFrom and permit is used.".to_string(), 595 | suggested_fix: "Ensure that the underlying ERC20 token correctly implements a permit function.".to_string(), 596 | }, 597 | VulnerabilityCheck { 598 | id: "SOLIDITY016".to_string(), 599 | language: "Solidity-Ethereum".to_string(), 600 | title: "Functions that Send Ether to Arbitrary Destinations".to_string(), 601 | severity: "High".to_string(), 602 | pattern: r"function\s+\w+\(\)\s*public\s*\{\s*\w+\.transfer\(".to_string(), 603 | safe_pattern: None, 604 | description: "Unprotected call to a function sending Ether to an arbitrary address.".to_string(), 605 | suggested_fix: "Ensure that an arbitrary user cannot withdraw unauthorized funds.".to_string(), 606 | }, 607 | VulnerabilityCheck { 608 | id: "SOLIDITY017".to_string(), 609 | language: "Solidity-Ethereum".to_string(), 610 | title: "Array Length Assignment".to_string(), 611 | severity: "High".to_string(), 612 | pattern: r"\.\s*length\s*=\s*\w+".to_string(), 613 | safe_pattern: None, 614 | description: "Detects the direct assignment of an array's length.".to_string(), 615 | suggested_fix: "Do not allow array lengths to be directly set; add values as needed.".to_string(), 616 | }, 617 | VulnerabilityCheck { 618 | id: "SOLIDITY018".to_string(), 619 | language: "Solidity-Ethereum".to_string(), 620 | title: "Controlled Delegatecall".to_string(), 621 | severity: "High".to_string(), 622 | pattern: r"\.delegatecall\(".to_string(), 623 | safe_pattern: None, 624 | description: "Delegatecall or callcode to an address controlled by the user.".to_string(), 625 | suggested_fix: "Avoid using delegatecall. Use only trusted destinations.".to_string(), 626 | }, 627 | VulnerabilityCheck { 628 | id: "SOLIDITY019".to_string(), 629 | language: "Solidity-Ethereum".to_string(), 630 | title: "Payable Functions Using Delegatecall Inside a Loop".to_string(), 631 | severity: "High".to_string(), 632 | pattern: r"public\s+payable\s*\{.*\.delegatecall\(".to_string(), 633 | safe_pattern: None, 634 | description: "Detect the use of delegatecall inside a loop in a payable function.".to_string(), 635 | suggested_fix: "Carefully check that the function called by delegatecall is not payable.".to_string(), 636 | }, 637 | VulnerabilityCheck { 638 | id: "SOLIDITY020".to_string(), 639 | language: "Solidity-Ethereum".to_string(), 640 | title: "Incorrect Exponentiation".to_string(), 641 | severity: "High".to_string(), 642 | pattern: r"2\s*\^\s*256".to_string(), 643 | safe_pattern: None, 644 | description: "Detect use of bitwise xor ^ instead of exponential **.".to_string(), 645 | suggested_fix: "Use the correct operator ** for exponentiation.".to_string(), 646 | }, 647 | VulnerabilityCheck { 648 | id: "SOLIDITY021".to_string(), 649 | language: "Solidity-Ethereum".to_string(), 650 | title: "Incorrect Return in Assembly".to_string(), 651 | severity: "High".to_string(), 652 | pattern: r"assembly\s*\{.*return\s*\(".to_string(), 653 | safe_pattern: None, 654 | description: "Detect if return in an assembly block halts unexpectedly the execution.".to_string(), 655 | suggested_fix: "Use the leave statement.".to_string(), 656 | }, 657 | VulnerabilityCheck { 658 | id: "SOLIDITY022".to_string(), 659 | language: "Solidity-Ethereum".to_string(), 660 | title: "msg.value Inside a Loop".to_string(), 661 | severity: "High".to_string(), 662 | pattern: r"for\s*\(.*\)\s*\{.*msg\.value.*\}".to_string(), 663 | safe_pattern: None, 664 | description: "Detect the use of msg.value inside a loop.".to_string(), 665 | suggested_fix: "Provide explicit amounts alongside the receivers array, matching msg.value.".to_string(), 666 | }, 667 | VulnerabilityCheck { 668 | id: "SOLIDITY023".to_string(), 669 | language: "Solidity-Ethereum".to_string(), 670 | title: "Reentrancy Vulnerabilities".to_string(), 671 | severity: "High".to_string(), 672 | pattern: r"call\.value\(".to_string(), 673 | safe_pattern: None, 674 | description: "Detection of the reentrancy bug involving Ether.".to_string(), 675 | suggested_fix: "Apply the check-effects-interactions pattern.".to_string(), 676 | }, 677 | VulnerabilityCheck { 678 | id: "SOLIDITY024".to_string(), 679 | language: "Solidity-Ethereum".to_string(), 680 | title: "Return Instead of Leave in Assembly".to_string(), 681 | severity: "High".to_string(), 682 | pattern: r"assembly\s*\{.*return\s*\(".to_string(), 683 | safe_pattern: None, 684 | description: "Detect if a return is used where a leave should be used.".to_string(), 685 | suggested_fix: "Use the leave statement.".to_string(), 686 | }, 687 | VulnerabilityCheck { 688 | id: "SOLIDITY025".to_string(), 689 | language: "Solidity-Ethereum".to_string(), 690 | title: "Storage Signed Integer Array".to_string(), 691 | severity: "High".to_string(), 692 | pattern: r"int\[\d+\]\s*\w+;".to_string(), 693 | safe_pattern: None, 694 | description: "solc versions 0.4.7-0.5.9 contain a compiler bug leading to incorrect values in signed integer arrays.".to_string(), 695 | suggested_fix: "Use a compiler version >= 0.5.10.".to_string(), 696 | }, 697 | VulnerabilityCheck { 698 | id: "SOLIDITY026".to_string(), 699 | language: "Solidity-Ethereum".to_string(), 700 | title: "Unchecked Transfer".to_string(), 701 | severity: "High".to_string(), 702 | pattern: r"\.transfer\(".to_string(), 703 | safe_pattern: None, 704 | description: "The return value of an external transfer/transferFrom call is not checked.".to_string(), 705 | suggested_fix: "Use SafeERC20, or ensure that the transfer/transferFrom return value is checked.".to_string(), 706 | }, 707 | VulnerabilityCheck { 708 | id: "SOLIDITY027".to_string(), 709 | language: "Solidity-Ethereum".to_string(), 710 | title: "Weak PRNG".to_string(), 711 | severity: "High".to_string(), 712 | pattern: r"(block\.timestamp|now|block\.blockhash)\s*%".to_string(), 713 | safe_pattern: None, 714 | description: "Weak PRNG due to a modulo on block.timestamp, now, or blockhash.".to_string(), 715 | suggested_fix: "Do not use block.timestamp, now, or blockhash as a source of randomness.".to_string(), 716 | }, 717 | VulnerabilityCheck { 718 | id: "SOLIDITY029".to_string(), 719 | language: "Solidity-Ethereum".to_string(), 720 | title: "Domain Separator Collision".to_string(), 721 | severity: "Medium".to_string(), 722 | pattern: r"DOMAIN_SEPARATOR\(\)".to_string(), 723 | safe_pattern: None, 724 | description: "An ERC20 token function signature collides with EIP-2612's DOMAIN_SEPARATOR().".to_string(), 725 | suggested_fix: "Remove or rename the function that collides with DOMAIN_SEPARATOR().".to_string(), 726 | }, 727 | VulnerabilityCheck { 728 | id: "SOLIDITY030".to_string(), 729 | language: "Solidity-Ethereum".to_string(), 730 | title: "Dangerous Enum Conversion".to_string(), 731 | severity: "Medium".to_string(), 732 | pattern: r"enum\s+\w+\s*\{.*\}\s*;.*=\s*\w+\s*\(".to_string(), 733 | safe_pattern: None, 734 | description: "Detect out-of-range enum conversion.".to_string(), 735 | suggested_fix: "Check the enum conversion range.".to_string(), 736 | }, 737 | VulnerabilityCheck { 738 | id: "SOLIDITY031".to_string(), 739 | language: "Solidity-Ethereum".to_string(), 740 | title: "Incorrect ERC20 Interface".to_string(), 741 | severity: "Medium".to_string(), 742 | pattern: r"function\s+transfer\(".to_string(), 743 | safe_pattern: None, 744 | description: "Incorrect return values for ERC20 functions.".to_string(), 745 | suggested_fix: "Set the appropriate return values for ERC20 functions.".to_string(), 746 | }, 747 | VulnerabilityCheck { 748 | id: "SOLIDITY032".to_string(), 749 | language: "Solidity-Ethereum".to_string(), 750 | title: "Incorrect ERC721 Interface".to_string(), 751 | severity: "Medium".to_string(), 752 | pattern: r"function\s+ownerOf\(".to_string(), 753 | safe_pattern: None, 754 | description: "Incorrect return values for ERC721 functions.".to_string(), 755 | suggested_fix: "Set the appropriate return values for ERC721 functions.".to_string(), 756 | }, 757 | VulnerabilityCheck { 758 | id: "SOLIDITY033".to_string(), 759 | language: "Solidity-Ethereum".to_string(), 760 | title: "Dangerous Strict Equalities".to_string(), 761 | severity: "Medium".to_string(), 762 | pattern: r"==\s*\d+\s*ether".to_string(), 763 | safe_pattern: None, 764 | description: "Use of strict equalities that can be easily manipulated by an attacker.".to_string(), 765 | suggested_fix: "Avoid using strict equality for Ether or token amounts.".to_string(), 766 | }, 767 | VulnerabilityCheck { 768 | id: "SOLIDITY034".to_string(), 769 | language: "Solidity-Ethereum".to_string(), 770 | title: "Contracts that Lock Ether".to_string(), 771 | severity: "Medium".to_string(), 772 | pattern: r"function\s+\w+\(\)\s+payable".to_string(), 773 | safe_pattern: None, 774 | description: "Contract with a payable function, but without a withdrawal capacity.".to_string(), 775 | suggested_fix: "Add a withdrawal function or remove the payable attribute.".to_string(), 776 | }, 777 | VulnerabilityCheck { 778 | id: "SOLIDITY035".to_string(), 779 | language: "Solidity-Ethereum".to_string(), 780 | title: "Deletion on Mapping Containing a Structure".to_string(), 781 | severity: "Medium".to_string(), 782 | pattern: r"delete\s+\w+\[.*\]\s*;".to_string(), 783 | safe_pattern: None, 784 | description: "A deletion in a structure containing a mapping will not delete the mapping.".to_string(), 785 | suggested_fix: "Use a lock mechanism instead of a deletion for structures containing a mapping.".to_string(), 786 | }, 787 | VulnerabilityCheck { 788 | id: "SOLIDITY036".to_string(), 789 | language: "Solidity-Ethereum".to_string(), 790 | title: "State Variable Shadowing from Abstract Contracts".to_string(), 791 | severity: "Medium".to_string(), 792 | pattern: r"contract\s+\w+\s*is\s+\w+\s*\{.*address\s+\w+\s*;".to_string(), 793 | safe_pattern: None, 794 | description: "Detection of state variables shadowed from abstract contracts.".to_string(), 795 | suggested_fix: "Remove the state variable shadowing.".to_string(), 796 | }, 797 | VulnerabilityCheck { 798 | id: "SOLIDITY037".to_string(), 799 | language: "Solidity-Ethereum".to_string(), 800 | title: "Misuse of a Boolean Constant".to_string(), 801 | severity: "Medium".to_string(), 802 | pattern: r"(if\s+\(\s*false\s*\))|(return\s+\(\s*b\s*\|\|\s*true\s*\))".to_string(), 803 | safe_pattern: None, 804 | description: "Detects the misuse of a Boolean constant.".to_string(), 805 | suggested_fix: "Verify and simplify the condition.".to_string(), 806 | }, 807 | VulnerabilityCheck { 808 | id: "SOLIDITY040".to_string(), 809 | language: "Solidity-Ethereum".to_string(), 810 | title: "Divide Before Multiply".to_string(), 811 | severity: "Medium".to_string(), 812 | pattern: r"\w+\s*=\s*\w+\s*/\s*\w+\s*\*\s*\w+".to_string(), 813 | safe_pattern: None, 814 | description: "Performing division before multiplication can lead to precision loss.".to_string(), 815 | suggested_fix: "Consider ordering multiplication before division.".to_string(), 816 | }, 817 | VulnerabilityCheck { 818 | id: "SOLIDITY041".to_string(), 819 | language: "Solidity-Ethereum".to_string(), 820 | title: "Reentrancy Vulnerabilities (No Ether)".to_string(), 821 | severity: "Medium".to_string(), 822 | pattern: r"msg\.sender\.call\(\)\s*[^;]*;".to_string(), 823 | safe_pattern: None, 824 | description: "Detection of reentrancy bug that does not involve Ether.".to_string(), 825 | suggested_fix: "Apply the check-effects-interactions pattern.".to_string(), 826 | }, 827 | VulnerabilityCheck { 828 | id: "SOLIDITY042".to_string(), 829 | language: "Solidity-Ethereum".to_string(), 830 | title: "Reused Base Constructors".to_string(), 831 | severity: "Medium".to_string(), 832 | pattern: r"constructor\(\)\s+(public|internal|external|private)\s+.*\(\)".to_string(), 833 | safe_pattern: None, 834 | description: "Detects if the same base constructor is called with arguments from two different locations.".to_string(), 835 | suggested_fix: "Remove the duplicate constructor call.".to_string(), 836 | }, 837 | VulnerabilityCheck { 838 | id: "SOLIDITY043".to_string(), 839 | language: "Solidity-Ethereum".to_string(), 840 | title: "Dangerous Usage of tx.origin".to_string(), 841 | severity: "Medium".to_string(), 842 | pattern: r"tx\.origin".to_string(), 843 | safe_pattern: None, 844 | description: "tx.origin-based protection can be abused by a malicious contract.".to_string(), 845 | suggested_fix: "Do not use tx.origin for authorization.".to_string(), 846 | }, 847 | VulnerabilityCheck { 848 | id: "SOLIDITY044".to_string(), 849 | language: "Solidity-Ethereum".to_string(), 850 | title: "Unchecked Low-Level Calls".to_string(), 851 | severity: "Medium".to_string(), 852 | pattern: r"\.call\(\)|\.delegatecall\(\)".to_string(), 853 | safe_pattern: None, 854 | description: "The return value of a low-level call is not checked.".to_string(), 855 | suggested_fix: "Ensure that the return value of a low-level call is checked or logged.".to_string(), 856 | }, 857 | VulnerabilityCheck { 858 | id: "SOLIDITY045".to_string(), 859 | language: "Solidity-Ethereum".to_string(), 860 | title: "Unchecked Send".to_string(), 861 | severity: "Medium".to_string(), 862 | pattern: r"\.send\(".to_string(), 863 | safe_pattern: None, 864 | description: "The return value of a send is not checked.".to_string(), 865 | suggested_fix: "Ensure that the return value of send is checked or logged.".to_string(), 866 | }, 867 | VulnerabilityCheck { 868 | id: "SOLIDITY047".to_string(), 869 | language: "Solidity-Ethereum".to_string(), 870 | title: "Return Bomb".to_string(), 871 | severity: "Low".to_string(), 872 | pattern: r"revert\\(0,\\s*1000000\\)".to_string(), 873 | safe_pattern: None, 874 | description: "A low-level callee may consume all caller's gas unexpectedly.".to_string(), 875 | suggested_fix: "Avoid unlimited implicit decoding of returndata.".to_string(), 876 | }, 877 | VulnerabilityCheck { 878 | id: "SOLIDITY048".to_string(), 879 | language: "Solidity-Ethereum".to_string(), 880 | title: "Block Timestamp".to_string(), 881 | severity: "Low".to_string(), 882 | pattern: r"block\\.timestamp".to_string(), 883 | safe_pattern: None, 884 | description: "Dangerous usage of block.timestamp, which can be manipulated by miners.".to_string(), 885 | suggested_fix: "Avoid relying on block.timestamp for critical logic or randomness.".to_string(), 886 | }, 887 | ] 888 | } 889 | --------------------------------------------------------------------------------