solidity-argus 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/AGENTS.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +249 -0
  4. package/package.json +43 -0
  5. package/skills/INVENTORY.md +79 -0
  6. package/skills/README.md +56 -0
  7. package/skills/checklists/cyfrin-best-practices-runtime/SKILL.md +424 -0
  8. package/skills/checklists/cyfrin-best-practices-upgrades/SKILL.md +157 -0
  9. package/skills/checklists/cyfrin-defi-core/SKILL.md +373 -0
  10. package/skills/checklists/cyfrin-defi-integrations/SKILL.md +412 -0
  11. package/skills/checklists/cyfrin-gas/SKILL.md +55 -0
  12. package/skills/checklists/general-audit/SKILL.md +433 -0
  13. package/skills/methodology/audit-workflow/SKILL.md +129 -0
  14. package/skills/methodology/report-template/SKILL.md +190 -0
  15. package/skills/methodology/severity-classification/SKILL.md +179 -0
  16. package/skills/protocol-patterns/amm-dex/SKILL.md +229 -0
  17. package/skills/protocol-patterns/bridges-cross-chain/SKILL.md +317 -0
  18. package/skills/protocol-patterns/dao-governance/SKILL.md +281 -0
  19. package/skills/protocol-patterns/lending-borrowing/SKILL.md +221 -0
  20. package/skills/protocol-patterns/staking-vesting/SKILL.md +247 -0
  21. package/skills/references/exploit-reference/SKILL.md +259 -0
  22. package/skills/references/smartbugs-examples/SKILL.md +296 -0
  23. package/skills/vulnerability-patterns/access-control/SKILL.md +298 -0
  24. package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +59 -0
  25. package/skills/vulnerability-patterns/assert-violation/SKILL.md +59 -0
  26. package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +61 -0
  27. package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +55 -0
  28. package/skills/vulnerability-patterns/default-visibility/SKILL.md +62 -0
  29. package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +60 -0
  30. package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +59 -0
  31. package/skills/vulnerability-patterns/dos-revert/SKILL.md +72 -0
  32. package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +249 -0
  33. package/skills/vulnerability-patterns/floating-pragma/SKILL.md +51 -0
  34. package/skills/vulnerability-patterns/hash-collision/SKILL.md +52 -0
  35. package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +61 -0
  36. package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +60 -0
  37. package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +59 -0
  38. package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +61 -0
  39. package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +61 -0
  40. package/skills/vulnerability-patterns/logic-errors/SKILL.md +333 -0
  41. package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +60 -0
  42. package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +66 -0
  43. package/skills/vulnerability-patterns/off-by-one/SKILL.md +67 -0
  44. package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +252 -0
  45. package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +65 -0
  46. package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +61 -0
  47. package/skills/vulnerability-patterns/reentrancy/SKILL.md +266 -0
  48. package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +72 -0
  49. package/skills/vulnerability-patterns/signature-malleability/SKILL.md +59 -0
  50. package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +63 -0
  51. package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +52 -0
  52. package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +65 -0
  53. package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +61 -0
  54. package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +63 -0
  55. package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +56 -0
  56. package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +80 -0
  57. package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +69 -0
  58. package/skills/vulnerability-patterns/unused-variables/SKILL.md +70 -0
  59. package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +81 -0
  60. package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +77 -0
  61. package/skills/vulnerability-patterns/weird-tokens/SKILL.md +294 -0
  62. package/src/agents/argus-prompt.ts +407 -0
  63. package/src/agents/pythia-prompt.ts +134 -0
  64. package/src/agents/scribe-prompt.ts +87 -0
  65. package/src/agents/sentinel-prompt.ts +133 -0
  66. package/src/cli/cli-program.ts +67 -0
  67. package/src/cli/commands/doctor.ts +83 -0
  68. package/src/cli/commands/init.ts +46 -0
  69. package/src/cli/commands/install.ts +55 -0
  70. package/src/cli/index.ts +13 -0
  71. package/src/cli/tui-prompts.ts +75 -0
  72. package/src/cli/types.ts +9 -0
  73. package/src/config/index.ts +3 -0
  74. package/src/config/loader.ts +36 -0
  75. package/src/config/schema.ts +82 -0
  76. package/src/config/types.ts +4 -0
  77. package/src/constants/defaults.ts +6 -0
  78. package/src/create-hooks.ts +84 -0
  79. package/src/create-managers.ts +26 -0
  80. package/src/create-tools.ts +30 -0
  81. package/src/features/audit-enforcer/audit-enforcer.ts +34 -0
  82. package/src/features/audit-enforcer/index.ts +1 -0
  83. package/src/features/background-agent/background-manager.ts +200 -0
  84. package/src/features/background-agent/index.ts +1 -0
  85. package/src/features/context-monitor/context-monitor.ts +48 -0
  86. package/src/features/context-monitor/index.ts +4 -0
  87. package/src/features/context-monitor/tool-output-truncator.ts +17 -0
  88. package/src/features/error-recovery/index.ts +2 -0
  89. package/src/features/error-recovery/session-recovery.ts +27 -0
  90. package/src/features/error-recovery/tool-error-recovery.ts +35 -0
  91. package/src/features/index.ts +5 -0
  92. package/src/features/persistent-state/audit-state-manager.ts +121 -0
  93. package/src/features/persistent-state/index.ts +1 -0
  94. package/src/hooks/compaction-hook.ts +50 -0
  95. package/src/hooks/config-handler.ts +116 -0
  96. package/src/hooks/event-hook-v2.ts +93 -0
  97. package/src/hooks/event-hook.ts +74 -0
  98. package/src/hooks/hook-system.ts +9 -0
  99. package/src/hooks/index.ts +5 -0
  100. package/src/hooks/knowledge-sync-hook.ts +57 -0
  101. package/src/hooks/safe-create-hook.ts +15 -0
  102. package/src/hooks/system-prompt-hook.ts +126 -0
  103. package/src/hooks/tool-tracking-hook.ts +234 -0
  104. package/src/hooks/types.ts +16 -0
  105. package/src/index.ts +36 -0
  106. package/src/knowledge/scvd-client.ts +242 -0
  107. package/src/knowledge/scvd-index.ts +183 -0
  108. package/src/knowledge/scvd-sync.ts +85 -0
  109. package/src/managers/index.ts +1 -0
  110. package/src/managers/types.ts +85 -0
  111. package/src/plugin-interface.ts +38 -0
  112. package/src/shared/binary-utils.ts +63 -0
  113. package/src/shared/deep-merge.ts +71 -0
  114. package/src/shared/file-utils.ts +56 -0
  115. package/src/shared/index.ts +5 -0
  116. package/src/shared/jsonc-parser.ts +39 -0
  117. package/src/shared/logger.ts +36 -0
  118. package/src/state/audit-state.ts +27 -0
  119. package/src/state/finding-store.ts +126 -0
  120. package/src/state/plugin-state.ts +14 -0
  121. package/src/state/types.ts +61 -0
  122. package/src/tools/contract-analyzer-tool.ts +184 -0
  123. package/src/tools/forge-fuzz-tool.ts +311 -0
  124. package/src/tools/forge-test-tool.ts +397 -0
  125. package/src/tools/pattern-checker-tool.ts +337 -0
  126. package/src/tools/report-generator-tool.ts +308 -0
  127. package/src/tools/slither-tool.ts +465 -0
  128. package/src/tools/solodit-search-tool.ts +131 -0
  129. package/src/tools/sync-knowledge-tool.ts +116 -0
  130. package/src/utils/project-detector.ts +133 -0
  131. package/src/utils/solidity-parser.ts +174 -0
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: dos-gas-limit
3
+ description: - Contract iterates over a dynamic array or mapping whose size can grow unboundedly
4
+ ---
5
+ <!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
6
+
7
+ # DoS with Block Gas Limit
8
+
9
+ ## Preconditions
10
+ - Contract iterates over a dynamic array or mapping whose size can grow unboundedly
11
+ - The iteration must complete in a single transaction (no batching/pagination)
12
+ - OR: time-sensitive logic where block stuffing by an attacker can delay transaction inclusion
13
+
14
+ ## Vulnerable Pattern
15
+ ```solidity
16
+ address[] public recipients;
17
+
18
+ function addRecipient(address r) external {
19
+ recipients.push(r); // Array grows without bound
20
+ }
21
+
22
+ // Push-payment: one tx must process all recipients
23
+ function distributeRewards() external {
24
+ for (uint256 i = 0; i < recipients.length; i++) {
25
+ // When recipients.length grows large enough,
26
+ // this loop exceeds block gas limit and ALWAYS reverts
27
+ payable(recipients[i]).transfer(reward);
28
+ }
29
+ }
30
+ ```
31
+
32
+ ## Detection Heuristics
33
+ 1. Identify all loops (`for`, `while`) in the codebase
34
+ 2. For each loop, check if the iteration count depends on a dynamic array or storage structure that can grow over time
35
+ 3. If the loop is unbounded and must complete in a single transaction, flag it — it will eventually exceed the block gas limit
36
+ 4. Check if the function supports batching or pagination (e.g., `startIndex`, `batchSize` parameters) — if not, flag it
37
+ 5. For time-sensitive functions (auctions, deadlines, liquidations), check if an attacker could stuff blocks with high-gas transactions to delay inclusion
38
+
39
+ ## False Positives
40
+ - Loop iterates over a fixed-size or bounded array (e.g., `uint256[10]`, array with a capped `maxLength`)
41
+ - Function supports paginated/batched execution across multiple transactions
42
+ - Loop iteration count is controlled by the caller (e.g., batch size parameter with reasonable max)
43
+ - The array is admin-only appendable and has a practical maximum
44
+
45
+ ## Remediation
46
+ - Replace push-payment (contract sends to all) with pull-payment (recipients withdraw individually)
47
+ - If iteration is unavoidable, add batching/pagination with `startIndex` and `batchSize` parameters
48
+ - Cap array sizes with a maximum length check on push operations
49
+ - For time-sensitive logic, avoid designs where block stuffing can be profitable
50
+ ```solidity
51
+ // Pull-payment pattern
52
+ mapping(address => uint256) public pendingWithdrawals;
53
+
54
+ function claimReward() external {
55
+ uint256 amount = pendingWithdrawals[msg.sender];
56
+ pendingWithdrawals[msg.sender] = 0;
57
+ payable(msg.sender).transfer(amount);
58
+ }
59
+ ```
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: dos-revert
3
+ description: - Critical contract logic depends on an external call succeeding
4
+ ---
5
+ <!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
6
+
7
+ # DoS with (Unexpected) Revert
8
+
9
+ ## Preconditions
10
+ - Critical contract logic depends on an external call succeeding
11
+ - A single revert in the external call blocks the entire function
12
+ - OR: strict equality checks on contract balance can be violated by force-sent ETH
13
+ - OR: division by zero is possible due to unvalidated denominators
14
+
15
+ ## Vulnerable Pattern
16
+ ```solidity
17
+ // Push-payment: one reverting recipient blocks all payments
18
+ function payAll() external {
19
+ for (uint256 i = 0; i < recipients.length; i++) {
20
+ // If ANY recipient reverts (e.g., contract with no receive()),
21
+ // the entire function reverts — no one gets paid
22
+ require(payable(recipients[i]).send(amounts[i]), "transfer failed");
23
+ }
24
+ }
25
+
26
+ // Strict balance check broken by force-sent ETH
27
+ function withdraw() external {
28
+ // Attacker sends ETH via selfdestruct, breaking this check
29
+ require(address(this).balance == expectedBalance, "invariant");
30
+ _processWithdrawal();
31
+ }
32
+
33
+ // Division by zero
34
+ function distribute(uint256 totalShares) external {
35
+ // If totalShares == 0, this reverts and blocks the function
36
+ uint256 perShare = totalRewards / totalShares;
37
+ }
38
+ ```
39
+
40
+ ## Detection Heuristics
41
+ 1. Search for loops containing `require` or `assert` on external call results — one failure blocks all iterations
42
+ 2. Search for push-payment patterns: contract iterating over recipients and sending ETH/tokens in one transaction
43
+ 3. Search for strict balance equality checks (`address(this).balance ==`) — these can be broken by `selfdestruct` or coinbase rewards force-sending ETH
44
+ 4. Search for division operations and check if the denominator can be zero
45
+ 5. Check for `require(success)` after `.send()` or `.call()` inside loops — this turns a single recipient failure into a full DoS
46
+ 6. Look for "highest bidder" or "king of the hill" patterns where the current leader's refund must succeed for a new leader to be set
47
+
48
+ ## False Positives
49
+ - Pull-payment pattern is used (each recipient withdraws individually)
50
+ - The external call target is a trusted, known contract that will not revert
51
+ - Division denominator is guaranteed non-zero by prior checks or invariants
52
+ - Balance checks use `>=` instead of `==`
53
+ - The function handles individual failures gracefully (try/catch, continue on failure)
54
+
55
+ ## Remediation
56
+ - Replace push-payment with pull-payment: let recipients withdraw individually
57
+ - Use `>=` instead of `==` for balance checks to tolerate force-sent ETH
58
+ - Validate all denominators before division: `require(totalShares > 0)`
59
+ - In loops, handle individual call failures without reverting the whole transaction
60
+ - Use try/catch for external calls where failure should not be fatal
61
+ ```solidity
62
+ // Pull-payment pattern
63
+ mapping(address => uint256) public pendingWithdrawals;
64
+
65
+ function claimPayment() external {
66
+ uint256 amount = pendingWithdrawals[msg.sender];
67
+ require(amount > 0, "nothing to claim");
68
+ pendingWithdrawals[msg.sender] = 0;
69
+ (bool success,) = msg.sender.call{value: amount}("");
70
+ require(success);
71
+ }
72
+ ```
@@ -0,0 +1,249 @@
1
+ ---
2
+ name: flash-loan-attacks
3
+ description: Flash-loan attack mechanics, exploit archetypes, and mitigations for capital-amplified threats.
4
+ ---
5
+
6
+ <!-- Source: DeFiFoFum/fofum-solidity-skills (MIT) -->
7
+ <!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
8
+
9
+ # Flash Loan Attack Exploits
10
+
11
+ ## Overview
12
+
13
+ Flash loans enable borrowing massive amounts with zero collateral, repaid within the same transaction. Attackers use this capital to:
14
+ - Manipulate prices
15
+ - Exploit governance
16
+ - Amplify arbitrage
17
+ - Attack economic assumptions
18
+
19
+ **Key insight:** Any assumption about "no one would have $100M" is broken by flash loans.
20
+
21
+ ---
22
+
23
+ ## Attack Patterns
24
+
25
+ ### 1. Price Manipulation
26
+
27
+ ```
28
+ 1. Flash borrow $100M
29
+ 2. Swap to move AMM price
30
+ 3. Interact with victim (borrow, liquidate, swap)
31
+ 4. Swap back
32
+ 5. Repay flash loan
33
+ 6. Profit
34
+ ```
35
+
36
+ ### 2. Governance Attacks
37
+
38
+ ```
39
+ 1. Flash borrow governance tokens
40
+ 2. Create proposal or vote
41
+ 3. If snapshot-based: exploit timing
42
+ 4. Return tokens
43
+ ```
44
+
45
+ ```solidity
46
+ // VULNERABLE: Instant voting power
47
+ function vote(uint256 proposalId, bool support) external {
48
+ uint256 votes = token.balanceOf(msg.sender); // Current balance!
49
+ proposals[proposalId].votes += votes;
50
+ }
51
+
52
+ // SECURE: Historical voting power
53
+ function vote(uint256 proposalId, bool support) external {
54
+ uint256 votes = token.getPastVotes(msg.sender, proposals[proposalId].snapshot);
55
+ proposals[proposalId].votes += votes;
56
+ }
57
+ ```
58
+
59
+ ### 3. Reentrancy Amplification
60
+
61
+ Flash loans amplify reentrancy by providing initial capital:
62
+ 1. Flash loan large amount
63
+ 2. Deposit into vulnerable protocol
64
+ 3. Reenter to drain more than deposited
65
+ 4. Repay loan with profits
66
+
67
+ ### 4. Collateral Manipulation
68
+
69
+ ```
70
+ 1. Flash loan collateral asset
71
+ 2. Deposit as collateral in lending protocol
72
+ 3. Borrow maximum against it
73
+ 4. Manipulate collateral price down
74
+ 5. Abandon position (bad debt)
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Real Exploits
80
+
81
+ ### Euler Finance (Mar 2023) — $197M
82
+
83
+ **What happened:**
84
+ - Attacker exploited donateToReserves() function
85
+ - Flash loaned DAI, created leveraged position
86
+ - Used donate function to inflate debt without updating health
87
+ - Triggered liquidation at favorable rate
88
+
89
+ **Root cause:** donateToReserves() increased liabilities without proper health check
90
+
91
+ **Lesson:** All state-changing functions must maintain invariants.
92
+
93
+ ### Cream Finance (Oct 2021) — $130M
94
+
95
+ **What happened:**
96
+ - Attacker used flash loan to manipulate yUSD price
97
+ - Created fake collateral value through price oracle manipulation
98
+ - Borrowed real assets against fake collateral
99
+
100
+ **Root cause:** Oracle manipulation + flash loan capital
101
+
102
+ ### Beanstalk (Apr 2022) — $182M
103
+
104
+ **What happened:**
105
+ - Attacker flash loaned governance tokens (BEAN + LP)
106
+ - Achieved quorum for malicious proposal
107
+ - Proposal drained treasury
108
+ - All in one transaction
109
+
110
+ **Root cause:** No time delay between proposal and execution
111
+
112
+ **Lesson:** Governance needs timelocks AND snapshot-based voting.
113
+
114
+ ### Pancake Bunny (May 2021) — $45M
115
+
116
+ **What happened:**
117
+ - Flash loaned BNB
118
+ - Manipulated BUNNY/BNB price
119
+ - Minted excessive BUNNY tokens at wrong price
120
+ - Dumped and repaid
121
+
122
+ **Root cause:** Flash-loan-vulnerable price calculation
123
+
124
+ ---
125
+
126
+ ## Detection Checklist
127
+
128
+ - [ ] Does the protocol assume capital constraints ("no one has $100M")?
129
+ - [ ] Can governance actions occur in same block as token acquisition?
130
+ - [ ] Does the protocol use spot prices? (See oracle.md)
131
+ - [ ] Are there any actions profitable only with large capital?
132
+ - [ ] Can positions be opened and closed in same transaction profitably?
133
+ - [ ] Is collateral value determined at time of borrow?
134
+ - [ ] Are there timelocks on sensitive operations?
135
+ - [ ] Does the protocol track historical balances for voting?
136
+
137
+ ## Flash Loan Sources
138
+
139
+ Attackers can source flash loans from:
140
+ - **Aave** — Largest, 0.09% fee, many assets
141
+ - **dYdX** — No fee (technically flash + repay)
142
+ - **Uniswap V2/V3** — Flash swaps, 0.3% fee
143
+ - **Balancer** — Flash loans, dynamic fee
144
+ - **Maker** — DAI flash mint (up to debt ceiling)
145
+
146
+ **Combined capital:** $10B+ available in single transaction
147
+
148
+ ## Secure Patterns
149
+
150
+ ### Snapshot-Based Governance
151
+
152
+ ```solidity
153
+ // ERC20Votes pattern
154
+ mapping(address => Checkpoint[]) private _checkpoints;
155
+
156
+ function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) {
157
+ require(blockNumber < block.number, "Block not yet mined");
158
+ return _checkpointsLookup(_checkpoints[account], blockNumber);
159
+ }
160
+ ```
161
+
162
+ ### Time-Delayed Actions
163
+
164
+ ```solidity
165
+ uint256 public constant TIMELOCK = 2 days;
166
+ mapping(bytes32 => uint256) public pendingActions;
167
+
168
+ function queueAction(bytes32 actionHash) external onlyGovernance {
169
+ pendingActions[actionHash] = block.timestamp + TIMELOCK;
170
+ }
171
+
172
+ function executeAction(bytes32 actionHash) external {
173
+ require(pendingActions[actionHash] != 0, "Not queued");
174
+ require(block.timestamp >= pendingActions[actionHash], "Timelock");
175
+ delete pendingActions[actionHash];
176
+ // Execute
177
+ }
178
+ ```
179
+
180
+ ### Flash Loan Guards
181
+
182
+ ```solidity
183
+ // Detect if called within flash loan context
184
+ modifier noFlashLoan() {
185
+ require(
186
+ block.number > lastDepositBlock[msg.sender],
187
+ "Same block"
188
+ );
189
+ _;
190
+ }
191
+ ```
192
+
193
+ ---
194
+
195
+ ## References
196
+
197
+ - [Flash Loan Attack Taxonomy (Arxiv)](https://arxiv.org/abs/2003.03810)
198
+ - [Beanstalk Exploit Analysis](https://rekt.news/beanstalk-rekt/)
199
+ - [Euler Exploit (BlockSec)](https://blocksec.com/blog/how-the-200m-euler-exploit-worked)
200
+
201
+ ## Supplemental Heuristics (kadenzipfel)
202
+
203
+ ## Preconditions
204
+ - Contract uses `block.timestamp` (or the deprecated `now` alias) for security-sensitive logic
205
+ - The outcome of that logic can be influenced by a timestamp shift within the manipulation window (~15s on PoW chains, slot-fixed on PoS Ethereum but variable on L2s/sidechains)
206
+ - OR: `block.number` is used as a proxy for elapsed time
207
+
208
+ ## Vulnerable Pattern
209
+ ```solidity
210
+ // Timestamp as sole randomness source
211
+ function roll() external {
212
+ // Validator can manipulate block.timestamp to bias outcome
213
+ uint256 result = uint256(keccak256(abi.encodePacked(block.timestamp))) % 6;
214
+ if (result == 0) {
215
+ _payWinner(msg.sender);
216
+ }
217
+ }
218
+
219
+ // Tight time window vulnerable to manipulation
220
+ function claimBonus() external {
221
+ // 15-second window — validator can push timestamp to include/exclude
222
+ require(block.timestamp >= deadline && block.timestamp <= deadline + 15);
223
+ _sendBonus(msg.sender);
224
+ }
225
+ ```
226
+
227
+ ## Detection Heuristics
228
+ 1. Search for `block.timestamp` and `now` (deprecated alias) usage
229
+ 2. If used for randomness (e.g., fed into `keccak256` for a random value), flag immediately — this is always exploitable
230
+ 3. If used in conditional logic, check the time window: can a ~15-second manipulation affect the outcome?
231
+ 4. Search for `block.number` used as a time proxy (e.g., `block.number * 12` for seconds) — flag as fragile since block times change
232
+ 5. For L2/sidechain deployments, check chain-specific timestamp guarantees — some have weaker constraints than mainnet PoS
233
+
234
+ ## False Positives
235
+ - `block.timestamp` used only for logging or non-critical display purposes
236
+ - Time windows are large enough (hours/days) that a 15-second manipulation is irrelevant
237
+ - On PoS Ethereum mainnet, timestamps are fixed per 12-second slots — validator manipulation is constrained to slot boundaries, not arbitrary values
238
+ - `block.timestamp` used with a commit-reveal scheme where the timestamp alone doesn't determine the outcome
239
+
240
+ ## Remediation
241
+ - Never use `block.timestamp` for randomness — use Chainlink VRF or another verifiable randomness oracle
242
+ - For time-dependent logic, ensure the acceptable window is significantly larger than the manipulation range
243
+ - Avoid `block.number` as a time proxy; use `block.timestamp` with appropriate tolerance
244
+ - On L2s/sidechains, verify chain-specific timestamp constraints before relying on `block.timestamp`
245
+ ```solidity
246
+ // Safe: large time window where 15s manipulation is irrelevant
247
+ require(block.timestamp >= vestingEnd, "still vesting");
248
+ // vestingEnd is months/years away — manipulation doesn't matter
249
+ ```
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: floating-pragma
3
+ description: - Deployable contract uses a floating or range pragma (e.g., `pragma solidity ^0.8.0`, `pragma solidity >=0.8.0`)
4
+ ---
5
+ <!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
6
+
7
+ # Floating Pragma
8
+
9
+ ## Preconditions
10
+ - Deployable contract uses a floating or range pragma (e.g., `pragma solidity ^0.8.0`, `pragma solidity >=0.8.0`)
11
+ - The contract is intended for deployment (not a library or package for external consumption)
12
+
13
+ ## Vulnerable Pattern
14
+ ```solidity
15
+ // Floating pragma — could compile with any 0.8.x version
16
+ pragma solidity ^0.8.0;
17
+
18
+ contract Token {
19
+ // May be compiled with 0.8.0 (tested) or 0.8.25 (untested)
20
+ // Different compiler versions may have different bugs or behavior
21
+ mapping(address => uint256) public balances;
22
+ }
23
+
24
+ // Range pragma — even wider range
25
+ pragma solidity >=0.7.0 <0.9.0;
26
+ ```
27
+
28
+ ## Detection Heuristics
29
+ 1. Search for `pragma solidity` declarations in all `.sol` files
30
+ 2. If the pragma contains `^`, `>=`, `>`, or a range (e.g., `>=0.8.0 <0.9.0`), flag it as floating
31
+ 3. Check if the file is a deployable contract or a library/package — libraries are exempt
32
+ 4. A locked pragma looks like `pragma solidity 0.8.20;` (exact version, no caret or range)
33
+ 5. Check if different files in the project use different Solidity versions — this creates inconsistency risk
34
+
35
+ ## False Positives
36
+ - Libraries and packages intended for external consumption (e.g., npm packages, OpenZeppelin-style libraries) appropriately use floating pragmas for compatibility
37
+ - Interface files (`.sol` files containing only `interface` definitions) may use floating pragmas
38
+ - The floating pragma is in a test file, not a production contract
39
+
40
+ ## Remediation
41
+ - Use locked pragmas for all deployable contracts: `pragma solidity 0.8.20;`
42
+ - Verify the locked version is tested and free of known compiler bugs
43
+ - Maintain consistent Solidity versions across the project
44
+ ```solidity
45
+ // Locked pragma — deterministic compilation
46
+ pragma solidity 0.8.20;
47
+
48
+ contract Token {
49
+ mapping(address => uint256) public balances;
50
+ }
51
+ ```
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: hash-collision
3
+ description: - Contract uses `abi.encodePacked()` to encode data before hashing (typically with `keccak256`)
4
+ ---
5
+ <!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
6
+
7
+ # Hash Collision with abi.encodePacked()
8
+
9
+ ## Preconditions
10
+ - Contract uses `abi.encodePacked()` to encode data before hashing (typically with `keccak256`)
11
+ - Two or more adjacent arguments in the `encodePacked` call are variable-length types (strings, bytes, dynamic arrays)
12
+ - The resulting hash is used for authentication, deduplication, or signature verification
13
+
14
+ ## Vulnerable Pattern
15
+ ```solidity
16
+ function verify(string memory a, string memory b, bytes memory sig) external {
17
+ // abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c")
18
+ // Attacker shifts bytes between arguments to forge a valid hash
19
+ bytes32 hash = keccak256(abi.encodePacked(a, b));
20
+ require(ECDSA.recover(hash, sig) == trustedSigner);
21
+ _execute(a, b);
22
+ }
23
+
24
+ // Array variant:
25
+ // abi.encodePacked([addr1, addr2], [addr3])
26
+ // == abi.encodePacked([addr1], [addr2, addr3])
27
+ ```
28
+
29
+ ## Detection Heuristics
30
+ 1. Search for `abi.encodePacked(` calls
31
+ 2. Check how many arguments are variable-length types (string, bytes, dynamic arrays like `address[]`, `uint256[]`)
32
+ 3. If two or more adjacent arguments are variable-length, flag it — elements can be shifted between arguments to produce an identical encoding
33
+ 4. Check if the packed result feeds into `keccak256` for security-sensitive purposes (signature verification, access control, deduplication)
34
+ 5. If only one argument is variable-length, or all arguments are fixed-length (address, uint256, bool), it is safe
35
+
36
+ ## False Positives
37
+ - Only one argument is a variable-length type (no adjacent dynamic types to shift between)
38
+ - All arguments are fixed-length types (address, uint256, bytes32, bool, etc.)
39
+ - `abi.encode()` is used instead of `abi.encodePacked()` (includes length prefixes, no collision)
40
+ - The hash is not used for any security-sensitive purpose
41
+
42
+ ## Remediation
43
+ - Replace `abi.encodePacked()` with `abi.encode()` — it includes length prefixes that prevent collisions
44
+ - If `encodePacked` must be used for gas efficiency, ensure at most one argument is a variable-length type
45
+ - Alternatively, separate variable-length arguments with fixed-length delimiters
46
+ ```solidity
47
+ // Safe: abi.encode includes length prefixes
48
+ bytes32 hash = keccak256(abi.encode(a, b));
49
+
50
+ // Also safe: only one variable-length argument
51
+ bytes32 hash = keccak256(abi.encodePacked(fixedAddr, dynamicString));
52
+ ```
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: inadherence-to-standards
3
+ description: - Contract claims to implement a standard (ERC20, ERC721, ERC1155, etc.) but deviates from the specification
4
+ ---
5
+ <!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
6
+
7
+ # Inadherence to Standards
8
+
9
+ ## Preconditions
10
+ - Contract claims to implement a standard (ERC20, ERC721, ERC1155, etc.) but deviates from the specification
11
+ - OR: contract integrates external tokens assuming strict standard compliance without handling common deviations
12
+
13
+ ## Vulnerable Pattern
14
+ ```solidity
15
+ // Non-compliant ERC20: missing return value on transfer
16
+ // (matches USDT, BNB behavior — breaks callers that check return)
17
+ function transfer(address to, uint256 amount) external {
18
+ balances[msg.sender] -= amount;
19
+ balances[to] += amount;
20
+ // Missing: return true;
21
+ // Missing: emit Transfer(msg.sender, to, amount);
22
+ }
23
+
24
+ // Caller assumes strict compliance — breaks on non-compliant tokens
25
+ function depositToken(IERC20 token, uint256 amount) external {
26
+ // Reverts on tokens that don't return bool (USDT)
27
+ require(token.transfer(address(this), amount), "transfer failed");
28
+ deposits[msg.sender] += amount;
29
+ // Bug: doesn't account for fee-on-transfer tokens
30
+ // Actual received amount may be less than `amount`
31
+ }
32
+ ```
33
+
34
+ ## Detection Heuristics
35
+ 1. For token implementations: check that all required functions, return values, and events match the standard exactly (e.g., ERC20 requires `transfer` returns `bool` and emits `Transfer`)
36
+ 2. For token integrations: check if `SafeERC20` is used for `transfer`/`transferFrom`/`approve` calls — raw IERC20 calls break on non-compliant tokens
37
+ 3. Check for hardcoded assumptions: 18 decimals, no fee-on-transfer, no rebasing, no blocklists
38
+ 4. For fee-on-transfer tokens: check if the contract uses balance-before/balance-after pattern to measure actual received amount
39
+ 5. Check for missing `safeTransfer`/`safeTransferFrom` wrappers
40
+
41
+ ## False Positives
42
+ - The contract explicitly documents that it only supports fully compliant ERC20 tokens and enforces this via a whitelist
43
+ - `SafeERC20` from OpenZeppelin is used, which handles missing return values
44
+ - The contract checks balance differences to account for fee-on-transfer
45
+
46
+ ## Remediation
47
+ - For token implementations: strictly follow the standard — include all return values, events, and function signatures
48
+ - For token integrations: use OpenZeppelin's `SafeERC20` for all token interactions
49
+ - Use balance-before/balance-after pattern for fee-on-transfer support
50
+ - Don't hardcode decimals — read from the token contract
51
+ ```solidity
52
+ import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
53
+ using SafeERC20 for IERC20;
54
+
55
+ function depositToken(IERC20 token, uint256 amount) external {
56
+ uint256 balBefore = token.balanceOf(address(this));
57
+ token.safeTransferFrom(msg.sender, address(this), amount);
58
+ uint256 received = token.balanceOf(address(this)) - balBefore;
59
+ deposits[msg.sender] += received;
60
+ }
61
+ ```
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: incorrect-constructor
3
+ description: - Solidity version <0.4.22 where constructors are named functions matching the contract name
4
+ ---
5
+ <!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
6
+
7
+ # Incorrect Constructor Name
8
+
9
+ ## Preconditions
10
+ - Solidity version <0.4.22 where constructors are named functions matching the contract name
11
+ - The function name does not exactly match the contract name (typo, case mismatch, or contract renamed without updating the constructor)
12
+
13
+ ## Vulnerable Pattern
14
+ ```solidity
15
+ // Solidity <0.4.22: constructor is a named function
16
+ contract Owned {
17
+ address public owner;
18
+
19
+ // Typo: "owned" != "Owned" (case mismatch)
20
+ // This becomes a regular public function anyone can call
21
+ function owned() public {
22
+ owner = msg.sender;
23
+ }
24
+ }
25
+
26
+ // Contract renamed but constructor not updated
27
+ contract Treasury {
28
+ address public owner;
29
+
30
+ // Was "Wallet" before rename — now a regular public function
31
+ function Wallet() public {
32
+ owner = msg.sender;
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## Detection Heuristics
38
+ 1. Check the Solidity version: if >=0.4.22 and the `constructor` keyword is used, this vulnerability does not apply
39
+ 2. For <0.4.22 contracts: find the function that sets initial state (owner, parameters) and verify its name exactly matches the contract name (case-sensitive)
40
+ 3. Search for public/external functions that set `owner` or perform one-time initialization — these may be misnamed constructors
41
+ 4. Check if the contract was renamed at any point (git history, comments) but the constructor function was not updated
42
+ 5. Flag any named function that appears to perform initialization logic (sets owner, initializes critical state) but doesn't match the contract name
43
+
44
+ ## False Positives
45
+ - Solidity >=0.4.22 using the `constructor` keyword (enforced by compiler)
46
+ - The function is intentionally a public initializer (e.g., in proxy patterns) with proper access control
47
+
48
+ ## Remediation
49
+ - Upgrade to Solidity >=0.4.22 and use the `constructor` keyword
50
+ - For legacy contracts, verify the constructor function name exactly matches the contract name
51
+ ```solidity
52
+ // Modern Solidity: compiler-enforced constructor
53
+ contract Owned {
54
+ address public owner;
55
+
56
+ constructor() {
57
+ owner = msg.sender;
58
+ }
59
+ }
60
+ ```
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: incorrect-inheritance-order
3
+ description: - Contract uses multiple inheritance (`is ContractA, ContractB, ...`)
4
+ ---
5
+ <!-- Source: kadenzipfel/smart-contract-vulnerabilities (MIT) -->
6
+
7
+ # Incorrect Inheritance Order
8
+
9
+ ## Preconditions
10
+ - Contract uses multiple inheritance (`is ContractA, ContractB, ...`)
11
+ - Two or more parent contracts define a function with the same name and signature
12
+ - The inheritance order (left-to-right) does not match the developer's intended resolution
13
+
14
+ ## Vulnerable Pattern
15
+ ```solidity
16
+ contract Ownable {
17
+ function owner() public view virtual returns (address) {
18
+ return _owner; // Returns EOA owner
19
+ }
20
+ }
21
+
22
+ contract Governance {
23
+ function owner() public view virtual returns (address) {
24
+ return governance; // Returns governance contract
25
+ }
26
+ }
27
+
28
+ // C3 linearization: rightmost (Ownable) takes precedence
29
+ // Developer intended Governance.owner() but gets Ownable.owner()
30
+ contract Treasury is Governance, Ownable {
31
+ // owner() resolves to Ownable (rightmost) — may not be intended
32
+ // Should be: is Ownable, Governance (if Governance should win)
33
+ }
34
+ ```
35
+
36
+ ## Detection Heuristics
37
+ 1. Identify all contracts using multiple inheritance (`is A, B, C`)
38
+ 2. For each inheritance chain, check if parent contracts define functions with the same name and signature
39
+ 3. Verify the inheritance order follows general-to-specific (most base first, most derived last) — Solidity's C3 linearization gives precedence to the rightmost parent
40
+ 4. Check `override` specifiers: `override(A, B)` should explicitly list which parents are being overridden
41
+ 5. Trace the actual resolution order and compare against documented or intended behavior
42
+
43
+ ## False Positives
44
+ - Only one parent defines the function (no ambiguity)
45
+ - The contract explicitly overrides the function with `override(A, B)` and provides its own implementation
46
+ - The inheritance order is intentionally general-to-specific and produces the correct resolution
47
+
48
+ ## Remediation
49
+ - Order inheritance from most base to most derived (general-to-specific, left-to-right)
50
+ - Explicitly override conflicting functions and specify which parents: `override(ContractA, ContractB)`
51
+ - Test function resolution in complex hierarchies
52
+ ```solidity
53
+ // Correct: general-to-specific order
54
+ contract Treasury is Ownable, Governance {
55
+ function owner() public view override(Ownable, Governance) returns (address) {
56
+ return Governance.owner(); // Explicit resolution
57
+ }
58
+ }
59
+ ```