max impl and Detection set complete - #871
Merged
Merged
Conversation
|
@Sadeequ Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Soroban Analysis Rules — Implementation Report
Date: 2026-08-29
Author: Sadeequ
Scope: Four new Soroban static analysis rules for GasGuard
Summary
I implemented four new Soroban-specific static analysis rules in the GasGuard codebase to detect function complexity, deep control-flow nesting, repeated computations, and dead code in Soroban smart contracts. All rules follow the existing
SorobanRuletrait pattern, are registered in the default rule engine, and include comprehensive unit tests.Files Modified
packages/rules/src/soroban/rule_engine.rspackages/rules/src/soroban/mod.rspackages/rules/src/lib.rsIssue 1: Soroban Function Complexity Analyzer
Rule ID:
soroban-function-complexityStruct:
SorobanFunctionComplexityRuleLocation:
rule_engine.rs:1068-1235What I Did
I created a rule that calculates the cyclomatic complexity of each Soroban function by counting decision points in the control flow. The implementation starts with a base complexity of 1 and increments for each of the following patterns:
if,else ifwhile,for,loop {matchand=>(match arms)&&,||,?(logical/ternary operators)unwrap_or,unwrap_or_elseDesign Decisions
I implemented a
strip_strings_and_commentshelper that removes string literals and both line (//) and block (/* */) comments before counting. This prevents false positives where keywords appear inside string literals or comments. I reused this helper across multiple rules.The rule uses two configurable thresholds:
Tests
I wrote two tests:
test_function_complexity_simple— verifies a simple function with no control flow produces no violationstest_function_complexity_high— verifies a function with many nestedif/else if/for/while/matchconstructs triggers a High severity violationIssue 2: Deep Soroban Control-Flow Nesting
Rule ID:
soroban-deep-nestingStruct:
SorobanDeepNestingRuleLocation:
rule_engine.rs:1237-1360What I Did
I created a rule that detects deeply nested control-flow structures by tracking brace depth as it scans each line of a function body. When it encounters a control-flow keyword (
if,for,while,loop,match,else if,else {) at the start of a line, it records the current nesting depth.Design Decisions
The algorithm processes closing braces first (to exit scopes correctly), then checks if the current line is a control-flow construct. If so, it records the depth and increments for the control structure's body. Non-control lines with opening braces increase the depth for subsequent lines.
The default maximum allowed nesting depth is 4 levels. Functions exceeding this threshold are flagged with Medium severity.
I chose this approach over simple indentation tracking because Soroban code may use varying indentation styles, but brace nesting is structurally unambiguous.
Tests
I wrote two tests:
test_deep_nesting_detection— verifies that 5 levels of nestedifblocks triggers a violationtest_no_shallow_nesting— verifies that 2 levels of nesting (within threshold) produces no violationIssue 3: Repeated Soroban Computations
Rule ID:
soroban-repeated-computationsStruct:
SorobanRepeatedComputationsRuleLocation:
rule_engine.rs:1362-1529What I Did
I created a rule that detects identical expressions computed multiple times within a function. It extracts method chains (e.g.,
env.storage().instance().get(&key)) and parenthesized sub-expressions, then counts how many times each expression appears.Design Decisions
The expression extraction works in two phases:
.). For each such token, I extract the full chain and all suffix sub-chains (e.g., fromenv.storage().instance().get(&key), I also extractstorage().instance().get(&key),instance().get(&key), etc.).The rule uses two configurable parameters:
self.x)This targets the common Soroban anti-pattern where developers repeatedly call
env.storage().instance().get(&key)instead of caching the result in a local variable.Tests
I wrote one test:
test_repeated_computations_detection— verifies that a function callingenv.storage().instance().get(&user)three times triggers a violationIssue 4: Soroban Dead Code Detector
Rule ID:
soroban-dead-codeStruct:
SorobanDeadCodeRuleLocation:
rule_engine.rs:1531-1685What I Did
I created a rule that detects unreachable code after terminating statements. It tracks brace depth to identify when code appears after a terminating statement within the same block scope.
Design Decisions
The algorithm scans each line and:
return,panic!,break,continue, andpanic_with_errorI chose to include
panic_with_errorbecause it's a common Soroban pattern (viaenv.panic_with_error()) that also terminates execution flow.The rule reports at Warning severity since dead code is not a runtime bug but indicates a logic error or incomplete refactoring.
Tests
I wrote three tests:
test_dead_code_after_return— verifies code afterreturn 42;is flaggedtest_dead_code_after_panic— verifies code afterpanic!("error");is flaggedtest_no_dead_code_clean_function— verifies a clean function with no dead code produces no violationsIntegration
I registered all four rules in the
add_default_rules()method ofSorobanRuleEngine(lines 70-73 of the modified file):I also exported the new types from both
packages/rules/src/soroban/mod.rsandpackages/rules/src/lib.rsso they are available to downstream consumers.Testing Summary
test_function_complexity_simpletest_function_complexity_hightest_deep_nesting_detectiontest_no_shallow_nestingtest_repeated_computations_detectiontest_dead_code_after_returntest_dead_code_after_panictest_no_dead_code_clean_functionTotal: 9 new unit tests
How to Run the Tests
cargo test -p gasguard-rulesTo run only the new tests:
cargo test -p gasguard-rules -- test_function_complexity test_deep_nesting test_repeated_computations test_dead_codeDesign Principles Followed
SorobanRuletrait withid(),name(),description(),severity(),is_enabled(),set_enabled(), andapply()methodsRelated Issues