web3crit-scanner 7.0.1

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 (42) hide show
  1. package/README.md +685 -0
  2. package/bin/web3crit +10 -0
  3. package/package.json +59 -0
  4. package/src/analyzers/control-flow.js +256 -0
  5. package/src/analyzers/data-flow.js +720 -0
  6. package/src/analyzers/exploit-chain.js +751 -0
  7. package/src/analyzers/immunefi-classifier.js +515 -0
  8. package/src/analyzers/poc-validator.js +396 -0
  9. package/src/analyzers/solodit-enricher.js +1122 -0
  10. package/src/cli.js +546 -0
  11. package/src/detectors/access-control-enhanced.js +458 -0
  12. package/src/detectors/base-detector.js +213 -0
  13. package/src/detectors/callback-reentrancy.js +362 -0
  14. package/src/detectors/cross-contract-reentrancy.js +697 -0
  15. package/src/detectors/delegatecall.js +167 -0
  16. package/src/detectors/deprecated-functions.js +62 -0
  17. package/src/detectors/flash-loan.js +408 -0
  18. package/src/detectors/frontrunning.js +553 -0
  19. package/src/detectors/gas-griefing.js +701 -0
  20. package/src/detectors/governance-attacks.js +366 -0
  21. package/src/detectors/integer-overflow.js +487 -0
  22. package/src/detectors/oracle-manipulation.js +524 -0
  23. package/src/detectors/permit-exploits.js +368 -0
  24. package/src/detectors/precision-loss.js +408 -0
  25. package/src/detectors/price-manipulation-advanced.js +548 -0
  26. package/src/detectors/proxy-vulnerabilities.js +651 -0
  27. package/src/detectors/readonly-reentrancy.js +473 -0
  28. package/src/detectors/rebasing-token-vault.js +416 -0
  29. package/src/detectors/reentrancy-enhanced.js +359 -0
  30. package/src/detectors/selfdestruct.js +259 -0
  31. package/src/detectors/share-manipulation.js +412 -0
  32. package/src/detectors/signature-replay.js +409 -0
  33. package/src/detectors/storage-collision.js +446 -0
  34. package/src/detectors/timestamp-dependence.js +494 -0
  35. package/src/detectors/toctou.js +427 -0
  36. package/src/detectors/token-standard-compliance.js +465 -0
  37. package/src/detectors/unchecked-call.js +214 -0
  38. package/src/detectors/vault-inflation.js +421 -0
  39. package/src/index.js +42 -0
  40. package/src/package-lock.json +2874 -0
  41. package/src/package.json +39 -0
  42. package/src/scanner-enhanced.js +816 -0
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "web3crit-scanner",
3
+ "version": "7.0.1",
4
+ "description": "Top-tier exploit-driven DeFi scanner for Immunefi High/Critical payouts. Models flash loans, MEV, and adversarial capabilities.",
5
+ "main": "src/scanner-enhanced.js",
6
+ "bin": {
7
+ "web3crit": "./bin/web3crit"
8
+ },
9
+ "scripts": {
10
+ "test": "node test/test.js",
11
+ "install-global": "npm install -g .",
12
+ "scan": "node src/cli.js scan",
13
+ "scan:immunefi": "node src/cli.js scan --immunefi-only"
14
+ },
15
+ "preferGlobal": true,
16
+ "files": [
17
+ "bin/",
18
+ "src/",
19
+ "README.md",
20
+ "INSTALL.md"
21
+ ],
22
+ "keywords": [
23
+ "solidity",
24
+ "security",
25
+ "audit",
26
+ "vulnerability",
27
+ "smart-contract",
28
+ "ethereum",
29
+ "defi",
30
+ "reentrancy",
31
+ "flash-loan",
32
+ "immunefi",
33
+ "bug-bounty",
34
+ "exploit",
35
+ "governance",
36
+ "vault",
37
+ "static-analysis"
38
+ ],
39
+ "author": "critfinds <jsbtc1@proton.me>",
40
+ "license": "MIT",
41
+ "dependencies": {
42
+ "@solidity-parser/parser": "^0.20.2",
43
+ "commander": "^9.4.1",
44
+ "chalk": "^4.1.2",
45
+ "ora": "^5.4.1",
46
+ "boxen": "^5.1.2",
47
+ "cli-table3": "^0.6.3",
48
+ "gradient-string": "^2.0.2",
49
+ "figures": "^3.2.0"
50
+ },
51
+ "devDependencies": {},
52
+ "engines": {
53
+ "node": ">=14.0.0"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/critfinds/Web3CRIT-Scanner"
58
+ }
59
+ }
@@ -0,0 +1,256 @@
1
+ const parser = require('@solidity-parser/parser');
2
+
3
+ /**
4
+ * Control Flow Graph (CFG) Analyzer (Rewritten)
5
+ * Tracks execution paths, function calls, and state changes using the official parser visitor
6
+ */
7
+ class ControlFlowAnalyzer {
8
+ constructor() {
9
+ this.reset();
10
+ }
11
+
12
+ analyze(ast, sourceCode) {
13
+ this.sourceCode = sourceCode;
14
+ this.reset();
15
+
16
+ let currentContract = null;
17
+ let currentFunction = null;
18
+ let currentModifier = null;
19
+
20
+ parser.visit(ast, {
21
+ ContractDefinition: node => {
22
+ currentContract = {
23
+ name: node.name,
24
+ kind: node.kind,
25
+ baseContracts: node.baseContracts || [],
26
+ stateVariables: [],
27
+ functions: [],
28
+ modifiers: []
29
+ };
30
+ this.contracts.set(node.name, currentContract);
31
+ },
32
+
33
+ 'ContractDefinition:exit': () => {
34
+ currentContract = null;
35
+ },
36
+
37
+ ModifierDefinition: node => {
38
+ if (!currentContract) return;
39
+ const modKey = `${currentContract.name}.${node.name}`;
40
+ currentModifier = {
41
+ name: node.name,
42
+ contract: currentContract.name,
43
+ node: node,
44
+ requireStatements: [],
45
+ checksAccess: false,
46
+ checksOwnership: false,
47
+ checksRole: false
48
+ };
49
+ this.modifiers.set(modKey, currentModifier);
50
+ currentContract.modifiers.push(node.name);
51
+ },
52
+
53
+ 'ModifierDefinition:exit': () => {
54
+ // Analyze collected require statements to determine access control type
55
+ if (currentModifier) {
56
+ this.analyzeModifierAccessControl(currentModifier);
57
+ }
58
+ currentModifier = null;
59
+ },
60
+
61
+ FunctionDefinition: node => {
62
+ if (!currentContract) return;
63
+ const funcName = node.name || (node.isConstructor ? 'constructor' : (node.isReceiveEther ? 'receive' : 'fallback'));
64
+ const funcKey = `${currentContract.name}.${funcName}`;
65
+ currentFunction = {
66
+ name: funcName,
67
+ contract: currentContract.name,
68
+ visibility: node.visibility || 'public',
69
+ stateMutability: node.stateMutability,
70
+ modifiers: (node.modifiers || []).map(m => m.name),
71
+ parameters: (node.parameters || []).map(p => ({
72
+ name: p.name,
73
+ type: this.getTypeName(p.typeName)
74
+ })),
75
+ isConstructor: node.isConstructor,
76
+ isFallback: !node.name && !node.isConstructor && !node.isReceiveEther,
77
+ isReceive: node.isReceiveEther,
78
+ externalCalls: [],
79
+ stateWrites: [],
80
+ stateReads: [],
81
+ node: node
82
+ };
83
+ this.functions.set(funcKey, currentFunction);
84
+ currentContract.functions.push(funcName);
85
+ },
86
+
87
+ 'FunctionDefinition:exit': () => {
88
+ currentFunction = null;
89
+ },
90
+
91
+ StateVariableDeclaration: node => {
92
+ if (!currentContract) return;
93
+ node.variables.forEach(variable => {
94
+ const varInfo = {
95
+ name: variable.name,
96
+ type: this.getTypeName(variable.typeName),
97
+ visibility: variable.visibility || 'internal',
98
+ isConstant: variable.isDeclaredConst,
99
+ isImmutable: variable.isImmutable,
100
+ contract: currentContract.name
101
+ };
102
+ this.stateVariables.set(`${currentContract.name}.${variable.name}`, varInfo);
103
+ currentContract.stateVariables.push(variable.name);
104
+ });
105
+ },
106
+
107
+ FunctionCall: node => {
108
+ // Track require/assert statements in modifiers
109
+ if (currentModifier && node.expression && node.expression.type === 'Identifier') {
110
+ const funcName = node.expression.name;
111
+ if (funcName === 'require' || funcName === 'assert') {
112
+ const requireCode = this.getSourceFromNode(node);
113
+ currentModifier.requireStatements.push(requireCode);
114
+ }
115
+ }
116
+
117
+ if (!currentFunction) return;
118
+
119
+ // Handle direct MemberAccess: .send(), .transfer()
120
+ if (node.expression && node.expression.type === 'MemberAccess') {
121
+ const memberName = node.expression.memberName;
122
+ if (['call', 'delegatecall', 'staticcall', 'send', 'transfer'].includes(memberName)) {
123
+ currentFunction.externalCalls.push({
124
+ type: memberName,
125
+ target: this.getSourceFromNode(node.expression.expression),
126
+ loc: node.loc
127
+ });
128
+ }
129
+ }
130
+
131
+ // Handle NameValueExpression: .call{value: x}(), .call{gas: x}()
132
+ if (node.expression && node.expression.type === 'NameValueExpression') {
133
+ const innerExpr = node.expression.expression;
134
+ if (innerExpr && innerExpr.type === 'MemberAccess') {
135
+ const memberName = innerExpr.memberName;
136
+ if (['call', 'delegatecall', 'staticcall'].includes(memberName)) {
137
+ currentFunction.externalCalls.push({
138
+ type: memberName,
139
+ target: this.getSourceFromNode(innerExpr.expression),
140
+ loc: node.loc
141
+ });
142
+ }
143
+ }
144
+ }
145
+ },
146
+
147
+ Identifier: node => {
148
+ // Track state variable reads
149
+ if (currentFunction && currentContract && this.isStateVariable(node, currentContract.name)) {
150
+ currentFunction.stateReads.push({
151
+ variable: node.name,
152
+ loc: node.loc
153
+ });
154
+ }
155
+ },
156
+
157
+ BinaryOperation: node => {
158
+ if (!currentFunction || !currentContract) return;
159
+ // Track state writes for assignment operators
160
+ if (node.operator === '=' || node.operator === '+=' || node.operator === '-=' ||
161
+ node.operator === '*=' || node.operator === '/=') {
162
+ if (this.isStateVariable(node.left, currentContract.name)) {
163
+ currentFunction.stateWrites.push({
164
+ variable: this.getVariableName(node.left),
165
+ loc: node.loc
166
+ });
167
+ }
168
+ }
169
+ }
170
+ });
171
+
172
+ return {
173
+ contracts: this.contracts,
174
+ functions: this.functions,
175
+ modifiers: this.modifiers,
176
+ stateVariables: this.stateVariables
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Analyze modifier require statements to determine what kind of access control it provides
182
+ */
183
+ analyzeModifierAccessControl(modInfo) {
184
+ const allRequires = modInfo.requireStatements.join(' ').toLowerCase();
185
+
186
+ // Check for ownership patterns
187
+ if (allRequires.includes('msg.sender') &&
188
+ (allRequires.includes('owner') || allRequires.includes('admin'))) {
189
+ modInfo.checksOwnership = true;
190
+ modInfo.checksAccess = true;
191
+ }
192
+
193
+ // Check for role-based patterns
194
+ if (allRequires.includes('hasrole') || allRequires.includes('role') ||
195
+ allRequires.includes('isauthorized') || allRequires.includes('onlyrole')) {
196
+ modInfo.checksRole = true;
197
+ modInfo.checksAccess = true;
198
+ }
199
+
200
+ // Check for general access control patterns
201
+ if (allRequires.includes('msg.sender') || allRequires.includes('tx.origin')) {
202
+ modInfo.checksAccess = true;
203
+ }
204
+
205
+ // Check for reentrancy guard patterns
206
+ if (allRequires.includes('_status') || allRequires.includes('locked') ||
207
+ allRequires.includes('_notentered') || allRequires.includes('reentrancy')) {
208
+ modInfo.checksReentrancy = true;
209
+ }
210
+
211
+ // Check modifier name for hints
212
+ const modNameLower = modInfo.name.toLowerCase();
213
+ if (modNameLower.includes('onlyowner') || modNameLower.includes('onlyadmin')) {
214
+ modInfo.checksOwnership = true;
215
+ modInfo.checksAccess = true;
216
+ }
217
+ if (modNameLower.includes('nonreentrant') || modNameLower.includes('lock')) {
218
+ modInfo.checksReentrancy = true;
219
+ }
220
+ }
221
+
222
+ reset() {
223
+ this.contracts = new Map();
224
+ this.functions = new Map();
225
+ this.modifiers = new Map();
226
+ this.stateVariables = new Map();
227
+ }
228
+
229
+ isStateVariable(node, currentContract) {
230
+ const varName = this.getVariableName(node);
231
+ return this.stateVariables.has(`${currentContract}.${varName}`);
232
+ }
233
+
234
+ getVariableName(node) {
235
+ if (node.type === 'Identifier') return node.name;
236
+ if (node.type === 'MemberAccess') return node.memberName;
237
+ if (node.type === 'IndexAccess') return this.getVariableName(node.base);
238
+ return null;
239
+ }
240
+
241
+ getSourceFromNode(node) {
242
+ if (!node || !node.range) return '';
243
+ return this.sourceCode.substring(node.range[0], node.range[1] + 1);
244
+ }
245
+
246
+
247
+ getTypeName(typeNode) {
248
+ if (!typeNode) return 'unknown';
249
+ if (typeNode.type === 'ElementaryTypeName') return typeNode.name;
250
+ if (typeNode.type === 'UserDefinedTypeName') return typeNode.namePath;
251
+ if (typeNode.type === 'Mapping') return 'mapping';
252
+ return 'complex';
253
+ }
254
+ }
255
+
256
+ module.exports = ControlFlowAnalyzer;