solidity-argus 0.1.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/AGENTS.md +3 -3
  2. package/README.md +229 -13
  3. package/package.json +37 -8
  4. package/skills/INVENTORY.md +88 -57
  5. package/skills/README.md +72 -6
  6. package/skills/case-studies/beanstalk-governance/SKILL.md +52 -0
  7. package/skills/case-studies/bzx-flash-loan/SKILL.md +53 -0
  8. package/skills/case-studies/cream-finance/SKILL.md +52 -0
  9. package/skills/case-studies/curve-reentrancy/SKILL.md +52 -0
  10. package/skills/case-studies/dao-hack/SKILL.md +51 -0
  11. package/skills/case-studies/euler-finance/SKILL.md +52 -0
  12. package/skills/case-studies/harvest-finance/SKILL.md +52 -0
  13. package/skills/case-studies/level-finance/SKILL.md +51 -0
  14. package/skills/case-studies/mango-markets/SKILL.md +53 -0
  15. package/skills/case-studies/nomad-bridge/SKILL.md +51 -0
  16. package/skills/case-studies/parity-multisig/SKILL.md +55 -0
  17. package/skills/case-studies/poly-network/SKILL.md +51 -0
  18. package/skills/case-studies/rari-fuse/SKILL.md +51 -0
  19. package/skills/case-studies/ronin-bridge/SKILL.md +52 -0
  20. package/skills/case-studies/wormhole-bridge/SKILL.md +51 -0
  21. package/skills/checklists/cyfrin-defi-core/SKILL.md +3 -0
  22. package/skills/manifests/cyfrin.json +16 -0
  23. package/skills/manifests/defifofum.json +25 -0
  24. package/skills/manifests/kadenzipfel.json +48 -0
  25. package/skills/manifests/scvd.json +9 -0
  26. package/skills/manifests/smartbugs.json +9 -0
  27. package/skills/manifests/solodit.json +9 -0
  28. package/skills/manifests/sunweb3sec.json +9 -0
  29. package/skills/manifests/trailofbits.json +9 -0
  30. package/skills/methodology/audit-workflow/SKILL.md +3 -0
  31. package/skills/protocol-patterns/amm-dex/SKILL.md +3 -0
  32. package/skills/references/exploit-reference/SKILL.md +3 -0
  33. package/skills/vulnerability-patterns/access-control/SKILL.md +27 -0
  34. package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +13 -1
  35. package/skills/vulnerability-patterns/assert-violation/SKILL.md +8 -1
  36. package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +12 -1
  37. package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +8 -1
  38. package/skills/vulnerability-patterns/cross-chain-bridge-vulnerabilities/SKILL.md +217 -0
  39. package/skills/vulnerability-patterns/default-visibility/SKILL.md +13 -1
  40. package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +8 -1
  41. package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +8 -1
  42. package/skills/vulnerability-patterns/dos-revert/SKILL.md +14 -1
  43. package/skills/vulnerability-patterns/erc4626-exchange-rate-manipulation/SKILL.md +64 -0
  44. package/skills/vulnerability-patterns/fee-on-transfer-tokens/SKILL.md +93 -0
  45. package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +13 -0
  46. package/skills/vulnerability-patterns/floating-pragma/SKILL.md +8 -1
  47. package/skills/vulnerability-patterns/front-running-attacks/SKILL.md +209 -0
  48. package/skills/vulnerability-patterns/gas-optimization-patterns/SKILL.md +203 -0
  49. package/skills/vulnerability-patterns/governance-attacks/SKILL.md +208 -0
  50. package/skills/vulnerability-patterns/hash-collision/SKILL.md +8 -1
  51. package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +12 -1
  52. package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +8 -1
  53. package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +8 -1
  54. package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +12 -1
  55. package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +7 -1
  56. package/skills/vulnerability-patterns/logic-errors/SKILL.md +10 -0
  57. package/skills/vulnerability-patterns/missing-parameter-bounds/SKILL.md +44 -0
  58. package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +17 -1
  59. package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +12 -1
  60. package/skills/vulnerability-patterns/off-by-one/SKILL.md +7 -1
  61. package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +22 -0
  62. package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +8 -1
  63. package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +11 -1
  64. package/skills/vulnerability-patterns/proxy-vulnerabilities/SKILL.md +209 -0
  65. package/skills/vulnerability-patterns/reentrancy/SKILL.md +22 -0
  66. package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +8 -1
  67. package/skills/vulnerability-patterns/share-accounting-desynchronization/SKILL.md +44 -0
  68. package/skills/vulnerability-patterns/signature-malleability/SKILL.md +11 -1
  69. package/skills/vulnerability-patterns/stateful-parameter-update-drift/SKILL.md +44 -0
  70. package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +12 -1
  71. package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +13 -1
  72. package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +8 -1
  73. package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +8 -1
  74. package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +8 -1
  75. package/skills/vulnerability-patterns/unsafe-erc20-transfers/SKILL.md +132 -0
  76. package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +12 -1
  77. package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +12 -1
  78. package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +11 -1
  79. package/skills/vulnerability-patterns/unused-variables/SKILL.md +8 -1
  80. package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +8 -1
  81. package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +8 -1
  82. package/skills/vulnerability-patterns/weird-tokens/SKILL.md +10 -0
  83. package/skills/vulnerability-patterns/zero-address-misconfiguration/SKILL.md +48 -0
  84. package/src/agents/argus-prompt.ts +27 -10
  85. package/src/agents/pythia-prompt.ts +7 -8
  86. package/src/agents/scribe-prompt.ts +10 -5
  87. package/src/agents/sentinel-prompt.ts +36 -7
  88. package/src/cli/cli-output.ts +16 -0
  89. package/src/cli/cli-program.ts +29 -22
  90. package/src/cli/commands/check-skills.ts +135 -0
  91. package/src/cli/commands/doctor.ts +303 -23
  92. package/src/cli/commands/init.ts +8 -6
  93. package/src/cli/commands/install.ts +10 -8
  94. package/src/cli/commands/lint-skills.ts +118 -0
  95. package/src/cli/index.ts +5 -5
  96. package/src/cli/tui-prompts.ts +4 -2
  97. package/src/cli/types.ts +3 -3
  98. package/src/config/index.ts +1 -1
  99. package/src/config/loader.ts +4 -6
  100. package/src/config/schema.ts +6 -5
  101. package/src/config/types.ts +2 -2
  102. package/src/constants/defaults.ts +2 -0
  103. package/src/create-hooks.ts +225 -29
  104. package/src/create-managers.ts +10 -8
  105. package/src/create-tools.ts +14 -8
  106. package/src/features/background-agent/background-manager.ts +93 -87
  107. package/src/features/background-agent/index.ts +1 -1
  108. package/src/features/context-monitor/context-monitor.ts +3 -3
  109. package/src/features/context-monitor/index.ts +2 -2
  110. package/src/features/error-recovery/session-recovery.ts +2 -4
  111. package/src/features/error-recovery/tool-error-recovery.ts +79 -19
  112. package/src/features/index.ts +5 -5
  113. package/src/features/persistent-state/audit-state-manager.ts +158 -52
  114. package/src/features/persistent-state/global-run-index.ts +38 -0
  115. package/src/features/persistent-state/index.ts +1 -1
  116. package/src/features/persistent-state/run-journal.ts +86 -0
  117. package/src/hooks/agent-tracker.ts +53 -0
  118. package/src/hooks/compaction-hook.ts +46 -37
  119. package/src/hooks/config-handler.ts +31 -11
  120. package/src/hooks/context-budget.ts +42 -0
  121. package/src/hooks/event-hook.ts +48 -23
  122. package/src/hooks/hook-system.ts +4 -4
  123. package/src/hooks/index.ts +5 -5
  124. package/src/hooks/knowledge-sync-hook.ts +19 -21
  125. package/src/hooks/recon-context-builder.ts +66 -0
  126. package/src/hooks/safe-create-hook.ts +9 -11
  127. package/src/hooks/system-prompt-hook.ts +128 -0
  128. package/src/hooks/tool-tracking-hook.ts +162 -29
  129. package/src/hooks/types.ts +2 -1
  130. package/src/index.ts +23 -13
  131. package/src/knowledge/retry.ts +53 -0
  132. package/src/knowledge/scvd-client.ts +103 -83
  133. package/src/knowledge/scvd-errors.ts +89 -0
  134. package/src/knowledge/scvd-index.ts +110 -62
  135. package/src/knowledge/scvd-sync.ts +223 -47
  136. package/src/knowledge/source-manifest.ts +102 -0
  137. package/src/managers/index.ts +1 -1
  138. package/src/managers/types.ts +19 -14
  139. package/src/plugin-interface.ts +19 -8
  140. package/src/shared/binary-utils.ts +44 -34
  141. package/src/shared/deep-merge.ts +55 -36
  142. package/src/shared/file-utils.ts +21 -19
  143. package/src/shared/index.ts +11 -5
  144. package/src/shared/jsonc-parser.ts +123 -28
  145. package/src/shared/logger.ts +91 -17
  146. package/src/shared/project-utils.ts +30 -0
  147. package/src/skills/analysis/cluster.ts +414 -0
  148. package/src/skills/analysis/gates.ts +227 -0
  149. package/src/skills/analysis/index.ts +33 -0
  150. package/src/skills/analysis/normalize.ts +217 -0
  151. package/src/skills/analysis/similarity.ts +224 -0
  152. package/src/skills/argus-skill-resolver.ts +237 -0
  153. package/src/skills/skill-schema.ts +99 -0
  154. package/src/solodit-lifecycle.ts +202 -0
  155. package/src/state/audit-state.ts +10 -8
  156. package/src/state/finding-store.ts +68 -55
  157. package/src/state/types.ts +96 -44
  158. package/src/tools/argus-skill-load-tool.ts +78 -0
  159. package/src/tools/contract-analyzer-tool.ts +60 -77
  160. package/src/tools/forge-coverage-tool.ts +226 -0
  161. package/src/tools/forge-fuzz-tool.ts +127 -127
  162. package/src/tools/forge-test-tool.ts +153 -157
  163. package/src/tools/gas-analysis-tool.ts +264 -0
  164. package/src/tools/pattern-checker-tool.ts +206 -167
  165. package/src/tools/pattern-loader.ts +77 -0
  166. package/src/tools/pattern-schema.ts +51 -0
  167. package/src/tools/proxy-detection-tool.ts +224 -0
  168. package/src/tools/report-generator-tool.ts +333 -142
  169. package/src/tools/slither-tool.ts +300 -210
  170. package/src/tools/solodit-search-tool.ts +255 -80
  171. package/src/tools/sync-knowledge-tool.ts +7 -11
  172. package/src/utils/audit-artifact-detector.ts +118 -0
  173. package/src/utils/dependency-scanner.ts +93 -0
  174. package/src/utils/project-detector.ts +175 -86
  175. package/src/utils/solidity-parser.ts +112 -67
  176. package/src/utils/solodit-health.ts +29 -0
  177. package/src/hooks/event-hook-v2.ts +0 -99
  178. package/src/state/plugin-state.ts +0 -14
@@ -0,0 +1,264 @@
1
+ import { type ToolContext, tool } from "@opencode-ai/plugin"
2
+ import { resolveProjectDir } from "../shared/project-utils"
3
+
4
+ type GasAnalysisArgs = {
5
+ target?: string
6
+ threshold?: number
7
+ }
8
+
9
+ type NormalizedGasAnalysisArgs = {
10
+ target: string
11
+ threshold: number
12
+ }
13
+
14
+ type ContractFunctionGas = {
15
+ name: string
16
+ min: number
17
+ avg: number
18
+ median: number
19
+ max: number
20
+ calls: number
21
+ }
22
+
23
+ type ContractGasReport = {
24
+ name: string
25
+ deploymentCost: number
26
+ deploymentSize: number
27
+ functions: ContractFunctionGas[]
28
+ }
29
+
30
+ type GasHotspot = {
31
+ contract: string
32
+ function: string
33
+ avgGas: number
34
+ }
35
+
36
+ type GasAnalysisResult = {
37
+ success: boolean
38
+ contracts: ContractGasReport[]
39
+ hotspots: GasHotspot[]
40
+ executionTime: number
41
+ error?: string
42
+ }
43
+
44
+ export type ForgeCommandRunner = (
45
+ command: string[],
46
+ signal: AbortSignal,
47
+ cwd: string,
48
+ ) => Promise<{ stdout: string; stderr: string; exitCode: number }>
49
+
50
+ function toNumber(value: string): number {
51
+ const normalized = value.replace(/[,_\s]/g, "").trim()
52
+ if (normalized.length === 0) {
53
+ return 0
54
+ }
55
+ const parsed = Number.parseInt(normalized, 10)
56
+ return Number.isFinite(parsed) ? parsed : 0
57
+ }
58
+
59
+ function parseCells(line: string): string[] {
60
+ return line
61
+ .split(/[│┆]/)
62
+ .map((cell) => cell.trim())
63
+ .filter((cell) => cell.length > 0)
64
+ }
65
+
66
+ function normalizeContractName(raw: string): string {
67
+ return raw.replace(/\s+(contract|library|interface)$/i, "").trim()
68
+ }
69
+
70
+ function parseGasReport(stdout: string): ContractGasReport[] {
71
+ const lines = stdout.split(/\r?\n/)
72
+ const contracts: ContractGasReport[] = []
73
+ let currentContract: ContractGasReport | undefined
74
+ let expectingDeploymentValues = false
75
+ let inFunctionSection = false
76
+
77
+ for (const line of lines) {
78
+ if (!line.includes("│") && !line.includes("┆")) {
79
+ continue
80
+ }
81
+
82
+ const cells = parseCells(line)
83
+ if (cells.length === 0) {
84
+ continue
85
+ }
86
+
87
+ const first = cells[0] ?? ""
88
+ if (first.length === 0) {
89
+ continue
90
+ }
91
+
92
+ const isContractLine = /\s(contract|library|interface)$/i.test(first)
93
+ if (isContractLine) {
94
+ currentContract = {
95
+ name: normalizeContractName(first),
96
+ deploymentCost: 0,
97
+ deploymentSize: 0,
98
+ functions: [],
99
+ }
100
+ contracts.push(currentContract)
101
+ expectingDeploymentValues = false
102
+ inFunctionSection = false
103
+ continue
104
+ }
105
+
106
+ if (!currentContract) {
107
+ continue
108
+ }
109
+
110
+ if (first === "Deployment Cost") {
111
+ expectingDeploymentValues = true
112
+ inFunctionSection = false
113
+ continue
114
+ }
115
+
116
+ if (expectingDeploymentValues && /^\d[\d,\s_]*$/.test(first)) {
117
+ currentContract.deploymentCost = toNumber(first)
118
+ currentContract.deploymentSize = toNumber(cells[1] ?? "0")
119
+ expectingDeploymentValues = false
120
+ continue
121
+ }
122
+
123
+ if (first === "Function Name") {
124
+ inFunctionSection = true
125
+ continue
126
+ }
127
+
128
+ if (!inFunctionSection || cells.length < 6) {
129
+ continue
130
+ }
131
+
132
+ if (first === "min" || first === "avg" || first === "median" || first === "max") {
133
+ continue
134
+ }
135
+
136
+ const [name, min, avg, median, max, calls] = cells
137
+ if (!/^\d/.test(min ?? "") || !/^\d/.test(avg ?? "")) {
138
+ continue
139
+ }
140
+
141
+ currentContract.functions.push({
142
+ name: name ?? "unknown",
143
+ min: toNumber(min ?? "0"),
144
+ avg: toNumber(avg ?? "0"),
145
+ median: toNumber(median ?? "0"),
146
+ max: toNumber(max ?? "0"),
147
+ calls: toNumber(calls ?? "0"),
148
+ })
149
+ }
150
+
151
+ return contracts
152
+ }
153
+
154
+ function normalizeArgs(args: GasAnalysisArgs, context: ToolContext): NormalizedGasAnalysisArgs {
155
+ return {
156
+ target: args.target ?? resolveProjectDir(context),
157
+ threshold:
158
+ typeof args.threshold === "number" && Number.isFinite(args.threshold)
159
+ ? args.threshold
160
+ : 100000,
161
+ }
162
+ }
163
+
164
+ const runForgeCommand: ForgeCommandRunner = async (command, signal, cwd) => {
165
+ const child = Bun.spawn(command, {
166
+ cwd,
167
+ stdout: "pipe",
168
+ stderr: "pipe",
169
+ signal,
170
+ })
171
+
172
+ const [exitCode, stdout, stderr] = await Promise.all([
173
+ child.exited,
174
+ new Response(child.stdout).text(),
175
+ new Response(child.stderr).text(),
176
+ ])
177
+
178
+ return {
179
+ stdout,
180
+ stderr,
181
+ exitCode,
182
+ }
183
+ }
184
+
185
+ export async function executeGasAnalysis(
186
+ args: GasAnalysisArgs,
187
+ context: ToolContext,
188
+ runCommand: ForgeCommandRunner = runForgeCommand,
189
+ ): Promise<GasAnalysisResult> {
190
+ const startedAt = Date.now()
191
+ const normalizedArgs = normalizeArgs(args, context)
192
+ context.metadata({ title: `Run forge gas report: ${normalizedArgs.target}` })
193
+
194
+ const fail = (error: string): GasAnalysisResult => ({
195
+ success: false,
196
+ contracts: [],
197
+ hotspots: [],
198
+ executionTime: Date.now() - startedAt,
199
+ error,
200
+ })
201
+
202
+ try {
203
+ const forgeResult = await runCommand(
204
+ ["forge", "test", "--gas-report"],
205
+ context.abort,
206
+ normalizedArgs.target,
207
+ )
208
+
209
+ const contracts = parseGasReport(forgeResult.stdout)
210
+ const hotspots = contracts
211
+ .flatMap((contract) =>
212
+ contract.functions.map((fn) => ({
213
+ contract: contract.name,
214
+ function: fn.name,
215
+ avgGas: fn.avg,
216
+ })),
217
+ )
218
+ .filter((hotspot) => hotspot.avgGas > normalizedArgs.threshold)
219
+ .sort((a, b) => b.avgGas - a.avgGas)
220
+
221
+ const success = forgeResult.exitCode === 0
222
+ const output: GasAnalysisResult = {
223
+ success,
224
+ contracts,
225
+ hotspots,
226
+ executionTime: Date.now() - startedAt,
227
+ }
228
+
229
+ if (!success) {
230
+ output.error =
231
+ forgeResult.stderr.trim() ||
232
+ `forge test --gas-report exited with code ${forgeResult.exitCode}`
233
+ }
234
+
235
+ return output
236
+ } catch (error) {
237
+ if (context.abort.aborted || (error instanceof DOMException && error.name === "AbortError")) {
238
+ return fail("forge gas analysis aborted")
239
+ }
240
+
241
+ const maybeError = error as Error & { code?: string }
242
+ if (maybeError.code === "ENOENT") {
243
+ return fail("Foundry not found. Install: curl -L https://foundry.paradigm.xyz | bash")
244
+ }
245
+ if (maybeError.code === "ETIMEDOUT" || maybeError.message.toLowerCase().includes("timed out")) {
246
+ return fail("forge gas analysis timed out")
247
+ }
248
+
249
+ return fail(maybeError.message || "forge gas analysis failed")
250
+ }
251
+ }
252
+
253
+ export const gasAnalysisTool = tool({
254
+ description:
255
+ "Run forge test --gas-report, parse per-function gas metrics, and identify hotspots.",
256
+ args: {
257
+ target: tool.schema.string().optional(),
258
+ threshold: tool.schema.number().default(100000),
259
+ },
260
+ async execute(args, context) {
261
+ const result = await executeGasAnalysis(args, context)
262
+ return JSON.stringify(result)
263
+ },
264
+ })