Spaces:
Runtime error
Runtime error
Uanderson Silva commited on
Commit Β·
48a8015
1
Parent(s): b66ebf4
use different models and add winston logger
Browse files- package-lock.json +281 -0
- package.json +1 -0
- src/agents/auditor/agent.ts +58 -25
- src/agents/auditor/model.ts +9 -1
- src/agents/auditor/prompts.ts +4 -8
- src/agents/auditor/state.ts +10 -5
- src/logger.ts +29 -0
package-lock.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
| 16 |
"@solidity-parser/parser": "^0.20.2",
|
| 17 |
"dotenv": "^17.4.2",
|
| 18 |
"langchain": "^1.4.0",
|
|
|
|
| 19 |
"zod": "^4.4.3"
|
| 20 |
},
|
| 21 |
"devDependencies": {
|
|
@@ -223,6 +224,26 @@
|
|
| 223 |
"integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
|
| 224 |
"license": "MIT"
|
| 225 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
"node_modules/@emnapi/core": {
|
| 227 |
"version": "1.10.0",
|
| 228 |
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
|
@@ -756,6 +777,16 @@
|
|
| 756 |
"dev": true,
|
| 757 |
"license": "MIT"
|
| 758 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 759 |
"node_modules/@solidity-parser/parser": {
|
| 760 |
"version": "0.20.2",
|
| 761 |
"resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.20.2.tgz",
|
|
@@ -820,6 +851,12 @@
|
|
| 820 |
"undici-types": "~7.19.0"
|
| 821 |
}
|
| 822 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 823 |
"node_modules/@vitest/expect": {
|
| 824 |
"version": "4.1.5",
|
| 825 |
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz",
|
|
@@ -955,6 +992,12 @@
|
|
| 955 |
"node": ">=12"
|
| 956 |
}
|
| 957 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 958 |
"node_modules/base64-js": {
|
| 959 |
"version": "1.5.1",
|
| 960 |
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
|
@@ -997,6 +1040,52 @@
|
|
| 997 |
"node": ">=18"
|
| 998 |
}
|
| 999 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1000 |
"node_modules/convert-source-map": {
|
| 1001 |
"version": "2.0.0",
|
| 1002 |
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
|
@@ -1035,6 +1124,12 @@
|
|
| 1035 |
"url": "https://dotenvx.com"
|
| 1036 |
}
|
| 1037 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1038 |
"node_modules/es-module-lexer": {
|
| 1039 |
"version": "2.1.0",
|
| 1040 |
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
|
|
@@ -1095,6 +1190,18 @@
|
|
| 1095 |
}
|
| 1096 |
}
|
| 1097 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1098 |
"node_modules/fsevents": {
|
| 1099 |
"version": "2.3.3",
|
| 1100 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
@@ -1110,6 +1217,12 @@
|
|
| 1110 |
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1111 |
}
|
| 1112 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1113 |
"node_modules/is-network-error": {
|
| 1114 |
"version": "1.3.1",
|
| 1115 |
"resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.1.tgz",
|
|
@@ -1122,6 +1235,18 @@
|
|
| 1122 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 1123 |
}
|
| 1124 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1125 |
"node_modules/js-tiktoken": {
|
| 1126 |
"version": "1.0.21",
|
| 1127 |
"resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz",
|
|
@@ -1144,6 +1269,12 @@
|
|
| 1144 |
"node": ">=16"
|
| 1145 |
}
|
| 1146 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1147 |
"node_modules/langchain": {
|
| 1148 |
"version": "1.4.0",
|
| 1149 |
"resolved": "https://registry.npmjs.org/langchain/-/langchain-1.4.0.tgz",
|
|
@@ -1456,6 +1587,23 @@
|
|
| 1456 |
"url": "https://opencollective.com/parcel"
|
| 1457 |
}
|
| 1458 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1459 |
"node_modules/magic-string": {
|
| 1460 |
"version": "0.30.21",
|
| 1461 |
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
|
@@ -1466,6 +1614,12 @@
|
|
| 1466 |
"@jridgewell/sourcemap-codec": "^1.5.5"
|
| 1467 |
}
|
| 1468 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1469 |
"node_modules/mustache": {
|
| 1470 |
"version": "4.2.0",
|
| 1471 |
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
|
|
@@ -1505,6 +1659,15 @@
|
|
| 1505 |
],
|
| 1506 |
"license": "MIT"
|
| 1507 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1508 |
"node_modules/openai": {
|
| 1509 |
"version": "6.36.0",
|
| 1510 |
"resolved": "https://registry.npmjs.org/openai/-/openai-6.36.0.tgz",
|
|
@@ -1634,6 +1797,20 @@
|
|
| 1634 |
"node": "^10 || ^12 || >=14"
|
| 1635 |
}
|
| 1636 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1637 |
"node_modules/rolldown": {
|
| 1638 |
"version": "1.0.0-rc.18",
|
| 1639 |
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz",
|
|
@@ -1668,6 +1845,35 @@
|
|
| 1668 |
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18"
|
| 1669 |
}
|
| 1670 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1671 |
"node_modules/siginfo": {
|
| 1672 |
"version": "2.0.0",
|
| 1673 |
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
|
|
@@ -1685,6 +1891,15 @@
|
|
| 1685 |
"node": ">=0.10.0"
|
| 1686 |
}
|
| 1687 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1688 |
"node_modules/stackback": {
|
| 1689 |
"version": "0.0.2",
|
| 1690 |
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
|
@@ -1699,6 +1914,21 @@
|
|
| 1699 |
"dev": true,
|
| 1700 |
"license": "MIT"
|
| 1701 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1702 |
"node_modules/tinybench": {
|
| 1703 |
"version": "2.9.0",
|
| 1704 |
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
|
@@ -1743,6 +1973,15 @@
|
|
| 1743 |
"node": ">=14.0.0"
|
| 1744 |
}
|
| 1745 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1746 |
"node_modules/ts-algebra": {
|
| 1747 |
"version": "2.0.0",
|
| 1748 |
"resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz",
|
|
@@ -1778,6 +2017,12 @@
|
|
| 1778 |
"dev": true,
|
| 1779 |
"license": "MIT"
|
| 1780 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1781 |
"node_modules/uuid": {
|
| 1782 |
"version": "10.0.0",
|
| 1783 |
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
|
@@ -1977,6 +2222,42 @@
|
|
| 1977 |
"node": ">=8"
|
| 1978 |
}
|
| 1979 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1980 |
"node_modules/zod": {
|
| 1981 |
"version": "4.4.3",
|
| 1982 |
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
|
|
|
| 16 |
"@solidity-parser/parser": "^0.20.2",
|
| 17 |
"dotenv": "^17.4.2",
|
| 18 |
"langchain": "^1.4.0",
|
| 19 |
+
"winston": "^3.19.0",
|
| 20 |
"zod": "^4.4.3"
|
| 21 |
},
|
| 22 |
"devDependencies": {
|
|
|
|
| 224 |
"integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
|
| 225 |
"license": "MIT"
|
| 226 |
},
|
| 227 |
+
"node_modules/@colors/colors": {
|
| 228 |
+
"version": "1.6.0",
|
| 229 |
+
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
|
| 230 |
+
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
|
| 231 |
+
"license": "MIT",
|
| 232 |
+
"engines": {
|
| 233 |
+
"node": ">=0.1.90"
|
| 234 |
+
}
|
| 235 |
+
},
|
| 236 |
+
"node_modules/@dabh/diagnostics": {
|
| 237 |
+
"version": "2.0.8",
|
| 238 |
+
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz",
|
| 239 |
+
"integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==",
|
| 240 |
+
"license": "MIT",
|
| 241 |
+
"dependencies": {
|
| 242 |
+
"@so-ric/colorspace": "^1.1.6",
|
| 243 |
+
"enabled": "2.0.x",
|
| 244 |
+
"kuler": "^2.0.0"
|
| 245 |
+
}
|
| 246 |
+
},
|
| 247 |
"node_modules/@emnapi/core": {
|
| 248 |
"version": "1.10.0",
|
| 249 |
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
|
|
|
| 777 |
"dev": true,
|
| 778 |
"license": "MIT"
|
| 779 |
},
|
| 780 |
+
"node_modules/@so-ric/colorspace": {
|
| 781 |
+
"version": "1.1.6",
|
| 782 |
+
"resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
|
| 783 |
+
"integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==",
|
| 784 |
+
"license": "MIT",
|
| 785 |
+
"dependencies": {
|
| 786 |
+
"color": "^5.0.2",
|
| 787 |
+
"text-hex": "1.0.x"
|
| 788 |
+
}
|
| 789 |
+
},
|
| 790 |
"node_modules/@solidity-parser/parser": {
|
| 791 |
"version": "0.20.2",
|
| 792 |
"resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.20.2.tgz",
|
|
|
|
| 851 |
"undici-types": "~7.19.0"
|
| 852 |
}
|
| 853 |
},
|
| 854 |
+
"node_modules/@types/triple-beam": {
|
| 855 |
+
"version": "1.3.5",
|
| 856 |
+
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
|
| 857 |
+
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
|
| 858 |
+
"license": "MIT"
|
| 859 |
+
},
|
| 860 |
"node_modules/@vitest/expect": {
|
| 861 |
"version": "4.1.5",
|
| 862 |
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz",
|
|
|
|
| 992 |
"node": ">=12"
|
| 993 |
}
|
| 994 |
},
|
| 995 |
+
"node_modules/async": {
|
| 996 |
+
"version": "3.2.6",
|
| 997 |
+
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
| 998 |
+
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
| 999 |
+
"license": "MIT"
|
| 1000 |
+
},
|
| 1001 |
"node_modules/base64-js": {
|
| 1002 |
"version": "1.5.1",
|
| 1003 |
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
|
|
|
| 1040 |
"node": ">=18"
|
| 1041 |
}
|
| 1042 |
},
|
| 1043 |
+
"node_modules/color": {
|
| 1044 |
+
"version": "5.0.3",
|
| 1045 |
+
"resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz",
|
| 1046 |
+
"integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==",
|
| 1047 |
+
"license": "MIT",
|
| 1048 |
+
"dependencies": {
|
| 1049 |
+
"color-convert": "^3.1.3",
|
| 1050 |
+
"color-string": "^2.1.3"
|
| 1051 |
+
},
|
| 1052 |
+
"engines": {
|
| 1053 |
+
"node": ">=18"
|
| 1054 |
+
}
|
| 1055 |
+
},
|
| 1056 |
+
"node_modules/color-convert": {
|
| 1057 |
+
"version": "3.1.3",
|
| 1058 |
+
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz",
|
| 1059 |
+
"integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
|
| 1060 |
+
"license": "MIT",
|
| 1061 |
+
"dependencies": {
|
| 1062 |
+
"color-name": "^2.0.0"
|
| 1063 |
+
},
|
| 1064 |
+
"engines": {
|
| 1065 |
+
"node": ">=14.6"
|
| 1066 |
+
}
|
| 1067 |
+
},
|
| 1068 |
+
"node_modules/color-name": {
|
| 1069 |
+
"version": "2.1.0",
|
| 1070 |
+
"resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
|
| 1071 |
+
"integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
|
| 1072 |
+
"license": "MIT",
|
| 1073 |
+
"engines": {
|
| 1074 |
+
"node": ">=12.20"
|
| 1075 |
+
}
|
| 1076 |
+
},
|
| 1077 |
+
"node_modules/color-string": {
|
| 1078 |
+
"version": "2.1.4",
|
| 1079 |
+
"resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
|
| 1080 |
+
"integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
|
| 1081 |
+
"license": "MIT",
|
| 1082 |
+
"dependencies": {
|
| 1083 |
+
"color-name": "^2.0.0"
|
| 1084 |
+
},
|
| 1085 |
+
"engines": {
|
| 1086 |
+
"node": ">=18"
|
| 1087 |
+
}
|
| 1088 |
+
},
|
| 1089 |
"node_modules/convert-source-map": {
|
| 1090 |
"version": "2.0.0",
|
| 1091 |
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
|
|
|
| 1124 |
"url": "https://dotenvx.com"
|
| 1125 |
}
|
| 1126 |
},
|
| 1127 |
+
"node_modules/enabled": {
|
| 1128 |
+
"version": "2.0.0",
|
| 1129 |
+
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
|
| 1130 |
+
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
|
| 1131 |
+
"license": "MIT"
|
| 1132 |
+
},
|
| 1133 |
"node_modules/es-module-lexer": {
|
| 1134 |
"version": "2.1.0",
|
| 1135 |
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
|
|
|
|
| 1190 |
}
|
| 1191 |
}
|
| 1192 |
},
|
| 1193 |
+
"node_modules/fecha": {
|
| 1194 |
+
"version": "4.2.3",
|
| 1195 |
+
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
|
| 1196 |
+
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
|
| 1197 |
+
"license": "MIT"
|
| 1198 |
+
},
|
| 1199 |
+
"node_modules/fn.name": {
|
| 1200 |
+
"version": "1.1.0",
|
| 1201 |
+
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
| 1202 |
+
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
|
| 1203 |
+
"license": "MIT"
|
| 1204 |
+
},
|
| 1205 |
"node_modules/fsevents": {
|
| 1206 |
"version": "2.3.3",
|
| 1207 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
|
|
| 1217 |
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1218 |
}
|
| 1219 |
},
|
| 1220 |
+
"node_modules/inherits": {
|
| 1221 |
+
"version": "2.0.4",
|
| 1222 |
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
| 1223 |
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 1224 |
+
"license": "ISC"
|
| 1225 |
+
},
|
| 1226 |
"node_modules/is-network-error": {
|
| 1227 |
"version": "1.3.1",
|
| 1228 |
"resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.1.tgz",
|
|
|
|
| 1235 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 1236 |
}
|
| 1237 |
},
|
| 1238 |
+
"node_modules/is-stream": {
|
| 1239 |
+
"version": "2.0.1",
|
| 1240 |
+
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
| 1241 |
+
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
| 1242 |
+
"license": "MIT",
|
| 1243 |
+
"engines": {
|
| 1244 |
+
"node": ">=8"
|
| 1245 |
+
},
|
| 1246 |
+
"funding": {
|
| 1247 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1248 |
+
}
|
| 1249 |
+
},
|
| 1250 |
"node_modules/js-tiktoken": {
|
| 1251 |
"version": "1.0.21",
|
| 1252 |
"resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz",
|
|
|
|
| 1269 |
"node": ">=16"
|
| 1270 |
}
|
| 1271 |
},
|
| 1272 |
+
"node_modules/kuler": {
|
| 1273 |
+
"version": "2.0.0",
|
| 1274 |
+
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
|
| 1275 |
+
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
|
| 1276 |
+
"license": "MIT"
|
| 1277 |
+
},
|
| 1278 |
"node_modules/langchain": {
|
| 1279 |
"version": "1.4.0",
|
| 1280 |
"resolved": "https://registry.npmjs.org/langchain/-/langchain-1.4.0.tgz",
|
|
|
|
| 1587 |
"url": "https://opencollective.com/parcel"
|
| 1588 |
}
|
| 1589 |
},
|
| 1590 |
+
"node_modules/logform": {
|
| 1591 |
+
"version": "2.7.0",
|
| 1592 |
+
"resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
|
| 1593 |
+
"integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
|
| 1594 |
+
"license": "MIT",
|
| 1595 |
+
"dependencies": {
|
| 1596 |
+
"@colors/colors": "1.6.0",
|
| 1597 |
+
"@types/triple-beam": "^1.3.2",
|
| 1598 |
+
"fecha": "^4.2.0",
|
| 1599 |
+
"ms": "^2.1.1",
|
| 1600 |
+
"safe-stable-stringify": "^2.3.1",
|
| 1601 |
+
"triple-beam": "^1.3.0"
|
| 1602 |
+
},
|
| 1603 |
+
"engines": {
|
| 1604 |
+
"node": ">= 12.0.0"
|
| 1605 |
+
}
|
| 1606 |
+
},
|
| 1607 |
"node_modules/magic-string": {
|
| 1608 |
"version": "0.30.21",
|
| 1609 |
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
|
|
|
| 1614 |
"@jridgewell/sourcemap-codec": "^1.5.5"
|
| 1615 |
}
|
| 1616 |
},
|
| 1617 |
+
"node_modules/ms": {
|
| 1618 |
+
"version": "2.1.3",
|
| 1619 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 1620 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 1621 |
+
"license": "MIT"
|
| 1622 |
+
},
|
| 1623 |
"node_modules/mustache": {
|
| 1624 |
"version": "4.2.0",
|
| 1625 |
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
|
|
|
|
| 1659 |
],
|
| 1660 |
"license": "MIT"
|
| 1661 |
},
|
| 1662 |
+
"node_modules/one-time": {
|
| 1663 |
+
"version": "1.0.0",
|
| 1664 |
+
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
|
| 1665 |
+
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
|
| 1666 |
+
"license": "MIT",
|
| 1667 |
+
"dependencies": {
|
| 1668 |
+
"fn.name": "1.x.x"
|
| 1669 |
+
}
|
| 1670 |
+
},
|
| 1671 |
"node_modules/openai": {
|
| 1672 |
"version": "6.36.0",
|
| 1673 |
"resolved": "https://registry.npmjs.org/openai/-/openai-6.36.0.tgz",
|
|
|
|
| 1797 |
"node": "^10 || ^12 || >=14"
|
| 1798 |
}
|
| 1799 |
},
|
| 1800 |
+
"node_modules/readable-stream": {
|
| 1801 |
+
"version": "3.6.2",
|
| 1802 |
+
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
| 1803 |
+
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
| 1804 |
+
"license": "MIT",
|
| 1805 |
+
"dependencies": {
|
| 1806 |
+
"inherits": "^2.0.3",
|
| 1807 |
+
"string_decoder": "^1.1.1",
|
| 1808 |
+
"util-deprecate": "^1.0.1"
|
| 1809 |
+
},
|
| 1810 |
+
"engines": {
|
| 1811 |
+
"node": ">= 6"
|
| 1812 |
+
}
|
| 1813 |
+
},
|
| 1814 |
"node_modules/rolldown": {
|
| 1815 |
"version": "1.0.0-rc.18",
|
| 1816 |
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz",
|
|
|
|
| 1845 |
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18"
|
| 1846 |
}
|
| 1847 |
},
|
| 1848 |
+
"node_modules/safe-buffer": {
|
| 1849 |
+
"version": "5.2.1",
|
| 1850 |
+
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
| 1851 |
+
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
| 1852 |
+
"funding": [
|
| 1853 |
+
{
|
| 1854 |
+
"type": "github",
|
| 1855 |
+
"url": "https://github.com/sponsors/feross"
|
| 1856 |
+
},
|
| 1857 |
+
{
|
| 1858 |
+
"type": "patreon",
|
| 1859 |
+
"url": "https://www.patreon.com/feross"
|
| 1860 |
+
},
|
| 1861 |
+
{
|
| 1862 |
+
"type": "consulting",
|
| 1863 |
+
"url": "https://feross.org/support"
|
| 1864 |
+
}
|
| 1865 |
+
],
|
| 1866 |
+
"license": "MIT"
|
| 1867 |
+
},
|
| 1868 |
+
"node_modules/safe-stable-stringify": {
|
| 1869 |
+
"version": "2.5.0",
|
| 1870 |
+
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
| 1871 |
+
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
| 1872 |
+
"license": "MIT",
|
| 1873 |
+
"engines": {
|
| 1874 |
+
"node": ">=10"
|
| 1875 |
+
}
|
| 1876 |
+
},
|
| 1877 |
"node_modules/siginfo": {
|
| 1878 |
"version": "2.0.0",
|
| 1879 |
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
|
|
|
|
| 1891 |
"node": ">=0.10.0"
|
| 1892 |
}
|
| 1893 |
},
|
| 1894 |
+
"node_modules/stack-trace": {
|
| 1895 |
+
"version": "0.0.10",
|
| 1896 |
+
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
| 1897 |
+
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
|
| 1898 |
+
"license": "MIT",
|
| 1899 |
+
"engines": {
|
| 1900 |
+
"node": "*"
|
| 1901 |
+
}
|
| 1902 |
+
},
|
| 1903 |
"node_modules/stackback": {
|
| 1904 |
"version": "0.0.2",
|
| 1905 |
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
|
|
|
| 1914 |
"dev": true,
|
| 1915 |
"license": "MIT"
|
| 1916 |
},
|
| 1917 |
+
"node_modules/string_decoder": {
|
| 1918 |
+
"version": "1.3.0",
|
| 1919 |
+
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
| 1920 |
+
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
| 1921 |
+
"license": "MIT",
|
| 1922 |
+
"dependencies": {
|
| 1923 |
+
"safe-buffer": "~5.2.0"
|
| 1924 |
+
}
|
| 1925 |
+
},
|
| 1926 |
+
"node_modules/text-hex": {
|
| 1927 |
+
"version": "1.0.0",
|
| 1928 |
+
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
|
| 1929 |
+
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
|
| 1930 |
+
"license": "MIT"
|
| 1931 |
+
},
|
| 1932 |
"node_modules/tinybench": {
|
| 1933 |
"version": "2.9.0",
|
| 1934 |
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
|
|
|
| 1973 |
"node": ">=14.0.0"
|
| 1974 |
}
|
| 1975 |
},
|
| 1976 |
+
"node_modules/triple-beam": {
|
| 1977 |
+
"version": "1.4.1",
|
| 1978 |
+
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
|
| 1979 |
+
"integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
|
| 1980 |
+
"license": "MIT",
|
| 1981 |
+
"engines": {
|
| 1982 |
+
"node": ">= 14.0.0"
|
| 1983 |
+
}
|
| 1984 |
+
},
|
| 1985 |
"node_modules/ts-algebra": {
|
| 1986 |
"version": "2.0.0",
|
| 1987 |
"resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz",
|
|
|
|
| 2017 |
"dev": true,
|
| 2018 |
"license": "MIT"
|
| 2019 |
},
|
| 2020 |
+
"node_modules/util-deprecate": {
|
| 2021 |
+
"version": "1.0.2",
|
| 2022 |
+
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
| 2023 |
+
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
| 2024 |
+
"license": "MIT"
|
| 2025 |
+
},
|
| 2026 |
"node_modules/uuid": {
|
| 2027 |
"version": "10.0.0",
|
| 2028 |
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
|
|
|
| 2222 |
"node": ">=8"
|
| 2223 |
}
|
| 2224 |
},
|
| 2225 |
+
"node_modules/winston": {
|
| 2226 |
+
"version": "3.19.0",
|
| 2227 |
+
"resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz",
|
| 2228 |
+
"integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==",
|
| 2229 |
+
"license": "MIT",
|
| 2230 |
+
"dependencies": {
|
| 2231 |
+
"@colors/colors": "^1.6.0",
|
| 2232 |
+
"@dabh/diagnostics": "^2.0.8",
|
| 2233 |
+
"async": "^3.2.3",
|
| 2234 |
+
"is-stream": "^2.0.0",
|
| 2235 |
+
"logform": "^2.7.0",
|
| 2236 |
+
"one-time": "^1.0.0",
|
| 2237 |
+
"readable-stream": "^3.4.0",
|
| 2238 |
+
"safe-stable-stringify": "^2.3.1",
|
| 2239 |
+
"stack-trace": "0.0.x",
|
| 2240 |
+
"triple-beam": "^1.3.0",
|
| 2241 |
+
"winston-transport": "^4.9.0"
|
| 2242 |
+
},
|
| 2243 |
+
"engines": {
|
| 2244 |
+
"node": ">= 12.0.0"
|
| 2245 |
+
}
|
| 2246 |
+
},
|
| 2247 |
+
"node_modules/winston-transport": {
|
| 2248 |
+
"version": "4.9.0",
|
| 2249 |
+
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
|
| 2250 |
+
"integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
|
| 2251 |
+
"license": "MIT",
|
| 2252 |
+
"dependencies": {
|
| 2253 |
+
"logform": "^2.7.0",
|
| 2254 |
+
"readable-stream": "^3.6.2",
|
| 2255 |
+
"triple-beam": "^1.3.0"
|
| 2256 |
+
},
|
| 2257 |
+
"engines": {
|
| 2258 |
+
"node": ">= 12.0.0"
|
| 2259 |
+
}
|
| 2260 |
+
},
|
| 2261 |
"node_modules/zod": {
|
| 2262 |
"version": "4.4.3",
|
| 2263 |
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
package.json
CHANGED
|
@@ -34,6 +34,7 @@
|
|
| 34 |
"@solidity-parser/parser": "^0.20.2",
|
| 35 |
"dotenv": "^17.4.2",
|
| 36 |
"langchain": "^1.4.0",
|
|
|
|
| 37 |
"zod": "^4.4.3"
|
| 38 |
}
|
| 39 |
}
|
|
|
|
| 34 |
"@solidity-parser/parser": "^0.20.2",
|
| 35 |
"dotenv": "^17.4.2",
|
| 36 |
"langchain": "^1.4.0",
|
| 37 |
+
"winston": "^3.19.0",
|
| 38 |
"zod": "^4.4.3"
|
| 39 |
}
|
| 40 |
}
|
src/agents/auditor/agent.ts
CHANGED
|
@@ -5,9 +5,10 @@ import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
| 5 |
import { END, type GraphNode, START, StateGraph } from "@langchain/langgraph";
|
| 6 |
import { z } from "zod";
|
| 7 |
|
| 8 |
-
import {
|
| 9 |
-
import {
|
| 10 |
-
import {
|
|
|
|
| 11 |
import { analyzeSolidityFile } from "./tools/solidity-analyzer-tool.ts";
|
| 12 |
import {
|
| 13 |
DOC_BASENAMES,
|
|
@@ -52,15 +53,23 @@ const walkDirectory = (dir: string, depth: number, solFiles: string[], docFiles:
|
|
| 52 |
};
|
| 53 |
|
| 54 |
const defineScope: GraphNode<typeof AuditorState> = async (state) => {
|
|
|
|
|
|
|
| 55 |
const solFiles: string[] = [];
|
| 56 |
const docFiles: string[] = [];
|
| 57 |
|
| 58 |
walkDirectory(state.repoPath, 0, solFiles, docFiles);
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
return { scope: solFiles, docs: docFiles };
|
| 61 |
};
|
| 62 |
|
| 63 |
const gatherContext: GraphNode<typeof AuditorState> = async (state) => {
|
|
|
|
|
|
|
| 64 |
const readFile = (filePath: string): string => {
|
| 65 |
try {
|
| 66 |
return fs.readFileSync(filePath, "utf-8");
|
|
@@ -74,7 +83,7 @@ const gatherContext: GraphNode<typeof AuditorState> = async (state) => {
|
|
| 74 |
for (const filePath of state.scope) {
|
| 75 |
const source = readFile(filePath).slice(0, MAX_SOL_CHARS);
|
| 76 |
if (!source) continue;
|
| 77 |
-
const analysis = await analyzeSolidityFile(source, "
|
| 78 |
solidityEntries.push({ filePath, source, analysis });
|
| 79 |
}
|
| 80 |
|
|
@@ -110,66 +119,82 @@ const gatherContext: GraphNode<typeof AuditorState> = async (state) => {
|
|
| 110 |
parts.push(`### ${filePath}\n\`\`\`solidity\n${source}\n\`\`\``);
|
| 111 |
}
|
| 112 |
|
| 113 |
-
const model =
|
| 114 |
-
const result = await model.invoke([new SystemMessage(GATHER_CONTEXT_PROMPT), new HumanMessage(parts.join("\n\n"))]);
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
-
return { solidityFile, repoContext:
|
| 117 |
};
|
| 118 |
|
| 119 |
const findVulnerabilities: GraphNode<typeof AuditorState> = async (state) => {
|
| 120 |
-
const model =
|
| 121 |
|
| 122 |
let userMessage = `Contract:\n\n${state.solidityFile}\n\nProtocol Context:\n${state.repoContext}`;
|
| 123 |
|
| 124 |
-
if (state.
|
| 125 |
-
const feedback = state.
|
| 126 |
.map(
|
| 127 |
-
(r) =>
|
| 128 |
-
`- "${r.findingTitle}": ${r.isFalsePositive ? "FALSE POSITIVE" : "TRUE POSITIVE"}\n Critic: ${r.review}`,
|
| 129 |
)
|
| 130 |
.join("\n");
|
| 131 |
-
userMessage += `\n\
|
| 132 |
}
|
| 133 |
|
|
|
|
|
|
|
|
|
|
| 134 |
const result = await model.invoke([new SystemMessage(FIND_VULNERABILITIES_PROMPT), new HumanMessage(userMessage)]);
|
| 135 |
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
| 137 |
};
|
| 138 |
|
| 139 |
-
const
|
| 140 |
if (state.candidateFindings.length === 0) {
|
|
|
|
| 141 |
return {
|
| 142 |
-
|
| 143 |
findings: [],
|
| 144 |
reflectionCount: state.reflectionCount + 1,
|
| 145 |
};
|
| 146 |
}
|
| 147 |
|
| 148 |
-
const model =
|
| 149 |
|
| 150 |
const findingsText = state.candidateFindings
|
| 151 |
.map(
|
| 152 |
(f, i) =>
|
| 153 |
-
`[Finding ${i + 1}] ${f.title}\nSeverity: ${f.severity}
|
| 154 |
)
|
| 155 |
.join("\n\n---\n\n");
|
| 156 |
|
|
|
|
|
|
|
|
|
|
| 157 |
const result = await model.invoke([
|
| 158 |
-
new SystemMessage(
|
| 159 |
new HumanMessage(
|
| 160 |
`Contract:\n\n${state.solidityFile}\n\nProtocol Context:\n${state.repoContext}\n\nCandidate Findings to Review:\n\n${findingsText}`,
|
| 161 |
),
|
| 162 |
]);
|
| 163 |
|
| 164 |
-
const reviewsByTitle = new Map(result.reviews.map((r) => [r.findingTitle.toLowerCase(), r]));
|
| 165 |
|
| 166 |
const confirmedFindings = state.candidateFindings.filter((f, i) => {
|
| 167 |
const review = reviewsByTitle.get(f.title.toLowerCase()) ?? result.reviews[i];
|
| 168 |
return review ? !review.isFalsePositive : true;
|
| 169 |
});
|
| 170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
return {
|
| 172 |
-
|
| 173 |
findings: confirmedFindings,
|
| 174 |
reflectionCount: state.reflectionCount + 1,
|
| 175 |
};
|
|
@@ -179,16 +204,24 @@ export const auditorAgent = new StateGraph(AuditorState)
|
|
| 179 |
.addNode("defineScope", defineScope)
|
| 180 |
.addNode("gatherContext", gatherContext)
|
| 181 |
.addNode("findVulnerabilities", findVulnerabilities)
|
| 182 |
-
.addNode("
|
| 183 |
.addEdge(START, "defineScope")
|
| 184 |
.addEdge("defineScope", "gatherContext")
|
| 185 |
.addEdge("gatherContext", "findVulnerabilities")
|
| 186 |
-
.addEdge("findVulnerabilities", "
|
| 187 |
-
.addConditionalEdges("
|
| 188 |
-
const hasFalsePositives = state.
|
| 189 |
if (hasFalsePositives && state.reflectionCount < MAX_REFLECTIONS) {
|
| 190 |
return "findVulnerabilities";
|
| 191 |
}
|
| 192 |
return END;
|
| 193 |
})
|
| 194 |
.compile();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
import { END, type GraphNode, START, StateGraph } from "@langchain/langgraph";
|
| 6 |
import { z } from "zod";
|
| 7 |
|
| 8 |
+
import { logger } from "../../logger.ts";
|
| 9 |
+
import { judgeFindingsModel, findVulnerabilitiesModel, gatherContextModel } from "./model.ts";
|
| 10 |
+
import { JUDGE_FINDINGS_PROMPT, FIND_VULNERABILITIES_PROMPT, GATHER_CONTEXT_PROMPT } from "./prompts.ts";
|
| 11 |
+
import { AuditorState, ReviewSchema, PartialFindingSchema } from "./state.ts";
|
| 12 |
import { analyzeSolidityFile } from "./tools/solidity-analyzer-tool.ts";
|
| 13 |
import {
|
| 14 |
DOC_BASENAMES,
|
|
|
|
| 53 |
};
|
| 54 |
|
| 55 |
const defineScope: GraphNode<typeof AuditorState> = async (state) => {
|
| 56 |
+
logger.info(`defineScope: walking repo at ${state.repoPath}`);
|
| 57 |
+
|
| 58 |
const solFiles: string[] = [];
|
| 59 |
const docFiles: string[] = [];
|
| 60 |
|
| 61 |
walkDirectory(state.repoPath, 0, solFiles, docFiles);
|
| 62 |
|
| 63 |
+
logger.info(`defineScope: found ${solFiles.length} Solidity file(s), ${docFiles.length} doc file(s)`);
|
| 64 |
+
logger.debug(`defineScope: Solidity files: ${JSON.stringify(solFiles)}`);
|
| 65 |
+
logger.debug(`defineScope: doc files: ${JSON.stringify(docFiles)}`);
|
| 66 |
+
|
| 67 |
return { scope: solFiles, docs: docFiles };
|
| 68 |
};
|
| 69 |
|
| 70 |
const gatherContext: GraphNode<typeof AuditorState> = async (state) => {
|
| 71 |
+
logger.info(`gatherContext: processing ${state.scope.length} Solidity file(s) and ${state.docs.length} doc file(s)`);
|
| 72 |
+
|
| 73 |
const readFile = (filePath: string): string => {
|
| 74 |
try {
|
| 75 |
return fs.readFileSync(filePath, "utf-8");
|
|
|
|
| 83 |
for (const filePath of state.scope) {
|
| 84 |
const source = readFile(filePath).slice(0, MAX_SOL_CHARS);
|
| 85 |
if (!source) continue;
|
| 86 |
+
const analysis = await analyzeSolidityFile(source, "full");
|
| 87 |
solidityEntries.push({ filePath, source, analysis });
|
| 88 |
}
|
| 89 |
|
|
|
|
| 119 |
parts.push(`### ${filePath}\n\`\`\`solidity\n${source}\n\`\`\``);
|
| 120 |
}
|
| 121 |
|
| 122 |
+
// const model = gatherContextModel.withStructuredOutput(z.object({ context: z.string() }));
|
| 123 |
+
// const result = await model.invoke([new SystemMessage(GATHER_CONTEXT_PROMPT), new HumanMessage(parts.join("\n\n"))]);
|
| 124 |
+
|
| 125 |
+
logger.info(`gatherContext: context built (${parts.join("\n\n").length} chars)`);
|
| 126 |
+
logger.debug(`gatherContext: full context:\n${parts.join("\n\n")}`);
|
| 127 |
|
| 128 |
+
return { solidityFile, repoContext: parts.join("\n\n") };
|
| 129 |
};
|
| 130 |
|
| 131 |
const findVulnerabilities: GraphNode<typeof AuditorState> = async (state) => {
|
| 132 |
+
const model = findVulnerabilitiesModel.withStructuredOutput(z.object({ findings: z.array(PartialFindingSchema) }));
|
| 133 |
|
| 134 |
let userMessage = `Contract:\n\n${state.solidityFile}\n\nProtocol Context:\n${state.repoContext}`;
|
| 135 |
|
| 136 |
+
if (state.judgeReviews.length > 0) {
|
| 137 |
+
const feedback = state.judgeReviews
|
| 138 |
.map(
|
| 139 |
+
(r) => `- "${r /*.title*/}": ${r.isFalsePositive ? "FALSE POSITIVE" : "TRUE POSITIVE"}\n Judge: ${r.review}`,
|
|
|
|
| 140 |
)
|
| 141 |
.join("\n");
|
| 142 |
+
userMessage += `\n\nJudge feedback from previous iteration (iteration ${state.reflectionCount}):\n${feedback}\n\nRevise your findings accordingly.`;
|
| 143 |
}
|
| 144 |
|
| 145 |
+
logger.info(`findVulnerabilities: invoking LLM (iteration ${state.reflectionCount + 1})`);
|
| 146 |
+
logger.debug(`findVulnerabilities: user message:\n${userMessage}`);
|
| 147 |
+
|
| 148 |
const result = await model.invoke([new SystemMessage(FIND_VULNERABILITIES_PROMPT), new HumanMessage(userMessage)]);
|
| 149 |
|
| 150 |
+
logger.info(`findVulnerabilities: LLM returned ${result.findings.length} candidate finding(s)`);
|
| 151 |
+
logger.debug(`findVulnerabilities: findings:\n${JSON.stringify(result.findings, null, 2)}`);
|
| 152 |
+
|
| 153 |
+
return { candidateFindings: result.findings.map((finding: any) => ({ ...finding, path: "/", location: "1-14" })) };
|
| 154 |
};
|
| 155 |
|
| 156 |
+
const judgeFindings: GraphNode<typeof AuditorState> = async (state) => {
|
| 157 |
if (state.candidateFindings.length === 0) {
|
| 158 |
+
logger.info("judgeFindings: no candidate findings to review, skipping LLM call");
|
| 159 |
return {
|
| 160 |
+
judgeReviews: [],
|
| 161 |
findings: [],
|
| 162 |
reflectionCount: state.reflectionCount + 1,
|
| 163 |
};
|
| 164 |
}
|
| 165 |
|
| 166 |
+
const model = findVulnerabilitiesModel.withStructuredOutput(z.object({ reviews: z.array(ReviewSchema) }));
|
| 167 |
|
| 168 |
const findingsText = state.candidateFindings
|
| 169 |
.map(
|
| 170 |
(f, i) =>
|
| 171 |
+
`[Finding ${i + 1}] ${f.title}\nSeverity: ${f.severity}\nDescription: ${f.description}\nLocation: ${f.path} lines ${f.location}\nCode:\n\`\`\`solidity\n${f.codeSnippet}\n\`\`\``,
|
| 172 |
)
|
| 173 |
.join("\n\n---\n\n");
|
| 174 |
|
| 175 |
+
logger.info(`judgeFindings: reviewing ${state.candidateFindings.length} candidate finding(s)`);
|
| 176 |
+
logger.debug(`judgeFindings: findings text:\n${findingsText}`);
|
| 177 |
+
|
| 178 |
const result = await model.invoke([
|
| 179 |
+
new SystemMessage(JUDGE_FINDINGS_PROMPT),
|
| 180 |
new HumanMessage(
|
| 181 |
`Contract:\n\n${state.solidityFile}\n\nProtocol Context:\n${state.repoContext}\n\nCandidate Findings to Review:\n\n${findingsText}`,
|
| 182 |
),
|
| 183 |
]);
|
| 184 |
|
| 185 |
+
const reviewsByTitle = new Map(result.reviews.map((r: any) => [r.findingTitle.toLowerCase(), r]));
|
| 186 |
|
| 187 |
const confirmedFindings = state.candidateFindings.filter((f, i) => {
|
| 188 |
const review = reviewsByTitle.get(f.title.toLowerCase()) ?? result.reviews[i];
|
| 189 |
return review ? !review.isFalsePositive : true;
|
| 190 |
});
|
| 191 |
|
| 192 |
+
const falsePositiveCount = state.candidateFindings.length - confirmedFindings.length;
|
| 193 |
+
logger.info(`judgeFindings: ${confirmedFindings.length} confirmed, ${falsePositiveCount} false positive(s)`);
|
| 194 |
+
logger.debug(`judgeFindings: reviews:\n${JSON.stringify(result.reviews, null, 2)}`);
|
| 195 |
+
|
| 196 |
return {
|
| 197 |
+
judgeReviews: result.reviews,
|
| 198 |
findings: confirmedFindings,
|
| 199 |
reflectionCount: state.reflectionCount + 1,
|
| 200 |
};
|
|
|
|
| 204 |
.addNode("defineScope", defineScope)
|
| 205 |
.addNode("gatherContext", gatherContext)
|
| 206 |
.addNode("findVulnerabilities", findVulnerabilities)
|
| 207 |
+
.addNode("judgeFindings", judgeFindings)
|
| 208 |
.addEdge(START, "defineScope")
|
| 209 |
.addEdge("defineScope", "gatherContext")
|
| 210 |
.addEdge("gatherContext", "findVulnerabilities")
|
| 211 |
+
.addEdge("findVulnerabilities", "judgeFindings")
|
| 212 |
+
.addConditionalEdges("judgeFindings", (state) => {
|
| 213 |
+
const hasFalsePositives = state.judgeReviews.some((r) => r.isFalsePositive);
|
| 214 |
if (hasFalsePositives && state.reflectionCount < MAX_REFLECTIONS) {
|
| 215 |
return "findVulnerabilities";
|
| 216 |
}
|
| 217 |
return END;
|
| 218 |
})
|
| 219 |
.compile();
|
| 220 |
+
|
| 221 |
+
export const testAgent = new StateGraph(AuditorState)
|
| 222 |
+
.addNode("defineScope", defineScope)
|
| 223 |
+
.addNode("gatherContext", gatherContext)
|
| 224 |
+
.addEdge(START, "defineScope")
|
| 225 |
+
.addEdge("defineScope", "gatherContext")
|
| 226 |
+
.addEdge("gatherContext", END)
|
| 227 |
+
.compile();
|
src/agents/auditor/model.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
| 1 |
import { ChatAnthropic } from "@langchain/anthropic";
|
| 2 |
|
| 3 |
-
export const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
model: "claude-sonnet-4-6",
|
| 5 |
});
|
|
|
|
| 1 |
import { ChatAnthropic } from "@langchain/anthropic";
|
| 2 |
|
| 3 |
+
export const gatherContextModel = new ChatAnthropic({
|
| 4 |
+
model: "claude-haiku-4-5",
|
| 5 |
+
});
|
| 6 |
+
|
| 7 |
+
export const findVulnerabilitiesModel = new ChatAnthropic({
|
| 8 |
+
model: "claude-sonnet-4-6",
|
| 9 |
+
});
|
| 10 |
+
|
| 11 |
+
export const judgeFindingsModel = new ChatAnthropic({
|
| 12 |
model: "claude-sonnet-4-6",
|
| 13 |
});
|
src/agents/auditor/prompts.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
export const GATHER_CONTEXT_PROMPT = `You are a smart contract security expert. You will receive documentation, a structural analysis, and the full source of all in-scope Solidity contracts. Produce a thorough protocol context that will guide vulnerability discovery.
|
| 2 |
|
| 3 |
Structure your output in the following sections:
|
|
@@ -30,25 +31,20 @@ For each vulnerability provide ALL of the following fields:
|
|
| 30 |
- **description**: Explain the EXPECTED behavior vs the OBSERVED (vulnerable) behavior in 2β4 sentences.
|
| 31 |
- **recommendation**: Specific, actionable remediation (e.g., "Apply checks-effects-interactions pattern", "Add onlyOwner modifier").
|
| 32 |
- **severity**: One of "high" (direct fund loss or contract takeover), "medium" (indirect or conditional risk), "low" (best-practice issue, no immediate financial risk).
|
| 33 |
-
- **confidence**: Integer 0β100 reflecting your confidence this is a real, exploitable vulnerability.
|
| 34 |
- **codeSnippet**: The exact vulnerable code block as it appears in the source.
|
| 35 |
-
- **location**: Line range as "start-end" (e.g., "42-58"). Use "unknown" if lines are not determinable.
|
| 36 |
-
- **path**: File path of the vulnerable contract. Use the contract name if a single inline source is provided.
|
| 37 |
-
- **exploitablePaths**: Array of one or more concrete exploit traces. Each trace must describe the attacker steps with realistic inputs/values (e.g., "1. Attacker calls deposit(100 ETH) 2. Attacker contract fallback re-enters withdraw() before balance update 3. Attacker drains 100 ETH twice").
|
| 38 |
|
| 39 |
Vulnerability categories to systematically check: reentrancy (single- and cross-function), access control, integer overflow/underflow, oracle manipulation, flash loan attacks, front-running/MEV, signature replay, storage collisions, uninitialized proxies, unsafe delegatecall, gas griefing, denial of service, precision loss, and logic/business rule violations.
|
| 40 |
|
| 41 |
-
If
|
| 42 |
|
| 43 |
-
export const
|
| 44 |
|
| 45 |
For each finding provide ALL of the following fields:
|
| 46 |
|
| 47 |
-
- **findingTitle**: Must match exactly the title of the finding you are reviewing.
|
| 48 |
- **review**: Detailed analysis (3β6 sentences) explaining why the vulnerability is or isn't real. Reference specific code, protocol invariants, preconditions, and mitigating controls.
|
| 49 |
- **isFalsePositive**: true if the finding is NOT exploitable in practice; false if it IS a real vulnerability.
|
| 50 |
- **confidence**: Integer 0β100 reflecting your confidence in this verdict.
|
| 51 |
-
- **exploitablePaths**: If a true positive, provide concrete paths confirming exploitability with real values. If a false positive, provide the reasoning that blocks the exploit.
|
| 52 |
|
| 53 |
A finding is a false positive if and only if: the exploit path is unreachable given access controls or preconditions, it is already fully mitigated by the code, it requires impossible or economically infeasible conditions, or it is explicitly documented as by-design behavior in the protocol assumptions.
|
| 54 |
|
|
|
|
| 1 |
+
// TODO: Compact the entire full context (parts: docs + static analysis) and infer the sections conciselly
|
| 2 |
export const GATHER_CONTEXT_PROMPT = `You are a smart contract security expert. You will receive documentation, a structural analysis, and the full source of all in-scope Solidity contracts. Produce a thorough protocol context that will guide vulnerability discovery.
|
| 3 |
|
| 4 |
Structure your output in the following sections:
|
|
|
|
| 31 |
- **description**: Explain the EXPECTED behavior vs the OBSERVED (vulnerable) behavior in 2β4 sentences.
|
| 32 |
- **recommendation**: Specific, actionable remediation (e.g., "Apply checks-effects-interactions pattern", "Add onlyOwner modifier").
|
| 33 |
- **severity**: One of "high" (direct fund loss or contract takeover), "medium" (indirect or conditional risk), "low" (best-practice issue, no immediate financial risk).
|
|
|
|
| 34 |
- **codeSnippet**: The exact vulnerable code block as it appears in the source.
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
Vulnerability categories to systematically check: reentrancy (single- and cross-function), access control, integer overflow/underflow, oracle manipulation, flash loan attacks, front-running/MEV, signature replay, storage collisions, uninitialized proxies, unsafe delegatecall, gas griefing, denial of service, precision loss, and logic/business rule violations.
|
| 37 |
|
| 38 |
+
If judge feedback is provided from a previous iteration, remove confirmed false positives from your list and refine or expand remaining findings based on the critique.`;
|
| 39 |
|
| 40 |
+
export const JUDGE_FINDINGS_PROMPT = `You are a rigorous smart contract security reviewer. Evaluate each candidate vulnerability submitted by the auditor and determine whether it is a true positive or a false positive.
|
| 41 |
|
| 42 |
For each finding provide ALL of the following fields:
|
| 43 |
|
|
|
|
| 44 |
- **review**: Detailed analysis (3β6 sentences) explaining why the vulnerability is or isn't real. Reference specific code, protocol invariants, preconditions, and mitigating controls.
|
| 45 |
- **isFalsePositive**: true if the finding is NOT exploitable in practice; false if it IS a real vulnerability.
|
| 46 |
- **confidence**: Integer 0β100 reflecting your confidence in this verdict.
|
| 47 |
+
- **exploitablePaths**: If a true positive, provide concrete paths confirming exploitability with real values. Each trace must describe the attacker steps with realistic inputs/values (e.g., "1. Attacker calls deposit(100 ETH) 2. Attacker contract fallback re-enters withdraw() before balance update 3. Attacker drains 100 ETH twice"). If a false positive, provide the reasoning that blocks the exploit.
|
| 48 |
|
| 49 |
A finding is a false positive if and only if: the exploit path is unreachable given access controls or preconditions, it is already fully mitigated by the code, it requires impossible or economically infeasible conditions, or it is explicitly documented as by-design behavior in the protocol assumptions.
|
| 50 |
|
src/agents/auditor/state.ts
CHANGED
|
@@ -1,20 +1,25 @@
|
|
| 1 |
import { StateSchema } from "@langchain/langgraph";
|
| 2 |
import { z } from "zod";
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
export const FindingSchema = z.object({
|
| 5 |
title: z.string(),
|
| 6 |
description: z.string(),
|
| 7 |
recommendation: z.string(),
|
| 8 |
severity: z.enum(["high", "medium", "low"]),
|
| 9 |
-
confidence: z.number(),
|
| 10 |
codeSnippet: z.string(),
|
| 11 |
location: z.string(),
|
| 12 |
path: z.string(),
|
| 13 |
-
exploitablePaths: z.array(z.string()),
|
| 14 |
});
|
| 15 |
|
| 16 |
-
export const
|
| 17 |
-
findingTitle: z.string(),
|
| 18 |
review: z.string(),
|
| 19 |
isFalsePositive: z.boolean(),
|
| 20 |
confidence: z.number(),
|
|
@@ -28,7 +33,7 @@ export const AuditorState = new StateSchema({
|
|
| 28 |
docs: z.array(z.string()).default([]),
|
| 29 |
repoContext: z.string().default(""),
|
| 30 |
candidateFindings: z.array(FindingSchema).default([]),
|
| 31 |
-
|
| 32 |
findings: z.array(FindingSchema).default([]),
|
| 33 |
reflectionCount: z.number().default(0),
|
| 34 |
});
|
|
|
|
| 1 |
import { StateSchema } from "@langchain/langgraph";
|
| 2 |
import { z } from "zod";
|
| 3 |
|
| 4 |
+
export const PartialFindingSchema = z.object({
|
| 5 |
+
title: z.string(),
|
| 6 |
+
description: z.string(),
|
| 7 |
+
recommendation: z.string(),
|
| 8 |
+
severity: z.enum(["high", "medium", "low"]),
|
| 9 |
+
codeSnippet: z.string(),
|
| 10 |
+
});
|
| 11 |
+
|
| 12 |
export const FindingSchema = z.object({
|
| 13 |
title: z.string(),
|
| 14 |
description: z.string(),
|
| 15 |
recommendation: z.string(),
|
| 16 |
severity: z.enum(["high", "medium", "low"]),
|
|
|
|
| 17 |
codeSnippet: z.string(),
|
| 18 |
location: z.string(),
|
| 19 |
path: z.string(),
|
|
|
|
| 20 |
});
|
| 21 |
|
| 22 |
+
export const ReviewSchema = z.object({
|
|
|
|
| 23 |
review: z.string(),
|
| 24 |
isFalsePositive: z.boolean(),
|
| 25 |
confidence: z.number(),
|
|
|
|
| 33 |
docs: z.array(z.string()).default([]),
|
| 34 |
repoContext: z.string().default(""),
|
| 35 |
candidateFindings: z.array(FindingSchema).default([]),
|
| 36 |
+
judgeReviews: z.array(ReviewSchema).default([]),
|
| 37 |
findings: z.array(FindingSchema).default([]),
|
| 38 |
reflectionCount: z.number().default(0),
|
| 39 |
});
|
src/logger.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fs from "node:fs";
|
| 2 |
+
import path from "node:path";
|
| 3 |
+
|
| 4 |
+
import winston from "winston";
|
| 5 |
+
|
| 6 |
+
const logsDir = path.join(process.cwd(), "logs");
|
| 7 |
+
fs.mkdirSync(logsDir, { recursive: true });
|
| 8 |
+
|
| 9 |
+
const runTimestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
| 10 |
+
|
| 11 |
+
const { combine, colorize, errors, printf, timestamp } = winston.format;
|
| 12 |
+
|
| 13 |
+
const lineFormat = printf(({ level, message, timestamp: ts, stack }) => {
|
| 14 |
+
const base = `${ts} [${level}] ${message}`;
|
| 15 |
+
return stack ? `${base}\n${stack}` : base;
|
| 16 |
+
});
|
| 17 |
+
|
| 18 |
+
export const logger = winston.createLogger({
|
| 19 |
+
level: "debug",
|
| 20 |
+
transports: [
|
| 21 |
+
new winston.transports.Console({
|
| 22 |
+
format: combine(colorize({ all: true }), timestamp({ format: "HH:mm:ss" }), errors({ stack: true }), lineFormat),
|
| 23 |
+
}),
|
| 24 |
+
new winston.transports.File({
|
| 25 |
+
filename: path.join(logsDir, `app-${runTimestamp}.log`),
|
| 26 |
+
format: combine(timestamp(), errors({ stack: true }), lineFormat),
|
| 27 |
+
}),
|
| 28 |
+
],
|
| 29 |
+
});
|