clawcontract-cli 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.
- package/README.md +229 -0
- package/dist/analyzer/fallback.d.ts +28 -0
- package/dist/analyzer/fallback.d.ts.map +1 -0
- package/dist/analyzer/fallback.js +142 -0
- package/dist/analyzer/fallback.js.map +1 -0
- package/dist/analyzer/index.d.ts +24 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +73 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/analyzer/slither.d.ts +36 -0
- package/dist/analyzer/slither.d.ts.map +1 -0
- package/dist/analyzer/slither.js +92 -0
- package/dist/analyzer/slither.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +7 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +74 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/create.d.ts +8 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +44 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/delete.d.ts +5 -0
- package/dist/cli/commands/delete.d.ts.map +1 -0
- package/dist/cli/commands/delete.js +48 -0
- package/dist/cli/commands/delete.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +7 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +130 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/featured.d.ts +4 -0
- package/dist/cli/commands/featured.d.ts.map +1 -0
- package/dist/cli/commands/featured.js +47 -0
- package/dist/cli/commands/featured.js.map +1 -0
- package/dist/cli/commands/full.d.ts +14 -0
- package/dist/cli/commands/full.d.ts.map +1 -0
- package/dist/cli/commands/full.js +72 -0
- package/dist/cli/commands/full.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +10 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +71 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/info.d.ts +4 -0
- package/dist/cli/commands/info.d.ts.map +1 -0
- package/dist/cli/commands/info.js +76 -0
- package/dist/cli/commands/info.js.map +1 -0
- package/dist/cli/commands/interact.d.ts +7 -0
- package/dist/cli/commands/interact.d.ts.map +1 -0
- package/dist/cli/commands/interact.js +173 -0
- package/dist/cli/commands/interact.js.map +1 -0
- package/dist/cli/commands/list.d.ts +6 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +37 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/register.d.ts +4 -0
- package/dist/cli/commands/register.d.ts.map +1 -0
- package/dist/cli/commands/register.js +54 -0
- package/dist/cli/commands/register.js.map +1 -0
- package/dist/cli/commands/verified.d.ts +9 -0
- package/dist/cli/commands/verified.d.ts.map +1 -0
- package/dist/cli/commands/verified.js +61 -0
- package/dist/cli/commands/verified.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +9 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +87 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +160 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils.d.ts +8 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +36 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/config/chains.d.ts +18 -0
- package/dist/config/chains.d.ts.map +1 -0
- package/dist/config/chains.js +52 -0
- package/dist/config/chains.js.map +1 -0
- package/dist/config/clawcontractbook.d.ts +3 -0
- package/dist/config/clawcontractbook.d.ts.map +1 -0
- package/dist/config/clawcontractbook.js +3 -0
- package/dist/config/clawcontractbook.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +1 -0
- package/dist/constants/index.d.ts +3 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +3 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/deployer/compiler.d.ts +10 -0
- package/dist/deployer/compiler.d.ts.map +1 -0
- package/dist/deployer/compiler.js +93 -0
- package/dist/deployer/compiler.js.map +1 -0
- package/dist/deployer/deploy.d.ts +28 -0
- package/dist/deployer/deploy.d.ts.map +1 -0
- package/dist/deployer/deploy.js +60 -0
- package/dist/deployer/deploy.js.map +1 -0
- package/dist/deployer/hardhat-config-template.d.ts +2 -0
- package/dist/deployer/hardhat-config-template.d.ts.map +1 -0
- package/dist/deployer/hardhat-config-template.js +21 -0
- package/dist/deployer/hardhat-config-template.js.map +1 -0
- package/dist/deployer/index.d.ts +7 -0
- package/dist/deployer/index.d.ts.map +1 -0
- package/dist/deployer/index.js +4 -0
- package/dist/deployer/index.js.map +1 -0
- package/dist/deployer/metadata.d.ts +17 -0
- package/dist/deployer/metadata.d.ts.map +1 -0
- package/dist/deployer/metadata.js +317 -0
- package/dist/deployer/metadata.js.map +1 -0
- package/dist/deployer/wallet.d.ts +6 -0
- package/dist/deployer/wallet.d.ts.map +1 -0
- package/dist/deployer/wallet.js +15 -0
- package/dist/deployer/wallet.js.map +1 -0
- package/dist/generator/index.d.ts +2 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +2 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/llm.d.ts +15 -0
- package/dist/generator/llm.d.ts.map +1 -0
- package/dist/generator/llm.js +128 -0
- package/dist/generator/llm.js.map +1 -0
- package/dist/generator/utils.d.ts +5 -0
- package/dist/generator/utils.d.ts.map +1 -0
- package/dist/generator/utils.js +49 -0
- package/dist/generator/utils.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/clawcontractbook.d.ts +65 -0
- package/dist/lib/clawcontractbook.d.ts.map +1 -0
- package/dist/lib/clawcontractbook.js +158 -0
- package/dist/lib/clawcontractbook.js.map +1 -0
- package/dist/lib/credentials.d.ts +28 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +73 -0
- package/dist/lib/credentials.js.map +1 -0
- package/dist/verifier/explorer-api.d.ts +21 -0
- package/dist/verifier/explorer-api.d.ts.map +1 -0
- package/dist/verifier/explorer-api.js +92 -0
- package/dist/verifier/explorer-api.js.map +1 -0
- package/dist/verifier/index.d.ts +20 -0
- package/dist/verifier/index.d.ts.map +1 -0
- package/dist/verifier/index.js +31 -0
- package/dist/verifier/index.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# ClawContract 🦞
|
|
2
|
+
|
|
3
|
+
**AI-powered smart contract generator, analyzer, and deployer for BNB Chain**
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](https://www.bnbchain.org)
|
|
8
|
+
[](#hackathon)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
ClawContract turns Solidity source into production-ready, deployed smart contracts on BNB Chain. Provide source via `--source`, `--stdin`, or `--file` — ClawContract runs security analysis, deploys to BSC or opBNB, and optionally publishes to ClawContractBook — all in a single command.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Security analysis** — runs Slither static analysis with an automatic regex-based fallback for environments without Python
|
|
19
|
+
- **One-command deployment** — deploy to BSC and opBNB (mainnet + testnet) with gas estimation
|
|
20
|
+
- **Non-interactive CLI** — fully automated pipeline with gas estimates, no user prompts required
|
|
21
|
+
- **OpenClaw skill integration** — register as an OpenClaw skill for chat-based contract generation and deployment
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/your-username/ClawContract.git
|
|
27
|
+
cd ClawContract
|
|
28
|
+
pnpm install
|
|
29
|
+
pnpm run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Register (required for deploy)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
clawcontract-cli register --name "My AI Agent"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Creates credentials in `clawcontractbook/credentials.json` with a wallet. Fund that address with BNB for gas.
|
|
41
|
+
|
|
42
|
+
### Create a contract from source
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
clawcontract-cli create --source "pragma solidity ^0.8.0; contract Foo { uint x; }"
|
|
46
|
+
cat MyContract.sol | clawcontract-cli create --stdin
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Writes the contract to `./contracts/`. Override with `--output <dir>`.
|
|
50
|
+
|
|
51
|
+
### Analyze a contract for vulnerabilities
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
clawcontract-cli analyze ./contracts/Counter.sol
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Deploy to a chain
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
clawcontract-cli deploy ./contracts/Counter.sol --chain bsc-testnet
|
|
61
|
+
clawcontract-cli deploy ./contracts/Counter.sol --chain bsc-testnet --publish
|
|
62
|
+
clawcontract-cli deploy ./contracts/Counter.sol --chain bsc-testnet --publish --description "simple counter"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Options: `--publish` to publish to ClawContractBook; `--description <text>` for the publish description.
|
|
66
|
+
|
|
67
|
+
### Interact with a deployed contract
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
clawcontract-cli interact 0xYourContractAddress getCount --chain bsc-testnet
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Call any function on a deployed contract. Read-only functions (`view`/`pure`) are called without gas. State-changing functions are sent as signed transactions.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Read-only call
|
|
77
|
+
clawcontract-cli interact 0xABC... getCount --chain bsc-testnet
|
|
78
|
+
|
|
79
|
+
# State-changing call
|
|
80
|
+
clawcontract-cli interact 0xABC... increment --chain bsc-testnet
|
|
81
|
+
|
|
82
|
+
# Payable call (send BNB value in wei)
|
|
83
|
+
clawcontract-cli interact 0xABC... deposit --value 100000000000000 --chain bsc-testnet
|
|
84
|
+
|
|
85
|
+
# Use ABI from source file instead of stored metadata
|
|
86
|
+
clawcontract-cli interact 0xABC... getCount --chain bsc-testnet --file ./contracts/Counter.sol
|
|
87
|
+
|
|
88
|
+
# Use ABI from URL (e.g. from verified/featured output)
|
|
89
|
+
clawcontract-cli interact 0xABC... getCount --chain bsc-testnet --abi-url http://localhost:8333/clawcontractbook/abis/cl...json
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### List deployments
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
clawcontract-cli list
|
|
96
|
+
clawcontract-cli list --chain bsc-testnet
|
|
97
|
+
clawcontract-cli list --json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Shows address, contract name, chain, deployer, and deployment date. Use `--chain` to filter; `--json` for machine-readable output.
|
|
101
|
+
|
|
102
|
+
### Delete a deployment record
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
clawcontract-cli delete 0xYourContractAddress
|
|
106
|
+
clawcontract-cli delete 0xYourContractAddress --force # skip confirmation
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Info (agent address and balance)
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
clawcontract-cli info
|
|
113
|
+
clawcontract-cli info --chain bsc-mainnet
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Browse verified and featured contracts
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
clawcontract-cli verified
|
|
120
|
+
clawcontract-cli verified --page 2 --limit 10 --chain bsc-testnet --search counter --sort newest
|
|
121
|
+
clawcontract-cli verified --json
|
|
122
|
+
|
|
123
|
+
clawcontract-cli featured
|
|
124
|
+
clawcontract-cli featured --json
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Full pipeline (create → analyze → deploy)
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
clawcontract-cli full --source "pragma solidity ^0.8.0; contract Bar {}" --chain bsc-testnet
|
|
131
|
+
clawcontract-cli full --stdin --chain bsc-testnet
|
|
132
|
+
clawcontract-cli full --file ./contracts/Counter.sol --chain bsc-testnet
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Options:
|
|
136
|
+
- `--skip-analyze` — skip security analysis step entirely
|
|
137
|
+
- `--skip-deploy` — stop after analysis, do not deploy
|
|
138
|
+
- `--publish` — publish deployment to ClawContractBook
|
|
139
|
+
- `--description <text>` — deployment description for publishing
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Analyze only — review before deploying
|
|
143
|
+
clawcontract-cli full --file ./contracts/Counter.sol --chain bsc-testnet --skip-deploy
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Global options
|
|
147
|
+
|
|
148
|
+
| Option | Description | Default |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `--chain <chain>` | Target blockchain | `bsc-testnet` |
|
|
151
|
+
| `--output <dir>` | Output directory for generated contracts | `./contracts` |
|
|
152
|
+
|
|
153
|
+
## Supported Chains
|
|
154
|
+
|
|
155
|
+
| Chain | Chain ID | RPC | Explorer |
|
|
156
|
+
|---|---|---|---|
|
|
157
|
+
| BNB Smart Chain | 56 | `https://bsc-dataseed.binance.org` | [bscscan.com](https://bscscan.com) |
|
|
158
|
+
| BNB Smart Chain Testnet | 97 | `https://data-seed-prebsc-1-s1.binance.org:8545` | [testnet.bscscan.com](https://testnet.bscscan.com) |
|
|
159
|
+
| opBNB | 204 | `https://opbnb-mainnet-rpc.bnbchain.org` | [opbnbscan.com](https://opbnbscan.com) |
|
|
160
|
+
| opBNB Testnet | 5611 | `https://opbnb-testnet-rpc.bnbchain.org` | [testnet.opbnbscan.com](https://testnet.opbnbscan.com) |
|
|
161
|
+
|
|
162
|
+
## Architecture
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
src/
|
|
166
|
+
├── cli/ # Commander.js CLI entry point + command handlers
|
|
167
|
+
├── generator/ # Contract generation (template matching)
|
|
168
|
+
├── analyzer/ # Security analysis (Slither + regex fallback)
|
|
169
|
+
├── deployer/ # Compilation + deployment via Hardhat + ethers.js (saves metadata)
|
|
170
|
+
├── config/ # Chain configurations and constants
|
|
171
|
+
└── lib/ # ClawContractBook publishing, credentials
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Pipeline Flow
|
|
175
|
+
|
|
176
|
+
The `full` command runs create → analyze → deploy end-to-end. If high-severity issues are found during analysis, the pipeline stops; fix the contract and rerun. Use `--skip-deploy` to stop after analysis.
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Source (--source / --stdin / --file)
|
|
180
|
+
↓
|
|
181
|
+
Create (write .sol) ── or skip if --file
|
|
182
|
+
↓
|
|
183
|
+
Security Analysis ─── Slither / regex checks (--skip-analyze to skip)
|
|
184
|
+
↓
|
|
185
|
+
Compilation ─────── Hardhat + solc
|
|
186
|
+
↓
|
|
187
|
+
Deployment ──────── ethers.js → BSC / opBNB (--skip-deploy to stop before)
|
|
188
|
+
↓
|
|
189
|
+
Publish (optional) ─ ClawContractBook when --publish
|
|
190
|
+
↓
|
|
191
|
+
Interaction ──────── ethers.js read/write calls
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## OpenClaw Integration
|
|
195
|
+
|
|
196
|
+
ClawContract ships with an [OpenClaw skill](src/openclaw/SKILL.md) that teaches OpenClaw agents how to use the CLI for chat-based contract generation and deployment. Copy or symlink `src/openclaw/` into your OpenClaw workspace `skills/` directory to enable it.
|
|
197
|
+
|
|
198
|
+
## Configuration
|
|
199
|
+
|
|
200
|
+
Deploy and interact use credentials from `clawcontract register` (stored in `clawcontractbook/credentials.json`). No `.env` or `CLAWCONTRACT_PRIVATE_KEY` needed.
|
|
201
|
+
|
|
202
|
+
Environment variables (optional):
|
|
203
|
+
|
|
204
|
+
| Variable | Description | Required |
|
|
205
|
+
|---|---|---|
|
|
206
|
+
| `CLAWCONTRACT_OPENROUTER_API_KEY` | OpenRouter API key (not needed for `--source`/`--stdin`/`--file`) | No |
|
|
207
|
+
| `CLAWCONTRACT_BSCSCAN_API_KEY` | BscScan / opBNBScan API key | No |
|
|
208
|
+
|
|
209
|
+
> **Security:** Never commit secrets to version control. When using Docker, set values in `docker-compose.yml` or pass them via environment variables.
|
|
210
|
+
|
|
211
|
+
> **Data:** Deployment metadata is saved to `contracts/.deployments/` using a directory-based store with deduplicated ABIs and an index for fast lookups. Legacy `.deployments.json` files are auto-migrated on first access. This directory is local and should not be committed.
|
|
212
|
+
|
|
213
|
+
## Requirements
|
|
214
|
+
|
|
215
|
+
- **Node.js** 20.0.0 or later
|
|
216
|
+
- **pnpm** (recommended package manager)
|
|
217
|
+
- **Python 3.8+** (optional — required for Slither static analysis; regex fallback is used if unavailable)
|
|
218
|
+
|
|
219
|
+
## Hackathon
|
|
220
|
+
|
|
221
|
+
Built for the **Good Vibes Only: OpenClaw Edition** hackathon.
|
|
222
|
+
|
|
223
|
+
- **Track:** Builders' Tools
|
|
224
|
+
- **Chain:** BNB Chain (BSC + opBNB)
|
|
225
|
+
- **Goal:** Make smart contract development accessible to everyone through AI and natural language
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface Finding {
|
|
2
|
+
severity: 'High' | 'Medium' | 'Low' | 'Informational' | 'Optimization';
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
location: string;
|
|
6
|
+
recommendation: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AnalysisSummary {
|
|
9
|
+
high: number;
|
|
10
|
+
medium: number;
|
|
11
|
+
low: number;
|
|
12
|
+
informational: number;
|
|
13
|
+
optimization: number;
|
|
14
|
+
total: number;
|
|
15
|
+
}
|
|
16
|
+
export interface AnalysisResult {
|
|
17
|
+
findings: Finding[];
|
|
18
|
+
summary: AnalysisSummary;
|
|
19
|
+
passed: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function checkReentrancy(source: string): Finding[];
|
|
22
|
+
export declare function checkUncheckedReturn(source: string): Finding[];
|
|
23
|
+
export declare function checkTxOrigin(source: string): Finding[];
|
|
24
|
+
export declare function checkSelfDestruct(source: string): Finding[];
|
|
25
|
+
export declare function checkFloatingPragma(source: string): Finding[];
|
|
26
|
+
export declare function runFallbackAnalysis(source: string): AnalysisResult;
|
|
27
|
+
export declare function runFallbackAnalysisFromFile(filePath: string): AnalysisResult;
|
|
28
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/analyzer/fallback.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CA2BzD;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAoB9D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAkBvD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAkB3D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAkB7D;AAyBD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAalE;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAG5E"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
export function checkReentrancy(source) {
|
|
3
|
+
const findings = [];
|
|
4
|
+
const lines = source.split('\n');
|
|
5
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6
|
+
const line = lines[i].trim();
|
|
7
|
+
if ((line.includes('.call{') || line.includes('.call(') || line.includes('.send(') || line.includes('.transfer(')) &&
|
|
8
|
+
!line.startsWith('//')) {
|
|
9
|
+
for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
|
|
10
|
+
const subsequent = lines[j].trim();
|
|
11
|
+
if (subsequent.includes('=') && !subsequent.startsWith('//') && !subsequent.startsWith('require') && !subsequent.startsWith('if')) {
|
|
12
|
+
findings.push({
|
|
13
|
+
severity: 'High',
|
|
14
|
+
title: '[Basic Check] Potential Reentrancy',
|
|
15
|
+
description: `External call at line ${i + 1} is followed by a state change at line ${j + 1}. This could be vulnerable to reentrancy attacks.`,
|
|
16
|
+
location: `Line ${i + 1}`,
|
|
17
|
+
recommendation: 'Use the checks-effects-interactions pattern or a reentrancy guard. Update state variables before making external calls.',
|
|
18
|
+
});
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return findings;
|
|
25
|
+
}
|
|
26
|
+
export function checkUncheckedReturn(source) {
|
|
27
|
+
const findings = [];
|
|
28
|
+
const lines = source.split('\n');
|
|
29
|
+
for (let i = 0; i < lines.length; i++) {
|
|
30
|
+
const line = lines[i].trim();
|
|
31
|
+
if (line.includes('.call{') || line.includes('.call(')) {
|
|
32
|
+
if (!line.includes('(bool') && !line.includes('require') && !line.includes('if')) {
|
|
33
|
+
findings.push({
|
|
34
|
+
severity: 'Medium',
|
|
35
|
+
title: '[Basic Check] Unchecked Return Value',
|
|
36
|
+
description: `Low-level call at line ${i + 1} does not check the return value. The call could silently fail.`,
|
|
37
|
+
location: `Line ${i + 1}`,
|
|
38
|
+
recommendation: 'Check the return value of low-level calls: (bool success, ) = addr.call{...}(...); require(success);',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return findings;
|
|
44
|
+
}
|
|
45
|
+
export function checkTxOrigin(source) {
|
|
46
|
+
const findings = [];
|
|
47
|
+
const lines = source.split('\n');
|
|
48
|
+
for (let i = 0; i < lines.length; i++) {
|
|
49
|
+
const line = lines[i].trim();
|
|
50
|
+
if (line.includes('tx.origin') && !line.startsWith('//')) {
|
|
51
|
+
findings.push({
|
|
52
|
+
severity: 'Medium',
|
|
53
|
+
title: '[Basic Check] tx.origin Usage',
|
|
54
|
+
description: `tx.origin used at line ${i + 1}. Using tx.origin for authorization is vulnerable to phishing attacks.`,
|
|
55
|
+
location: `Line ${i + 1}`,
|
|
56
|
+
recommendation: 'Use msg.sender instead of tx.origin for authorization checks.',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return findings;
|
|
61
|
+
}
|
|
62
|
+
export function checkSelfDestruct(source) {
|
|
63
|
+
const findings = [];
|
|
64
|
+
const lines = source.split('\n');
|
|
65
|
+
for (let i = 0; i < lines.length; i++) {
|
|
66
|
+
const line = lines[i].trim();
|
|
67
|
+
if ((line.includes('selfdestruct(') || line.includes('selfdestruct (')) && !line.startsWith('//')) {
|
|
68
|
+
findings.push({
|
|
69
|
+
severity: 'High',
|
|
70
|
+
title: '[Basic Check] selfdestruct Usage',
|
|
71
|
+
description: `selfdestruct found at line ${i + 1}. This can permanently destroy the contract and send remaining Ether to an address.`,
|
|
72
|
+
location: `Line ${i + 1}`,
|
|
73
|
+
recommendation: 'Remove selfdestruct or protect it with strict access control. Note: selfdestruct is deprecated in newer Solidity versions.',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return findings;
|
|
78
|
+
}
|
|
79
|
+
export function checkFloatingPragma(source) {
|
|
80
|
+
const findings = [];
|
|
81
|
+
const lines = source.split('\n');
|
|
82
|
+
for (let i = 0; i < lines.length; i++) {
|
|
83
|
+
const line = lines[i].trim();
|
|
84
|
+
if (line.startsWith('pragma solidity') && line.includes('^')) {
|
|
85
|
+
findings.push({
|
|
86
|
+
severity: 'Informational',
|
|
87
|
+
title: '[Basic Check] Floating Pragma',
|
|
88
|
+
description: `Floating pragma found at line ${i + 1}: "${line}". Contracts should be deployed with the same compiler version they were tested with.`,
|
|
89
|
+
location: `Line ${i + 1}`,
|
|
90
|
+
recommendation: 'Lock the pragma to a specific version, e.g., "pragma solidity 0.8.20;" instead of "pragma solidity ^0.8.20;".',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return findings;
|
|
95
|
+
}
|
|
96
|
+
function buildSummary(findings) {
|
|
97
|
+
const summary = {
|
|
98
|
+
high: 0,
|
|
99
|
+
medium: 0,
|
|
100
|
+
low: 0,
|
|
101
|
+
informational: 0,
|
|
102
|
+
optimization: 0,
|
|
103
|
+
total: findings.length,
|
|
104
|
+
};
|
|
105
|
+
for (const finding of findings) {
|
|
106
|
+
switch (finding.severity) {
|
|
107
|
+
case 'High':
|
|
108
|
+
summary.high++;
|
|
109
|
+
break;
|
|
110
|
+
case 'Medium':
|
|
111
|
+
summary.medium++;
|
|
112
|
+
break;
|
|
113
|
+
case 'Low':
|
|
114
|
+
summary.low++;
|
|
115
|
+
break;
|
|
116
|
+
case 'Informational':
|
|
117
|
+
summary.informational++;
|
|
118
|
+
break;
|
|
119
|
+
case 'Optimization':
|
|
120
|
+
summary.optimization++;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return summary;
|
|
125
|
+
}
|
|
126
|
+
export function runFallbackAnalysis(source) {
|
|
127
|
+
const findings = [
|
|
128
|
+
...checkReentrancy(source),
|
|
129
|
+
...checkUncheckedReturn(source),
|
|
130
|
+
...checkTxOrigin(source),
|
|
131
|
+
...checkSelfDestruct(source),
|
|
132
|
+
...checkFloatingPragma(source),
|
|
133
|
+
];
|
|
134
|
+
const summary = buildSummary(findings);
|
|
135
|
+
const passed = summary.high === 0;
|
|
136
|
+
return { findings, summary, passed };
|
|
137
|
+
}
|
|
138
|
+
export function runFallbackAnalysisFromFile(filePath) {
|
|
139
|
+
const source = readFileSync(filePath, 'utf-8');
|
|
140
|
+
return runFallbackAnalysis(source);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/analyzer/fallback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface Finding {
|
|
2
|
+
severity: 'High' | 'Medium' | 'Low' | 'Informational' | 'Optimization';
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
location: string;
|
|
6
|
+
recommendation: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AnalysisSummary {
|
|
9
|
+
high: number;
|
|
10
|
+
medium: number;
|
|
11
|
+
low: number;
|
|
12
|
+
informational: number;
|
|
13
|
+
optimization: number;
|
|
14
|
+
total: number;
|
|
15
|
+
}
|
|
16
|
+
export interface AnalysisResult {
|
|
17
|
+
findings: Finding[];
|
|
18
|
+
summary: AnalysisSummary;
|
|
19
|
+
passed: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function analyzeContract(filePath: string): Promise<AnalysisResult>;
|
|
22
|
+
export declare function isSlitherAvailable(): boolean;
|
|
23
|
+
export declare function getSlitherInstallInstructions(): string;
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAyBD,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA4B/E;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAWtD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { isSlitherInstalled, runSlither, mapSlitherResults } from './slither.js';
|
|
3
|
+
import { runFallbackAnalysis } from './fallback.js';
|
|
4
|
+
function buildSummary(findings) {
|
|
5
|
+
const summary = {
|
|
6
|
+
high: 0,
|
|
7
|
+
medium: 0,
|
|
8
|
+
low: 0,
|
|
9
|
+
informational: 0,
|
|
10
|
+
optimization: 0,
|
|
11
|
+
total: findings.length,
|
|
12
|
+
};
|
|
13
|
+
for (const finding of findings) {
|
|
14
|
+
switch (finding.severity) {
|
|
15
|
+
case 'High':
|
|
16
|
+
summary.high++;
|
|
17
|
+
break;
|
|
18
|
+
case 'Medium':
|
|
19
|
+
summary.medium++;
|
|
20
|
+
break;
|
|
21
|
+
case 'Low':
|
|
22
|
+
summary.low++;
|
|
23
|
+
break;
|
|
24
|
+
case 'Informational':
|
|
25
|
+
summary.informational++;
|
|
26
|
+
break;
|
|
27
|
+
case 'Optimization':
|
|
28
|
+
summary.optimization++;
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return summary;
|
|
33
|
+
}
|
|
34
|
+
export async function analyzeContract(filePath) {
|
|
35
|
+
if (!existsSync(filePath)) {
|
|
36
|
+
throw new Error(`Contract file not found: ${filePath}`);
|
|
37
|
+
}
|
|
38
|
+
if (isSlitherAvailable()) {
|
|
39
|
+
const output = runSlither(filePath);
|
|
40
|
+
const findings = mapSlitherResults(output);
|
|
41
|
+
const summary = buildSummary(findings);
|
|
42
|
+
const passed = summary.high === 0;
|
|
43
|
+
return { findings, summary, passed };
|
|
44
|
+
}
|
|
45
|
+
const source = readFileSync(filePath, 'utf-8');
|
|
46
|
+
const result = runFallbackAnalysis(source);
|
|
47
|
+
result.findings.unshift({
|
|
48
|
+
severity: 'Informational',
|
|
49
|
+
title: '[Basic Check] Slither Not Installed',
|
|
50
|
+
description: 'Slither is not installed. Only basic regex-based checks were performed. Install Slither for comprehensive analysis.',
|
|
51
|
+
location: 'N/A',
|
|
52
|
+
recommendation: getSlitherInstallInstructions(),
|
|
53
|
+
});
|
|
54
|
+
result.summary.informational++;
|
|
55
|
+
result.summary.total++;
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
export function isSlitherAvailable() {
|
|
59
|
+
return isSlitherInstalled();
|
|
60
|
+
}
|
|
61
|
+
export function getSlitherInstallInstructions() {
|
|
62
|
+
return [
|
|
63
|
+
'Install Slither for comprehensive smart contract analysis:',
|
|
64
|
+
' pip3 install slither-analyzer',
|
|
65
|
+
'',
|
|
66
|
+
'Or with pipx:',
|
|
67
|
+
' pipx install slither-analyzer',
|
|
68
|
+
'',
|
|
69
|
+
'Requires Python 3.8+ and solc (Solidity compiler).',
|
|
70
|
+
'More info: https://github.com/crytic/slither',
|
|
71
|
+
].join('\n');
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AA0BpD,SAAS,YAAY,CAAC,QAAmB;IACvC,MAAM,OAAO,GAAoB;QAC/B,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;QACN,aAAa,EAAE,CAAC;QAChB,YAAY,EAAE,CAAC;QACf,KAAK,EAAE,QAAQ,CAAC,MAAM;KACvB,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;YACzB,KAAK,MAAM;gBAAE,OAAO,CAAC,IAAI,EAAE,CAAC;gBAAC,MAAM;YACnC,KAAK,QAAQ;gBAAE,OAAO,CAAC,MAAM,EAAE,CAAC;gBAAC,MAAM;YACvC,KAAK,KAAK;gBAAE,OAAO,CAAC,GAAG,EAAE,CAAC;gBAAC,MAAM;YACjC,KAAK,eAAe;gBAAE,OAAO,CAAC,aAAa,EAAE,CAAC;gBAAC,MAAM;YACrD,KAAK,cAAc;gBAAE,OAAO,CAAC,YAAY,EAAE,CAAC;gBAAC,MAAM;QACrD,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,kBAAkB,EAAE,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAc,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC;QAClC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE3C,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QACtB,QAAQ,EAAE,eAAe;QACzB,KAAK,EAAE,qCAAqC;QAC5C,WAAW,EAAE,qHAAqH;QAClI,QAAQ,EAAE,KAAK;QACf,cAAc,EAAE,6BAA6B,EAAE;KAChD,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;IAC/B,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAEvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,kBAAkB,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,6BAA6B;IAC3C,OAAO;QACL,4DAA4D;QAC5D,iCAAiC;QACjC,EAAE;QACF,eAAe;QACf,iCAAiC;QACjC,EAAE;QACF,oDAAoD;QACpD,8CAA8C;KAC/C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface SlitherDetector {
|
|
2
|
+
check: string;
|
|
3
|
+
impact: string;
|
|
4
|
+
confidence: string;
|
|
5
|
+
description: string;
|
|
6
|
+
elements: SlitherElement[];
|
|
7
|
+
first_markdown_element: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SlitherElement {
|
|
10
|
+
type: string;
|
|
11
|
+
name: string;
|
|
12
|
+
source_mapping: {
|
|
13
|
+
filename_relative: string;
|
|
14
|
+
lines: number[];
|
|
15
|
+
starting_column: number;
|
|
16
|
+
ending_column: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface SlitherOutput {
|
|
20
|
+
success: boolean;
|
|
21
|
+
error: string | null;
|
|
22
|
+
results: {
|
|
23
|
+
detectors: SlitherDetector[];
|
|
24
|
+
} | null;
|
|
25
|
+
}
|
|
26
|
+
export interface Finding {
|
|
27
|
+
severity: 'High' | 'Medium' | 'Low' | 'Informational' | 'Optimization';
|
|
28
|
+
title: string;
|
|
29
|
+
description: string;
|
|
30
|
+
location: string;
|
|
31
|
+
recommendation: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function isSlitherInstalled(): boolean;
|
|
34
|
+
export declare function runSlither(filePath: string): SlitherOutput;
|
|
35
|
+
export declare function mapSlitherResults(output: SlitherOutput): Finding[];
|
|
36
|
+
//# sourceMappingURL=slither.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slither.d.ts","sourceRoot":"","sources":["../../src/analyzer/slither.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE;QACd,iBAAiB,EAAE,MAAM,CAAC;QAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE;QACP,SAAS,EAAE,eAAe,EAAE,CAAC;KAC9B,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAcD,wBAAgB,kBAAkB,IAAI,OAAO,CAQ5C;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CA6B1D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,EAAE,CAkBlE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { platform } from 'node:os';
|
|
3
|
+
const IMPACT_MAP = {
|
|
4
|
+
High: 'High',
|
|
5
|
+
Medium: 'Medium',
|
|
6
|
+
Low: 'Low',
|
|
7
|
+
Informational: 'Informational',
|
|
8
|
+
Optimization: 'Optimization',
|
|
9
|
+
};
|
|
10
|
+
function mapImpact(impact) {
|
|
11
|
+
return IMPACT_MAP[impact] ?? 'Informational';
|
|
12
|
+
}
|
|
13
|
+
export function isSlitherInstalled() {
|
|
14
|
+
try {
|
|
15
|
+
const cmd = platform() === 'win32' ? 'where slither' : 'which slither';
|
|
16
|
+
execSync(cmd, { stdio: 'pipe' });
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function runSlither(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
const stdout = execSync(`slither ${filePath} --json -`, {
|
|
26
|
+
encoding: 'utf-8',
|
|
27
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
28
|
+
timeout: 120_000,
|
|
29
|
+
});
|
|
30
|
+
const parsed = JSON.parse(stdout);
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err && typeof err === 'object' && 'stdout' in err) {
|
|
35
|
+
const stdout = err.stdout;
|
|
36
|
+
if (stdout && typeof stdout === 'string') {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(stdout);
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// JSON parse failed, fall through
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: err instanceof Error ? err.message : String(err),
|
|
49
|
+
results: null,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function mapSlitherResults(output) {
|
|
54
|
+
if (!output.results?.detectors) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
return output.results.detectors.map((detector) => {
|
|
58
|
+
const location = detector.elements.length > 0
|
|
59
|
+
? formatLocation(detector.elements[0])
|
|
60
|
+
: 'Unknown';
|
|
61
|
+
return {
|
|
62
|
+
severity: mapImpact(detector.impact),
|
|
63
|
+
title: detector.check,
|
|
64
|
+
description: detector.description,
|
|
65
|
+
location,
|
|
66
|
+
recommendation: getRecommendation(detector.check),
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function formatLocation(element) {
|
|
71
|
+
const file = element.source_mapping.filename_relative;
|
|
72
|
+
const lines = element.source_mapping.lines;
|
|
73
|
+
if (lines.length > 0) {
|
|
74
|
+
return `${file}#L${lines[0]}`;
|
|
75
|
+
}
|
|
76
|
+
return file;
|
|
77
|
+
}
|
|
78
|
+
function getRecommendation(check) {
|
|
79
|
+
const recommendations = {
|
|
80
|
+
'reentrancy-eth': 'Use the checks-effects-interactions pattern or a reentrancy guard.',
|
|
81
|
+
'reentrancy-no-eth': 'Use the checks-effects-interactions pattern or a reentrancy guard.',
|
|
82
|
+
'unchecked-lowlevel': 'Check the return value of low-level calls.',
|
|
83
|
+
'unchecked-send': 'Check the return value of send().',
|
|
84
|
+
'tx-origin': 'Use msg.sender instead of tx.origin for authorization.',
|
|
85
|
+
'suicidal': 'Remove or protect selfdestruct with access control.',
|
|
86
|
+
'arbitrary-send': 'Restrict who can call functions that send Ether.',
|
|
87
|
+
'locked-ether': 'Add a withdrawal function for locked Ether.',
|
|
88
|
+
'solc-version': 'Use a fixed Solidity version pragma.',
|
|
89
|
+
};
|
|
90
|
+
return recommendations[check] ?? 'Review and address this finding.';
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=slither.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slither.js","sourceRoot":"","sources":["../../src/analyzer/slither.ts"],"names":[],"mappings":"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"}
|