Missing Lint Detectors in forge lint
this checklist compares Slither and Aderyn detectors against forge lint and merges equivalent detectors into single items.
NOTE: some items are blocked until Solar outputs a CFG (control-flow graph), as they need data-flow analysis, taint tracking, or path-sensitive reasoning.
Implementable Today (only require AST or HIR)
High
Medium
Low
Info / Optimization
Require CFG
these detectors need control-flow graph, data-flow/taint analysis, or path-sensitive reasoning that cannot be done with AST pattern matching alone.
High
Medium
Low
Info / Optimization
Additional context
ref: #11034 (comment)
Missing Lint Detectors in
forge lintthis checklist compares Slither and Aderyn detectors against
forge lintand merges equivalent detectors into single items.Implementable Today (only require AST or HIR)
High
encode-packed-collision—abi.encodePackedwith multiple dynamic types causes hash collisionsmsg-value-loop—msg.valuereused inside a loop (double-spend)delegatecall-loop— Payabledelegatecallinside a looprtlo— Right-to-left-override Unicode character hiding malicious codeincorrect-exp—^(xor) used instead of**(exponentiation)(OSS-381)yul-return—returnin assembly/Yul halts entire EVM execution, not just the function(OSS-25)suicidal— Unprotectedselfdestruct(no access-control modifier)weak-prng—block.timestamp/blockhashused as randomness source(OSS-18)shadowing-state— Child contract re-declares parent's state variable(OSS-23)controlled-array-length— Direct assignment to storage array.length(OSS-16)array-by-reference— Storage array modified via memory copy has no effectfunction-selector-collision— Colliding 4-byte function selectors(OSS-35)name-reused— Contract name reused across inheritance treeenumerable-loop-removal— Removing items fromEnumerableSetinside a loop usingat+removecorrupts orderMedium
tx-origin—tx.originused for authorizationmapping-deletion—deleteon struct containing a mapping doesn't clear ittautological-compare—x >= xalways truetautology—uint >= 0always true,uint8 < 512always trueerc20-interface— ERC20 functions with wrong return typeserc721-interface— ERC721 functions with wrong return typesboolean-cst—if(false)orb || true— misused boolean constant(OSS-51)domain-separator-collision— Function selector collides with EIP-2612DOMAIN_SEPARATOR()reused-constructor— Base constructor called with args from two inheritance pathsecrecover— Bareecrecoverwithout signature malleability checknon-reentrant-not-first—nonReentrantmodifier not listed firstunsafe-oz-erc721-mint—_mintinstead of_safeMintfor ERC721(OSS-45)out-of-order-retryable— Multiple L1→L2 retryable tickets depend on execution order(OSS-378)block-timestamp-deadline—block.timestampused as swap/tx deadline (proposer can hold)dangerous-unary-operator—x=+1instead ofx+=1Low
calls-loop— External calls inside unbounded loop (DoS risk)require-revert-in-loop—require/revertinside a loop (single failure reverts all)timestamp— Dangerousblock.timestampdependency(OSS-57)shadowing-local— Local variable shadows state variable(OSS-53)shadowing-builtin— Variable shadows Solidity builtin (now,assert, etc.)incorrect-modifier— Modifier doesn't always execute_or revertvoid-cst— Calling empty parent constructorcentralization-risk— Single-owner critical functionsdeprecated-oz-function— Deprecated OpenZeppelin functionsolmate-safe-transfer-lib— Solmate SafeTransferLib doesn't check contract existenceempty-block— Empty function bodyempty-require-revert—require()/revert()with no error message(OSS-381)multiple-placeholders— Modifier with multiple_placeholdersInfo / Optimization
constable-states— State variable never modified; should beconstantimmutable-states— State variable only set in constructor; should beimmutablecache-array-length— Storage.lengthre-read every loop iterationvar-read-using-this—this.xadds unnecessarySTATICCALLoverheadexternal-function—publicfunction never called internally; useexternalunused-state— Unused state variableunused-error— Unused custom error definitionboolean-equal—x == trueinstead of justx(OSS-76)redundant-statements— Statement with no side effectassert-state-change— State modification insideassert()too-many-digits—10000000000000000000instead of10 etherliteral-instead-of-constant— Magic numbers should be named constants(OSS-33)deprecated-standards—throw,msg.gas,sha3(),suicide()(OSS-380)erc20-indexed— Transfer/Approval events missingindexedkeywordunindexed-event-address— Event has address params but none indexedfunction-init-state— State variable initialized via non-pure function callmissing-inheritance— Contract implements interface functions but doesn't inherit it(OSS-31)solc-version— Outdated or overly complex pragmapragma— Different Solidity versions used across filescyclomatic-complexity— Function complexity exceeds thresholdincorrect-using-for—using L for Twhere L has no functions taking T(OSS-38)unimplemented-functions— Derived contract missing interface implementationstodo— TODO comments left in production code, available since solc 0.8.20push-0-opcode— Usepush0opcodeinconsistent-type-names— Inconsistent type naming across codebaseinternal-function-used-once— Internal function used only once; inline itmodifier-used-only-once— Modifier used only once; inline itRequire CFG
these detectors need control-flow graph, data-flow/taint analysis, or path-sensitive reasoning that cannot be done with AST pattern matching alone.
High
reentrancy-eth— Reentrancy that drains ETH (state change after external call)reentrancy-no-eth— Reentrancy that manipulates state without ETHreentrancy-balance— Reentrancy exploitingaddress(this).balancecheckarbitrary-send-erc20— Taint: attacker-controlledfromintransferFromarbitrary-send-erc20-permit— Taint:transferFrom+permitfront-runningarbitrary-send-eth— Taint: ETH sent to attacker-controlled destinationunprotected-upgrade— No access control oninitialize()across all pathscontrolled-delegatecall— Taint: user-controlled delegatecall targetuninitialized-state— State variable used before initialization on some path(OSS-19)uninitialized-storage— Storage pointer left uninitialized on some pathprotected-vars— Protected variable accessed without required modifierMedium
locked-ether— Contract accepts ETH (payable) but has no withdrawal pathincorrect-equality— Strict equality onaddress(this).balance(manipulable)unused-return— External call return value ignoredwrite-after-write— Variable written twice without intermediate readuninitialized-local— Local variable used before assignment on some pathcostly-loop—SSTOREinside loop bodyLow
missing-zero-check— Parameter flows to state variable write without!= address(0)checkevents-access— State change on access-control parameter without emitting eventreturn-bomb— Callee can returnbomb caller via unlimited returndata copy(OSS-66)variable-scope— Variable used before declaration is dominatedInfo / Optimization
(OSS-32)dead-code— Unreachable internal functions (call graph reachability)(OSS-59)reentrancy-benign— Benign reentrancy (double-call, no exploit)reentrancy-events— Reentrancy manipulates event emission orderreentrancy-unlimited-gas— Reentrancy viatransfer/sendwith gas repricing riskAdditional context
ref: #11034 (comment)