audit-notes-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/LICENSE +21 -0
- package/README.md +41 -0
- package/dist/analyzer/accessControl.js +25 -0
- package/dist/analyzer/detectors.js +16 -0
- package/dist/analyzer/externalCalls.js +20 -0
- package/dist/analyzer/slither.js +17 -0
- package/dist/analyzer/stateGraph.js +17 -0
- package/dist/analyzer/upgradeability.js +21 -0
- package/dist/cli.js +63 -0
- package/dist/invariants/generator.js +23 -0
- package/dist/license.js +13 -0
- package/dist/llm/client.js +43 -0
- package/dist/llm/prompt.js +21 -0
- package/dist/report/markdown.js +66 -0
- package/dist/risk/reentrancy.js +27 -0
- package/dist/rules/rules.schema.json +30 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Crypto Finder
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# AI-Assisted Audit
|
|
2
|
+
|
|
3
|
+
Professional **security pre-review tooling** for smart contracts.
|
|
4
|
+
|
|
5
|
+
This is NOT:
|
|
6
|
+
- an AI auditor
|
|
7
|
+
- a vulnerability scanner
|
|
8
|
+
- severity scoring software
|
|
9
|
+
|
|
10
|
+
This IS:
|
|
11
|
+
- a thinking assistant for auditors
|
|
12
|
+
- deterministic security analysis
|
|
13
|
+
- structured audit context before full audits
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## What it generates
|
|
18
|
+
- Privileged role map
|
|
19
|
+
- External call & reentrancy surfaces
|
|
20
|
+
- State mutation & invariant hints
|
|
21
|
+
- Upgradeability risks
|
|
22
|
+
- Critical audit questions
|
|
23
|
+
|
|
24
|
+
Output: `AUDIT_NOTES.md`
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Why this exists
|
|
29
|
+
Most audit issues are missed due to **lost context**, not missing tools.
|
|
30
|
+
|
|
31
|
+
This tool extracts structure so humans reason better.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Usage (CLI)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
slither . --json slither.json
|
|
39
|
+
export AUDIT_NOTES_LICENSE=AN-XXXX
|
|
40
|
+
export OPENAI_API_KEY=sk-...
|
|
41
|
+
audit-notes .
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractRoles = extractRoles;
|
|
4
|
+
function extractRoles(slither) {
|
|
5
|
+
const roles = new Map();
|
|
6
|
+
for (const [, rawContract] of Object.entries(slither.contracts)) {
|
|
7
|
+
const contract = rawContract;
|
|
8
|
+
for (const func of contract.functions || []) {
|
|
9
|
+
for (const mod of func.modifiers || []) {
|
|
10
|
+
if (mod.includes("onlyOwner")) {
|
|
11
|
+
addFn(roles, "OWNER", func.name);
|
|
12
|
+
}
|
|
13
|
+
if (mod.includes("onlyRole")) {
|
|
14
|
+
addFn(roles, "ROLE_BASED", func.name);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return Object.fromEntries([...roles.entries()].map(([k, v]) => [k, [...v]]));
|
|
20
|
+
}
|
|
21
|
+
function addFn(map, role, fn) {
|
|
22
|
+
if (!map.has(role))
|
|
23
|
+
map.set(role, new Set());
|
|
24
|
+
map.get(role).add(fn);
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeDetectors = analyzeDetectors;
|
|
4
|
+
function analyzeDetectors(detectors) {
|
|
5
|
+
return detectors.map(d => ({
|
|
6
|
+
check: d.check,
|
|
7
|
+
impact: d.impact,
|
|
8
|
+
confidence: d.confidence,
|
|
9
|
+
description: d.description,
|
|
10
|
+
locations: (d.elements || []).map((e) => ({
|
|
11
|
+
contract: e.contract,
|
|
12
|
+
function: e.name,
|
|
13
|
+
lines: e.source_mapping?.lines
|
|
14
|
+
}))
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractExternalCalls = extractExternalCalls;
|
|
4
|
+
function extractExternalCalls(slither) {
|
|
5
|
+
const calls = [];
|
|
6
|
+
for (const [, rawContract] of Object.entries(slither.contracts)) {
|
|
7
|
+
const contract = rawContract;
|
|
8
|
+
for (const func of contract.functions || []) {
|
|
9
|
+
for (const call of func.external_calls || []) {
|
|
10
|
+
calls.push({
|
|
11
|
+
function: func.name,
|
|
12
|
+
target: call.contract_name,
|
|
13
|
+
type: call.type,
|
|
14
|
+
stateUpdatedAfter: (func.state_variables_written || []).length > 0
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return calls;
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadSlither = loadSlither;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
function loadSlither(filePath) {
|
|
9
|
+
const raw = fs_1.default.readFileSync(filePath, "utf8");
|
|
10
|
+
const json = JSON.parse(raw);
|
|
11
|
+
if (!json?.results?.detectors) {
|
|
12
|
+
throw new Error("Invalid Slither JSON: detectors not found");
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
detectors: json.results.detectors
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractStateWrites = extractStateWrites;
|
|
4
|
+
function extractStateWrites(slither) {
|
|
5
|
+
const state = new Map();
|
|
6
|
+
for (const [, rawContract] of Object.entries(slither.contracts)) {
|
|
7
|
+
const contract = rawContract;
|
|
8
|
+
for (const func of contract.functions || []) {
|
|
9
|
+
for (const v of func.state_variables_written || []) {
|
|
10
|
+
if (!state.has(v))
|
|
11
|
+
state.set(v, new Set());
|
|
12
|
+
state.get(v).add(func.name);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return Object.fromEntries([...state.entries()].map(([k, v]) => [k, [...v]]));
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectUpgradeability = detectUpgradeability;
|
|
4
|
+
function detectUpgradeability(slither) {
|
|
5
|
+
const patterns = {
|
|
6
|
+
uups: false,
|
|
7
|
+
transparent: false,
|
|
8
|
+
beacon: false
|
|
9
|
+
};
|
|
10
|
+
for (const [, rawContract] of Object.entries(slither.contracts)) {
|
|
11
|
+
const contract = rawContract;
|
|
12
|
+
const inherit = contract.inheritance || [];
|
|
13
|
+
if (inherit.includes("UUPSUpgradeable"))
|
|
14
|
+
patterns.uups = true;
|
|
15
|
+
if (inherit.includes("TransparentUpgradeableProxy"))
|
|
16
|
+
patterns.transparent = true;
|
|
17
|
+
if (inherit.includes("BeaconProxy"))
|
|
18
|
+
patterns.beacon = true;
|
|
19
|
+
}
|
|
20
|
+
return patterns;
|
|
21
|
+
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const license_1 = require("./license");
|
|
10
|
+
const slither_1 = require("./analyzer/slither");
|
|
11
|
+
const detectors_1 = require("./analyzer/detectors");
|
|
12
|
+
const markdown_1 = require("./report/markdown");
|
|
13
|
+
const prompt_1 = require("./llm/prompt");
|
|
14
|
+
const client_1 = require("./llm/client");
|
|
15
|
+
const rules_schema_json_1 = __importDefault(require("./rules/rules.schema.json"));
|
|
16
|
+
const cmd = process.argv[2] ?? "run";
|
|
17
|
+
async function run() {
|
|
18
|
+
(0, license_1.validateLicense)();
|
|
19
|
+
const slitherPath = path_1.default.resolve("slither.json");
|
|
20
|
+
if (!fs_1.default.existsSync(slitherPath)) {
|
|
21
|
+
throw new Error("slither.json not found. Run `slither <path> --json slither.json` first.");
|
|
22
|
+
}
|
|
23
|
+
const slither = (0, slither_1.loadSlither)(slitherPath);
|
|
24
|
+
const findings = (0, detectors_1.analyzeDetectors)(slither.detectors);
|
|
25
|
+
let report;
|
|
26
|
+
if (process.env.OPENAI_API_KEY) {
|
|
27
|
+
const prompt = (0, prompt_1.buildPrompt)({ findings }, rules_schema_json_1.default);
|
|
28
|
+
report = await (0, client_1.generateAuditNotes)(prompt);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
report = (0, markdown_1.generateMarkdown)({ findings });
|
|
32
|
+
}
|
|
33
|
+
fs_1.default.mkdirSync("output", { recursive: true });
|
|
34
|
+
fs_1.default.writeFileSync("output/AUDIT_NOTES.md", report);
|
|
35
|
+
console.log("✅ Audit notes generated → output/AUDIT_NOTES.md");
|
|
36
|
+
}
|
|
37
|
+
async function main() {
|
|
38
|
+
try {
|
|
39
|
+
if (cmd === "run") {
|
|
40
|
+
await run();
|
|
41
|
+
}
|
|
42
|
+
else if (cmd === "check") {
|
|
43
|
+
(0, license_1.validateLicense)();
|
|
44
|
+
console.log("✅ License valid");
|
|
45
|
+
}
|
|
46
|
+
else if (cmd === "version") {
|
|
47
|
+
console.log("audit-notes v0.1.0");
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
console.log(`
|
|
51
|
+
Usage:
|
|
52
|
+
audit-notes run
|
|
53
|
+
audit-notes check
|
|
54
|
+
audit-notes version
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
console.error("❌", err.message);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
main();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateInvariantHints = generateInvariantHints;
|
|
4
|
+
function generateInvariantHints(state) {
|
|
5
|
+
const invariants = [];
|
|
6
|
+
for (const [variable, writers] of Object.entries(state)) {
|
|
7
|
+
if (writers.length >= 2) {
|
|
8
|
+
invariants.push({
|
|
9
|
+
variable,
|
|
10
|
+
type: "consistency",
|
|
11
|
+
hint: `${variable} should remain internally consistent across ${writers.join(", ")}`
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
if (variable.toLowerCase().includes("total")) {
|
|
15
|
+
invariants.push({
|
|
16
|
+
variable,
|
|
17
|
+
type: "accounting",
|
|
18
|
+
hint: `${variable} should equal or exceed sum of dependent balances`
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return invariants;
|
|
23
|
+
}
|
package/dist/license.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateLicense = validateLicense;
|
|
4
|
+
function validateLicense() {
|
|
5
|
+
const key = process.env.AUDIT_NOTES_LICENSE;
|
|
6
|
+
if (!key) {
|
|
7
|
+
throw new Error("Missing license key. Set AUDIT_NOTES_LICENSE environment variable.");
|
|
8
|
+
}
|
|
9
|
+
if (key.length < 20 || !key.startsWith("AN-")) {
|
|
10
|
+
throw new Error("Invalid license key format.");
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateAuditNotes = generateAuditNotes;
|
|
7
|
+
const openai_1 = __importDefault(require("openai"));
|
|
8
|
+
let client = null;
|
|
9
|
+
function getClient() {
|
|
10
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
11
|
+
throw new Error("OPENAI_API_KEY not set");
|
|
12
|
+
}
|
|
13
|
+
if (!client) {
|
|
14
|
+
client = new openai_1.default({
|
|
15
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return client;
|
|
19
|
+
}
|
|
20
|
+
async function generateAuditNotes(prompt) {
|
|
21
|
+
const openai = getClient();
|
|
22
|
+
const res = await openai.chat.completions.create({
|
|
23
|
+
model: "gpt-4o-mini",
|
|
24
|
+
temperature: 0,
|
|
25
|
+
messages: [
|
|
26
|
+
{
|
|
27
|
+
role: "system",
|
|
28
|
+
content: `
|
|
29
|
+
You assist a professional smart contract auditor.
|
|
30
|
+
|
|
31
|
+
STRICT RULES:
|
|
32
|
+
- Use ONLY provided data
|
|
33
|
+
- Do NOT invent vulnerabilities
|
|
34
|
+
- Do NOT assign severity
|
|
35
|
+
- Do NOT suggest fixes
|
|
36
|
+
- Output structured audit notes and questions only
|
|
37
|
+
`.trim()
|
|
38
|
+
},
|
|
39
|
+
{ role: "user", content: prompt }
|
|
40
|
+
]
|
|
41
|
+
});
|
|
42
|
+
return res.choices[0].message.content ?? "";
|
|
43
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildPrompt = buildPrompt;
|
|
4
|
+
function buildPrompt(analysis, rules) {
|
|
5
|
+
return `
|
|
6
|
+
Produce audit notes with:
|
|
7
|
+
|
|
8
|
+
1. Privileged roles & risks
|
|
9
|
+
2. External call surfaces
|
|
10
|
+
3. Reentrancy attention areas
|
|
11
|
+
4. State mutation summary & invariants
|
|
12
|
+
5. Upgradeability risks
|
|
13
|
+
6. Critical audit questions (NO conclusions)
|
|
14
|
+
|
|
15
|
+
DATA:
|
|
16
|
+
${JSON.stringify(analysis, null, 2)}
|
|
17
|
+
|
|
18
|
+
RULES:
|
|
19
|
+
${JSON.stringify(rules, null, 2)}
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateMarkdown = generateMarkdown;
|
|
4
|
+
function generateMarkdown(input) {
|
|
5
|
+
const byImpact = {};
|
|
6
|
+
for (const f of input.findings) {
|
|
7
|
+
const key = f.impact ?? "Unknown";
|
|
8
|
+
byImpact[key] ?? (byImpact[key] = []);
|
|
9
|
+
byImpact[key].push(f);
|
|
10
|
+
}
|
|
11
|
+
let md = `# Smart Contract Audit Notes\n\n`;
|
|
12
|
+
md += `## Overview\n`;
|
|
13
|
+
md += `This document contains **automated audit notes** generated using static analysis tools.\n`;
|
|
14
|
+
md += `It is intended to **assist auditors and developers** during manual review.\n\n`;
|
|
15
|
+
md += `> ⚠️ **Disclaimer**\n`;
|
|
16
|
+
md += `> This report does **not** constitute a full security audit.\n`;
|
|
17
|
+
md += `> Findings represent **potential risk indicators**, not confirmed vulnerabilities.\n\n`;
|
|
18
|
+
md += `---\n\n`;
|
|
19
|
+
md += `## Scope\n`;
|
|
20
|
+
md += `- Target: Solidity smart contracts in the repository\n`;
|
|
21
|
+
md += `- Tooling: Slither (static analysis)\n`;
|
|
22
|
+
md += `- Coverage:\n`;
|
|
23
|
+
md += ` - Reentrancy patterns\n`;
|
|
24
|
+
md += ` - Low-level calls\n`;
|
|
25
|
+
md += ` - Compiler configuration issues\n`;
|
|
26
|
+
md += ` - Best-practice violations\n`;
|
|
27
|
+
md += `- Exclusions:\n`;
|
|
28
|
+
md += ` - Business logic correctness\n`;
|
|
29
|
+
md += ` - Economic & governance attacks\n`;
|
|
30
|
+
md += ` - Cross-protocol integrations\n\n`;
|
|
31
|
+
md += `---\n\n`;
|
|
32
|
+
md += `## Severity Interpretation\n`;
|
|
33
|
+
md += `- **High**: Likely exploitable patterns or critical misconfigurations\n`;
|
|
34
|
+
md += `- **Medium**: Context-dependent risks requiring review\n`;
|
|
35
|
+
md += `- **Low**: Best-practice or hygiene issues\n`;
|
|
36
|
+
md += `- **Informational**: Awareness items\n\n`;
|
|
37
|
+
md += `---\n\n`;
|
|
38
|
+
md += `## Summary\n`;
|
|
39
|
+
md += `Total findings identified: **${input.findings.length}**\n\n`;
|
|
40
|
+
for (const impact of Object.keys(byImpact)) {
|
|
41
|
+
md += `## ${impact} Severity Findings\n\n`;
|
|
42
|
+
for (const f of byImpact[impact]) {
|
|
43
|
+
md += `### ${f.check}\n\n`;
|
|
44
|
+
md += `${f.description.trim()}\n\n`;
|
|
45
|
+
if (f.locations.length > 0) {
|
|
46
|
+
md += `**Relevant Locations:**\n`;
|
|
47
|
+
for (const l of f.locations) {
|
|
48
|
+
md += `- \`${l.contract ?? "?"}::${l.function ?? "?"}\``;
|
|
49
|
+
if (l.lines)
|
|
50
|
+
md += ` (lines ${l.lines.join(", ")})`;
|
|
51
|
+
md += `\n`;
|
|
52
|
+
}
|
|
53
|
+
md += `\n`;
|
|
54
|
+
}
|
|
55
|
+
md += `---\n\n`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
md += `## Auditor Review Checklist\n`;
|
|
59
|
+
md += `- Validate exploitability of reentrancy paths\n`;
|
|
60
|
+
md += `- Review trust assumptions around low-level calls\n`;
|
|
61
|
+
md += `- Confirm compiler version risks are acceptable\n`;
|
|
62
|
+
md += `- Ensure findings align with protocol design intent\n\n`;
|
|
63
|
+
md += `---\n\n`;
|
|
64
|
+
md += `*Generated automatically. Manual review is strongly recommended.*\n`;
|
|
65
|
+
return md;
|
|
66
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scoreReentrancy = scoreReentrancy;
|
|
4
|
+
function scoreReentrancy(calls) {
|
|
5
|
+
return calls.map(call => {
|
|
6
|
+
let score = 0;
|
|
7
|
+
if (call.type === "call")
|
|
8
|
+
score += 2;
|
|
9
|
+
if (call.type === "delegatecall")
|
|
10
|
+
score += 5;
|
|
11
|
+
if (call.stateUpdatedAfter)
|
|
12
|
+
score += 3;
|
|
13
|
+
if (call.target?.includes("ERC777"))
|
|
14
|
+
score += 4;
|
|
15
|
+
let level = "LOW ATTENTION";
|
|
16
|
+
if (score >= 7)
|
|
17
|
+
level = "HIGH ATTENTION";
|
|
18
|
+
else if (score >= 4)
|
|
19
|
+
level = "MEDIUM ATTENTION";
|
|
20
|
+
return {
|
|
21
|
+
function: call.function,
|
|
22
|
+
target: call.target,
|
|
23
|
+
score,
|
|
24
|
+
level
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"access_control": {
|
|
3
|
+
"risk": "Privilege concentration",
|
|
4
|
+
"questions": [
|
|
5
|
+
"What happens if this role is compromised?",
|
|
6
|
+
"Can privileged roles modify core logic or funds?"
|
|
7
|
+
]
|
|
8
|
+
},
|
|
9
|
+
"external_calls": {
|
|
10
|
+
"risk": "Reentrancy and execution hijack",
|
|
11
|
+
"questions": [
|
|
12
|
+
"Is state updated before external calls?",
|
|
13
|
+
"Can a malicious token reenter?"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"state": {
|
|
17
|
+
"risk": "Invariant violations",
|
|
18
|
+
"questions": [
|
|
19
|
+
"What must always remain true?",
|
|
20
|
+
"Are invariants broken during flash loans?"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"upgradeability": {
|
|
24
|
+
"risk": "Logic replacement",
|
|
25
|
+
"questions": [
|
|
26
|
+
"Who authorizes upgrades?",
|
|
27
|
+
"Can upgrades happen mid-operation?"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "audit-notes-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "commonjs",
|
|
5
|
+
"bin": {
|
|
6
|
+
"audit-notes": "dist/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"openai": "^4.0.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"typescript": "^5.3.3",
|
|
17
|
+
"@types/node": "^20.11.0"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"smart-contract",
|
|
26
|
+
"security",
|
|
27
|
+
"audit",
|
|
28
|
+
"slither",
|
|
29
|
+
"web3"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
}
|
|
35
|
+
}
|