solidity-argus 0.2.0 → 0.3.2
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/AGENTS.md +3 -3
- package/README.md +93 -37
- package/package.json +34 -7
- package/skills/INVENTORY.md +88 -57
- package/skills/README.md +26 -23
- package/skills/case-studies/beanstalk-governance/SKILL.md +52 -0
- package/skills/case-studies/bzx-flash-loan/SKILL.md +53 -0
- package/skills/case-studies/cream-finance/SKILL.md +52 -0
- package/skills/case-studies/curve-reentrancy/SKILL.md +52 -0
- package/skills/case-studies/dao-hack/SKILL.md +51 -0
- package/skills/case-studies/euler-finance/SKILL.md +52 -0
- package/skills/case-studies/harvest-finance/SKILL.md +52 -0
- package/skills/case-studies/level-finance/SKILL.md +51 -0
- package/skills/case-studies/mango-markets/SKILL.md +53 -0
- package/skills/case-studies/nomad-bridge/SKILL.md +51 -0
- package/skills/case-studies/parity-multisig/SKILL.md +55 -0
- package/skills/case-studies/poly-network/SKILL.md +51 -0
- package/skills/case-studies/rari-fuse/SKILL.md +51 -0
- package/skills/case-studies/ronin-bridge/SKILL.md +52 -0
- package/skills/case-studies/wormhole-bridge/SKILL.md +51 -0
- package/skills/manifests/smartbugs.json +1 -3
- package/skills/manifests/sunweb3sec.json +1 -3
- package/skills/vulnerability-patterns/access-control/SKILL.md +14 -0
- package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +13 -1
- package/skills/vulnerability-patterns/assert-violation/SKILL.md +8 -1
- package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +12 -1
- package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +2 -1
- package/skills/vulnerability-patterns/cross-chain-bridge-vulnerabilities/SKILL.md +217 -0
- package/skills/vulnerability-patterns/default-visibility/SKILL.md +13 -1
- package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +2 -1
- package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +8 -1
- package/skills/vulnerability-patterns/dos-revert/SKILL.md +1 -0
- package/skills/vulnerability-patterns/erc4626-exchange-rate-manipulation/SKILL.md +64 -0
- package/skills/vulnerability-patterns/fee-on-transfer-tokens/SKILL.md +93 -0
- package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +1 -0
- package/skills/vulnerability-patterns/floating-pragma/SKILL.md +8 -1
- package/skills/vulnerability-patterns/front-running-attacks/SKILL.md +209 -0
- package/skills/vulnerability-patterns/gas-optimization-patterns/SKILL.md +203 -0
- package/skills/vulnerability-patterns/governance-attacks/SKILL.md +208 -0
- package/skills/vulnerability-patterns/hash-collision/SKILL.md +8 -1
- package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +12 -1
- package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +8 -1
- package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +8 -1
- package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +12 -1
- package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +7 -1
- package/skills/vulnerability-patterns/logic-errors/SKILL.md +10 -0
- package/skills/vulnerability-patterns/missing-parameter-bounds/SKILL.md +44 -0
- package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +17 -1
- package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +12 -1
- package/skills/vulnerability-patterns/off-by-one/SKILL.md +7 -1
- package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +9 -0
- package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +8 -1
- package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +1 -0
- package/skills/vulnerability-patterns/proxy-vulnerabilities/SKILL.md +209 -0
- package/skills/vulnerability-patterns/reentrancy/SKILL.md +9 -0
- package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +8 -1
- package/skills/vulnerability-patterns/share-accounting-desynchronization/SKILL.md +44 -0
- package/skills/vulnerability-patterns/signature-malleability/SKILL.md +2 -1
- package/skills/vulnerability-patterns/stateful-parameter-update-drift/SKILL.md +44 -0
- package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +2 -1
- package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +8 -1
- package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +8 -1
- package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +8 -1
- package/skills/vulnerability-patterns/unsafe-erc20-transfers/SKILL.md +132 -0
- package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +11 -1
- package/skills/vulnerability-patterns/unused-variables/SKILL.md +8 -1
- package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +8 -1
- package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +8 -1
- package/skills/vulnerability-patterns/weird-tokens/SKILL.md +10 -0
- package/skills/vulnerability-patterns/zero-address-misconfiguration/SKILL.md +48 -0
- package/src/agents/argus-prompt.ts +34 -7
- package/src/agents/pythia-prompt.ts +13 -4
- package/src/agents/scribe-prompt.ts +20 -2
- package/src/agents/sentinel-prompt.ts +45 -5
- package/src/cli/cli-program.ts +29 -26
- package/src/cli/commands/check-skills.ts +135 -0
- package/src/cli/commands/doctor.ts +48 -26
- package/src/cli/commands/init.ts +5 -3
- package/src/cli/commands/install.ts +7 -5
- package/src/cli/commands/lint-skills.ts +16 -12
- package/src/cli/index.ts +5 -5
- package/src/cli/types.ts +3 -3
- package/src/config/index.ts +1 -1
- package/src/config/loader.ts +4 -6
- package/src/config/schema.ts +6 -5
- package/src/config/types.ts +2 -2
- package/src/constants/defaults.ts +2 -0
- package/src/create-hooks.ts +145 -34
- package/src/create-managers.ts +10 -8
- package/src/create-tools.ts +13 -9
- package/src/features/background-agent/background-manager.ts +93 -87
- package/src/features/background-agent/index.ts +1 -1
- package/src/features/context-monitor/context-monitor.ts +3 -3
- package/src/features/context-monitor/index.ts +2 -2
- package/src/features/error-recovery/session-recovery.ts +2 -4
- package/src/features/error-recovery/tool-error-recovery.ts +12 -7
- package/src/features/index.ts +5 -5
- package/src/features/persistent-state/audit-state-manager.ts +143 -60
- package/src/features/persistent-state/global-run-index.ts +38 -0
- package/src/features/persistent-state/index.ts +1 -1
- package/src/features/persistent-state/run-journal.ts +86 -0
- package/src/hooks/config-handler.ts +28 -11
- package/src/hooks/context-budget.ts +2 -5
- package/src/hooks/event-hook.ts +47 -23
- package/src/hooks/hook-system.ts +4 -4
- package/src/hooks/index.ts +5 -5
- package/src/hooks/knowledge-sync-hook.ts +18 -21
- package/src/hooks/recon-context-builder.ts +2 -2
- package/src/hooks/safe-create-hook.ts +6 -7
- package/src/hooks/system-prompt-hook.ts +18 -1
- package/src/hooks/tool-tracking-hook.ts +110 -51
- package/src/hooks/types.ts +2 -1
- package/src/index.ts +24 -37
- package/src/knowledge/retry.ts +22 -22
- package/src/knowledge/scvd-client.ts +88 -95
- package/src/knowledge/scvd-errors.ts +35 -35
- package/src/knowledge/scvd-index.ts +78 -80
- package/src/knowledge/scvd-sync.ts +106 -101
- package/src/managers/index.ts +1 -1
- package/src/managers/types.ts +19 -14
- package/src/plugin-interface.ts +7 -9
- package/src/shared/binary-utils.ts +44 -35
- package/src/shared/deep-merge.ts +55 -36
- package/src/shared/file-utils.ts +21 -19
- package/src/shared/index.ts +11 -5
- package/src/shared/jsonc-parser.ts +123 -28
- package/src/shared/logger.ts +16 -3
- package/src/shared/project-utils.ts +30 -0
- package/src/skills/analysis/cluster.ts +414 -0
- package/src/skills/analysis/gates.ts +227 -0
- package/src/skills/analysis/index.ts +33 -0
- package/src/skills/analysis/normalize.ts +217 -0
- package/src/skills/analysis/similarity.ts +224 -0
- package/src/skills/argus-skill-resolver.ts +17 -6
- package/src/skills/skill-schema.ts +11 -10
- package/src/solodit-lifecycle.ts +203 -0
- package/src/state/audit-state.ts +8 -8
- package/src/state/finding-store.ts +68 -55
- package/src/state/types.ts +88 -67
- package/src/tools/argus-skill-load-tool.ts +12 -7
- package/src/tools/contract-analyzer-tool.ts +142 -77
- package/src/tools/forge-coverage-tool.ts +226 -0
- package/src/tools/forge-fuzz-tool.ts +127 -127
- package/src/tools/forge-test-tool.ts +201 -158
- package/src/tools/gas-analysis-tool.ts +264 -0
- package/src/tools/pattern-checker-tool.ts +203 -191
- package/src/tools/pattern-loader.ts +5 -111
- package/src/tools/pattern-schema.ts +3 -0
- package/src/tools/proxy-detection-tool.ts +224 -0
- package/src/tools/report-generator-tool.ts +305 -206
- package/src/tools/slither-tool.ts +266 -218
- package/src/tools/solodit-search-tool.ts +235 -119
- package/src/tools/sync-knowledge-tool.ts +7 -11
- package/src/utils/audit-artifact-detector.ts +28 -29
- package/src/utils/dependency-scanner.ts +37 -37
- package/src/utils/project-detector.ts +111 -124
- package/src/utils/solidity-parser.ts +175 -75
- package/skills/patterns/access-control.yaml +0 -31
- package/skills/patterns/erc4626.yaml +0 -29
- package/skills/patterns/flash-loan.yaml +0 -20
- package/skills/patterns/oracle.yaml +0 -30
- package/skills/patterns/proxy.yaml +0 -30
- package/skills/patterns/reentrancy.yaml +0 -30
- package/skills/patterns/signature.yaml +0 -31
- package/src/hooks/event-hook-v2.ts +0 -99
- package/src/state/plugin-state.ts +0 -14
|
@@ -1,133 +1,134 @@
|
|
|
1
|
-
import { createHash } from "crypto"
|
|
2
|
-
import { mkdtempSync,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import type { Finding, FindingSeverity } from "../state/types"
|
|
8
|
-
|
|
1
|
+
import { createHash } from "node:crypto"
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { tmpdir } from "node:os"
|
|
4
|
+
import { dirname, isAbsolute, join, resolve } from "node:path"
|
|
5
|
+
import { type ToolContext, tool } from "@opencode-ai/plugin"
|
|
6
|
+
import { createLogger } from "../shared/logger"
|
|
7
|
+
import type { Finding, FindingSeverity } from "../state/types"
|
|
8
|
+
|
|
9
|
+
const logger = createLogger()
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
extractContractNames as extractContractNamesShared,
|
|
13
|
+
hasBinary as hasBinaryShared,
|
|
14
|
+
parseSolcVersion as parseSolcVersionShared,
|
|
15
|
+
} from "../shared/binary-utils"
|
|
16
|
+
import { resolveProjectDir } from "../shared/project-utils"
|
|
9
17
|
|
|
10
18
|
type SlitherArgs = {
|
|
11
|
-
target: string
|
|
12
|
-
detectors?: string[]
|
|
13
|
-
exclude?: string[]
|
|
14
|
-
solc_version?: string
|
|
15
|
-
via_ir?: boolean
|
|
16
|
-
}
|
|
19
|
+
target: string
|
|
20
|
+
detectors?: string[]
|
|
21
|
+
exclude?: string[]
|
|
22
|
+
solc_version?: string
|
|
23
|
+
via_ir?: boolean
|
|
24
|
+
}
|
|
17
25
|
|
|
18
26
|
type SlitherDetector = {
|
|
19
|
-
check?: string
|
|
20
|
-
impact?: string
|
|
21
|
-
confidence?: string
|
|
22
|
-
description?: string
|
|
27
|
+
check?: string
|
|
28
|
+
impact?: string
|
|
29
|
+
confidence?: string
|
|
30
|
+
description?: string
|
|
23
31
|
elements?: Array<{
|
|
24
32
|
source_mapping?: {
|
|
25
|
-
filename_relative?: string
|
|
26
|
-
lines?: number[]
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
33
|
+
filename_relative?: string
|
|
34
|
+
lines?: number[]
|
|
35
|
+
}
|
|
36
|
+
}>
|
|
37
|
+
}
|
|
30
38
|
|
|
31
39
|
type SlitherPayload = {
|
|
32
|
-
success?: boolean
|
|
33
|
-
error?: string | null
|
|
40
|
+
success?: boolean
|
|
41
|
+
error?: string | null
|
|
34
42
|
results?: {
|
|
35
|
-
detectors?: SlitherDetector[]
|
|
36
|
-
}
|
|
37
|
-
}
|
|
43
|
+
detectors?: SlitherDetector[]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
38
46
|
|
|
39
47
|
export type SlitherRunResult = {
|
|
40
|
-
stdout: string
|
|
41
|
-
stderr: string
|
|
42
|
-
exitCode: number
|
|
43
|
-
}
|
|
48
|
+
stdout: string
|
|
49
|
+
stderr: string
|
|
50
|
+
exitCode: number
|
|
51
|
+
}
|
|
44
52
|
|
|
45
53
|
export type RunSlitherCommand = (
|
|
46
54
|
command: string[],
|
|
47
55
|
signal: AbortSignal,
|
|
48
|
-
cwd: string
|
|
49
|
-
) => Promise<SlitherRunResult
|
|
56
|
+
cwd: string,
|
|
57
|
+
) => Promise<SlitherRunResult>
|
|
50
58
|
|
|
51
59
|
export type SlitherAnalyzeResult = {
|
|
52
|
-
success: boolean
|
|
53
|
-
findingsCount: number
|
|
54
|
-
findings: Finding[]
|
|
55
|
-
executionTime: number
|
|
56
|
-
errors: string[]
|
|
57
|
-
error?: string
|
|
58
|
-
}
|
|
60
|
+
success: boolean
|
|
61
|
+
findingsCount: number
|
|
62
|
+
findings: Finding[]
|
|
63
|
+
executionTime: number
|
|
64
|
+
errors: string[]
|
|
65
|
+
error?: string
|
|
66
|
+
}
|
|
59
67
|
|
|
60
68
|
function mapSeverity(impact?: string): FindingSeverity {
|
|
61
69
|
switch (impact) {
|
|
62
70
|
case "High":
|
|
63
|
-
return "High"
|
|
71
|
+
return "High"
|
|
64
72
|
case "Medium":
|
|
65
|
-
return "Medium"
|
|
73
|
+
return "Medium"
|
|
66
74
|
case "Low":
|
|
67
|
-
return "Low"
|
|
75
|
+
return "Low"
|
|
68
76
|
case "Informational":
|
|
69
|
-
return "Informational"
|
|
77
|
+
return "Informational"
|
|
70
78
|
default:
|
|
71
|
-
return "Informational"
|
|
79
|
+
return "Informational"
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
function mapConfidence(confidence?: string): "High" | "Medium" | "Low" {
|
|
76
84
|
switch (confidence) {
|
|
77
85
|
case "High":
|
|
78
|
-
return "High"
|
|
86
|
+
return "High"
|
|
79
87
|
case "Medium":
|
|
80
|
-
return "Medium"
|
|
88
|
+
return "Medium"
|
|
81
89
|
case "Low":
|
|
82
|
-
return "Low"
|
|
90
|
+
return "Low"
|
|
83
91
|
default:
|
|
84
|
-
return "Low"
|
|
92
|
+
return "Low"
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
function findingLines(lines?: number[]): [number, number] {
|
|
89
97
|
if (!lines || lines.length === 0) {
|
|
90
|
-
return [1, 1]
|
|
98
|
+
return [1, 1]
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
if (lines.length === 1) {
|
|
94
|
-
const only = lines[0] ?? 1
|
|
95
|
-
return [only, only]
|
|
102
|
+
const only = lines[0] ?? 1
|
|
103
|
+
return [only, only]
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
const start = lines[0] ?? 1
|
|
99
|
-
const end = lines[lines.length - 1] ?? start
|
|
100
|
-
return [start, end]
|
|
106
|
+
const start = lines[0] ?? 1
|
|
107
|
+
const end = lines[lines.length - 1] ?? start
|
|
108
|
+
return [start, end]
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
function createFindingID(check: string, file: string, lines: [number, number]): string {
|
|
104
|
-
const key = `${check}:${file}:${lines[0]}-${lines[1]}
|
|
105
|
-
return createHash("sha256").update(key).digest("hex").slice(0, 16)
|
|
112
|
+
const key = `${check}:${file}:${lines[0]}-${lines[1]}`
|
|
113
|
+
return createHash("sha256").update(key).digest("hex").slice(0, 16)
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
function buildCommand(args: SlitherArgs): string[] {
|
|
109
|
-
const command = [
|
|
110
|
-
"slither",
|
|
111
|
-
args.target,
|
|
112
|
-
"--json",
|
|
113
|
-
"-",
|
|
114
|
-
"--filter-paths",
|
|
115
|
-
"node_modules",
|
|
116
|
-
];
|
|
117
|
+
const command = ["slither", args.target, "--json", "-", "--filter-paths", "node_modules"]
|
|
117
118
|
|
|
118
119
|
if (args.detectors && args.detectors.length > 0) {
|
|
119
|
-
command.push("--detect", args.detectors.join(","))
|
|
120
|
+
command.push("--detect", args.detectors.join(","))
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
if (args.exclude && args.exclude.length > 0) {
|
|
123
|
-
command.push("--exclude-detectors", args.exclude.join(","))
|
|
124
|
+
command.push("--exclude-detectors", args.exclude.join(","))
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
if (args.solc_version) {
|
|
127
|
-
command.push("--solc", `solc:${args.solc_version}`)
|
|
128
|
+
command.push("--solc", `solc:${args.solc_version}`)
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
return command
|
|
131
|
+
return command
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
const FALLBACK_TRIGGERS = [
|
|
@@ -143,28 +144,38 @@ const FALLBACK_TRIGGERS = [
|
|
|
143
144
|
"YulException",
|
|
144
145
|
"StackTooDeep",
|
|
145
146
|
"Stack too deep",
|
|
146
|
-
]
|
|
147
|
+
]
|
|
147
148
|
|
|
148
149
|
function shouldTryFlattenFallback(errors: string[], stderr: string): boolean {
|
|
149
|
-
const combined = [...errors, stderr].join(" ")
|
|
150
|
-
return FALLBACK_TRIGGERS.some((trigger) => combined.includes(trigger))
|
|
150
|
+
const combined = [...errors, stderr].join(" ")
|
|
151
|
+
return FALLBACK_TRIGGERS.some((trigger) => combined.includes(trigger))
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
const parseSolcVersion = parseSolcVersionShared
|
|
154
155
|
const extractContractNames = extractContractNamesShared
|
|
155
156
|
const hasBinary = hasBinaryShared
|
|
156
157
|
|
|
157
|
-
function ensureSolc(version: string): boolean {
|
|
158
|
-
if (hasBinary("solc")) return true
|
|
159
|
-
if (!hasBinary("solc-select")) return false
|
|
158
|
+
async function ensureSolc(version: string): Promise<boolean> {
|
|
159
|
+
if (hasBinary("solc")) return true
|
|
160
|
+
if (!hasBinary("solc-select")) return false
|
|
160
161
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
const installProc = Bun.spawn(["solc-select", "install", version], {
|
|
163
|
+
stdout: "pipe",
|
|
164
|
+
stderr: "pipe",
|
|
165
|
+
signal: AbortSignal.timeout(30_000),
|
|
166
|
+
})
|
|
167
|
+
const installExit = await installProc.exited
|
|
168
|
+
if (installExit !== 0) return false
|
|
169
|
+
|
|
170
|
+
const useProc = Bun.spawn(["solc-select", "use", version], {
|
|
171
|
+
stdout: "pipe",
|
|
172
|
+
stderr: "pipe",
|
|
173
|
+
signal: AbortSignal.timeout(30_000),
|
|
174
|
+
})
|
|
175
|
+
const useExit = await useProc.exited
|
|
176
|
+
return useExit === 0
|
|
166
177
|
} catch (_e) {
|
|
167
|
-
return false
|
|
178
|
+
return false
|
|
168
179
|
}
|
|
169
180
|
}
|
|
170
181
|
|
|
@@ -174,30 +185,50 @@ export const runSlitherCommand: RunSlitherCommand = async (command, signal, cwd)
|
|
|
174
185
|
stdout: "pipe",
|
|
175
186
|
stderr: "pipe",
|
|
176
187
|
signal,
|
|
177
|
-
})
|
|
188
|
+
})
|
|
178
189
|
|
|
179
190
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
180
191
|
child.exited,
|
|
181
192
|
new Response(child.stdout).text(),
|
|
182
193
|
new Response(child.stderr).text(),
|
|
183
|
-
])
|
|
194
|
+
])
|
|
184
195
|
|
|
185
196
|
return {
|
|
186
197
|
stdout,
|
|
187
198
|
stderr,
|
|
188
199
|
exitCode,
|
|
189
|
-
}
|
|
190
|
-
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export type SpawnFn = (
|
|
204
|
+
command: string[],
|
|
205
|
+
options?: { cwd?: string; timeout?: number },
|
|
206
|
+
) => Promise<{ stdout: string; exitCode: number }>
|
|
191
207
|
|
|
192
208
|
export type FlattenFallbackDeps = {
|
|
193
|
-
runCommand: RunSlitherCommand
|
|
194
|
-
hasBinary: (name: string) => boolean
|
|
195
|
-
ensureSolc: (version: string) => boolean
|
|
196
|
-
parseSolcVersion: (target: string) => string | undefined
|
|
197
|
-
extractContractNames: (filePath: string) => string[]
|
|
198
|
-
|
|
199
|
-
cwd: string
|
|
200
|
-
}
|
|
209
|
+
runCommand: RunSlitherCommand
|
|
210
|
+
hasBinary: (name: string) => boolean
|
|
211
|
+
ensureSolc: (version: string) => Promise<boolean>
|
|
212
|
+
parseSolcVersion: (target: string) => Promise<string | undefined> | string | undefined
|
|
213
|
+
extractContractNames: (filePath: string) => Promise<string[]> | string[]
|
|
214
|
+
spawnFn: SpawnFn
|
|
215
|
+
cwd: string
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function defaultSpawnFn(
|
|
219
|
+
command: string[],
|
|
220
|
+
options?: { cwd?: string; timeout?: number },
|
|
221
|
+
): Promise<{ stdout: string; exitCode: number }> {
|
|
222
|
+
const proc = Bun.spawn(command, {
|
|
223
|
+
stdout: "pipe",
|
|
224
|
+
stderr: "pipe",
|
|
225
|
+
cwd: options?.cwd,
|
|
226
|
+
...(options?.timeout ? { signal: AbortSignal.timeout(options.timeout) } : {}),
|
|
227
|
+
})
|
|
228
|
+
const exitCode = await proc.exited
|
|
229
|
+
const stdout = await new Response(proc.stdout).text()
|
|
230
|
+
return { stdout, exitCode }
|
|
231
|
+
}
|
|
201
232
|
|
|
202
233
|
const defaultFlattenDeps: FlattenFallbackDeps = {
|
|
203
234
|
runCommand: runSlitherCommand,
|
|
@@ -205,16 +236,16 @@ const defaultFlattenDeps: FlattenFallbackDeps = {
|
|
|
205
236
|
ensureSolc,
|
|
206
237
|
parseSolcVersion,
|
|
207
238
|
extractContractNames,
|
|
208
|
-
|
|
239
|
+
spawnFn: defaultSpawnFn,
|
|
209
240
|
cwd: process.cwd(),
|
|
210
|
-
}
|
|
241
|
+
}
|
|
211
242
|
|
|
212
243
|
export async function flattenFallback(
|
|
213
244
|
args: SlitherArgs,
|
|
214
245
|
context: ToolContext,
|
|
215
246
|
deps: FlattenFallbackDeps = defaultFlattenDeps,
|
|
216
247
|
): Promise<SlitherAnalyzeResult | undefined> {
|
|
217
|
-
const startedAt = Date.now()
|
|
248
|
+
const startedAt = Date.now()
|
|
218
249
|
|
|
219
250
|
if (!deps.hasBinary("forge")) {
|
|
220
251
|
return {
|
|
@@ -224,117 +255,129 @@ export async function flattenFallback(
|
|
|
224
255
|
executionTime: Date.now() - startedAt,
|
|
225
256
|
errors: ["forge binary not found — required for via_ir flatten fallback"],
|
|
226
257
|
error: "forge binary not found — required for via_ir flatten fallback",
|
|
227
|
-
}
|
|
258
|
+
}
|
|
228
259
|
}
|
|
229
260
|
|
|
230
|
-
const solcVersion = args.solc_version ?? deps.parseSolcVersion(args.target)
|
|
261
|
+
const solcVersion = args.solc_version ?? (await deps.parseSolcVersion(args.target))
|
|
231
262
|
if (!solcVersion) {
|
|
232
263
|
return {
|
|
233
264
|
success: false,
|
|
234
265
|
findingsCount: 0,
|
|
235
266
|
findings: [],
|
|
236
267
|
executionTime: Date.now() - startedAt,
|
|
237
|
-
errors: [
|
|
238
|
-
|
|
239
|
-
|
|
268
|
+
errors: [
|
|
269
|
+
"Could not determine solc version from foundry.toml or pragma — required for flatten fallback",
|
|
270
|
+
],
|
|
271
|
+
error:
|
|
272
|
+
"Could not determine solc version from foundry.toml or pragma — required for flatten fallback",
|
|
273
|
+
}
|
|
240
274
|
}
|
|
241
275
|
|
|
242
|
-
if (!deps.ensureSolc(solcVersion)) {
|
|
276
|
+
if (!(await deps.ensureSolc(solcVersion))) {
|
|
243
277
|
return {
|
|
244
278
|
success: false,
|
|
245
279
|
findingsCount: 0,
|
|
246
280
|
findings: [],
|
|
247
281
|
executionTime: Date.now() - startedAt,
|
|
248
282
|
errors: ["solc not available and solc-select not found"],
|
|
249
|
-
error:
|
|
250
|
-
|
|
283
|
+
error:
|
|
284
|
+
"Flatten fallback requires solc on PATH. Install with: pipx install solc-select && solc-select install " +
|
|
285
|
+
solcVersion,
|
|
286
|
+
}
|
|
251
287
|
}
|
|
252
288
|
|
|
253
|
-
const srcDir = join(args.target, "src")
|
|
254
|
-
let solFiles: string[] = []
|
|
289
|
+
const srcDir = join(args.target, "src")
|
|
290
|
+
let solFiles: string[] = []
|
|
255
291
|
if (args.target.endsWith(".sol")) {
|
|
256
|
-
solFiles = [args.target]
|
|
292
|
+
solFiles = [args.target]
|
|
257
293
|
} else if (existsSync(srcDir)) {
|
|
258
294
|
try {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
295
|
+
const findResult = await deps.spawnFn(
|
|
296
|
+
[
|
|
297
|
+
"find",
|
|
298
|
+
srcDir,
|
|
299
|
+
"-maxdepth",
|
|
300
|
+
"3",
|
|
301
|
+
"-name",
|
|
302
|
+
"*.sol",
|
|
303
|
+
"-not",
|
|
304
|
+
"-path",
|
|
305
|
+
"*/mocks/*",
|
|
306
|
+
"-not",
|
|
307
|
+
"-path",
|
|
308
|
+
"*/test/*",
|
|
309
|
+
],
|
|
310
|
+
{ timeout: 5_000 },
|
|
311
|
+
)
|
|
312
|
+
if (findResult.exitCode !== 0) return undefined
|
|
313
|
+
solFiles = findResult.stdout.trim().split("\n").filter(Boolean)
|
|
267
314
|
} catch (_e) {
|
|
268
|
-
return undefined
|
|
315
|
+
return undefined
|
|
269
316
|
}
|
|
270
317
|
}
|
|
271
318
|
|
|
272
|
-
if (solFiles.length === 0) return undefined
|
|
319
|
+
if (solFiles.length === 0) return undefined
|
|
273
320
|
|
|
274
|
-
const tmpDir = mkdtempSync(join(tmpdir(), "argus-slither-"))
|
|
275
|
-
const allFindings: Finding[] = []
|
|
276
|
-
const errors: string[] = []
|
|
321
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "argus-slither-"))
|
|
322
|
+
const allFindings: Finding[] = []
|
|
323
|
+
const errors: string[] = []
|
|
277
324
|
|
|
278
325
|
try {
|
|
279
326
|
for (const solFile of solFiles) {
|
|
280
|
-
if (context.abort.aborted) break
|
|
327
|
+
if (context.abort.aborted) break
|
|
281
328
|
|
|
282
|
-
const baseName = solFile.split("/").pop()?.replace(".sol", "") ?? "Contract"
|
|
283
|
-
const flatFile = join(tmpDir, `${baseName}.flat.sol`)
|
|
284
|
-
const originalContracts = deps.extractContractNames(solFile)
|
|
329
|
+
const baseName = solFile.split("/").pop()?.replace(".sol", "") ?? "Contract"
|
|
330
|
+
const flatFile = join(tmpDir, `${baseName}.flat.sol`)
|
|
331
|
+
const originalContracts = await deps.extractContractNames(solFile)
|
|
285
332
|
|
|
286
333
|
try {
|
|
287
|
-
const
|
|
288
|
-
encoding: "utf-8",
|
|
289
|
-
timeout: 30_000,
|
|
334
|
+
const flatResult = await deps.spawnFn(["forge", "flatten", solFile], {
|
|
290
335
|
cwd: deps.cwd,
|
|
291
|
-
|
|
292
|
-
})
|
|
293
|
-
|
|
336
|
+
timeout: 30_000,
|
|
337
|
+
})
|
|
338
|
+
if (flatResult.exitCode !== 0) {
|
|
339
|
+
errors.push(`forge flatten failed for ${solFile}`)
|
|
340
|
+
continue
|
|
341
|
+
}
|
|
342
|
+
writeFileSync(flatFile, flatResult.stdout)
|
|
294
343
|
} catch (_e) {
|
|
295
|
-
errors.push(`forge flatten failed for ${solFile}`)
|
|
296
|
-
continue
|
|
344
|
+
errors.push(`forge flatten failed for ${solFile}`)
|
|
345
|
+
continue
|
|
297
346
|
}
|
|
298
347
|
|
|
299
|
-
const command = [
|
|
300
|
-
"slither",
|
|
301
|
-
flatFile,
|
|
302
|
-
"--json",
|
|
303
|
-
"-",
|
|
304
|
-
"--solc-solcs-select",
|
|
305
|
-
solcVersion,
|
|
306
|
-
];
|
|
348
|
+
const command = ["slither", flatFile, "--json", "-", "--solc-solcs-select", solcVersion]
|
|
307
349
|
|
|
308
350
|
try {
|
|
309
|
-
const runResult = await deps.runCommand(command, context.abort, deps.cwd)
|
|
351
|
+
const runResult = await deps.runCommand(command, context.abort, deps.cwd)
|
|
310
352
|
|
|
311
|
-
let payload: SlitherPayload
|
|
353
|
+
let payload: SlitherPayload
|
|
312
354
|
try {
|
|
313
|
-
payload = JSON.parse(runResult.stdout) as SlitherPayload
|
|
355
|
+
payload = JSON.parse(runResult.stdout) as SlitherPayload
|
|
314
356
|
} catch (_e) {
|
|
315
|
-
if (runResult.stderr.trim()) errors.push(runResult.stderr.trim())
|
|
316
|
-
continue
|
|
357
|
+
if (runResult.stderr.trim()) errors.push(runResult.stderr.trim())
|
|
358
|
+
continue
|
|
317
359
|
}
|
|
318
360
|
|
|
319
|
-
const rawFindings = parseFindings(payload)
|
|
320
|
-
const filtered =
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
361
|
+
const rawFindings = parseFindings(payload)
|
|
362
|
+
const filtered =
|
|
363
|
+
originalContracts.length > 0
|
|
364
|
+
? rawFindings.filter((f) => {
|
|
365
|
+
if (f.file.includes(".flat.sol") || f.file === flatFile) return true
|
|
366
|
+
return originalContracts.some(
|
|
367
|
+
(name) => f.description.includes(name) || f.file.includes(name),
|
|
368
|
+
)
|
|
369
|
+
})
|
|
370
|
+
: rawFindings
|
|
328
371
|
|
|
329
372
|
const remapped = filtered.map((f) => ({
|
|
330
373
|
...f,
|
|
331
|
-
file: f.file.includes(".flat.sol") ? solFile.replace(args.target
|
|
332
|
-
}))
|
|
374
|
+
file: f.file.includes(".flat.sol") ? solFile.replace(`${args.target}/`, "") : f.file,
|
|
375
|
+
}))
|
|
333
376
|
|
|
334
|
-
allFindings.push(...remapped)
|
|
377
|
+
allFindings.push(...remapped)
|
|
335
378
|
} catch (e) {
|
|
336
|
-
const msg = e instanceof Error ? e.message : String(e)
|
|
337
|
-
errors.push(`Slither flatten fallback failed for ${baseName}: ${msg}`)
|
|
379
|
+
const msg = e instanceof Error ? e.message : String(e)
|
|
380
|
+
errors.push(`Slither flatten fallback failed for ${baseName}: ${msg}`)
|
|
338
381
|
}
|
|
339
382
|
}
|
|
340
383
|
|
|
@@ -343,24 +386,27 @@ export async function flattenFallback(
|
|
|
343
386
|
findingsCount: allFindings.length,
|
|
344
387
|
findings: allFindings,
|
|
345
388
|
executionTime: Date.now() - startedAt,
|
|
346
|
-
errors:
|
|
347
|
-
|
|
389
|
+
errors:
|
|
390
|
+
errors.length > 0
|
|
391
|
+
? [`[flatten-fallback] ${errors.join("; ")}`]
|
|
392
|
+
: ["[flatten-fallback] Analysis completed via forge flatten"],
|
|
393
|
+
}
|
|
348
394
|
} finally {
|
|
349
395
|
try {
|
|
350
|
-
rmSync(tmpDir, { recursive: true, force: true })
|
|
396
|
+
rmSync(tmpDir, { recursive: true, force: true })
|
|
351
397
|
} catch (_cleanupErr) {
|
|
352
|
-
|
|
398
|
+
logger.debug("Failed to clean up temp directory")
|
|
353
399
|
}
|
|
354
400
|
}
|
|
355
401
|
}
|
|
356
402
|
|
|
357
403
|
function parseFindings(payload: SlitherPayload): Finding[] {
|
|
358
|
-
const detectors = payload.results?.detectors ?? []
|
|
404
|
+
const detectors = payload.results?.detectors ?? []
|
|
359
405
|
|
|
360
406
|
return detectors.map((detector) => {
|
|
361
|
-
const file = detector.elements?.[0]?.source_mapping?.filename_relative ?? "unknown"
|
|
362
|
-
const lines = findingLines(detector.elements?.[0]?.source_mapping?.lines)
|
|
363
|
-
const check = detector.check ?? "unknown-check"
|
|
407
|
+
const file = detector.elements?.[0]?.source_mapping?.filename_relative ?? "unknown"
|
|
408
|
+
const lines = findingLines(detector.elements?.[0]?.source_mapping?.lines)
|
|
409
|
+
const check = detector.check ?? "unknown-check"
|
|
364
410
|
|
|
365
411
|
return {
|
|
366
412
|
id: createFindingID(check, file, lines),
|
|
@@ -371,62 +417,65 @@ function parseFindings(payload: SlitherPayload): Finding[] {
|
|
|
371
417
|
file,
|
|
372
418
|
lines,
|
|
373
419
|
source: "slither",
|
|
374
|
-
}
|
|
375
|
-
})
|
|
420
|
+
}
|
|
421
|
+
})
|
|
376
422
|
}
|
|
377
423
|
|
|
378
424
|
export async function executeSlitherAnalyze(
|
|
379
425
|
args: SlitherArgs,
|
|
380
426
|
context: ToolContext,
|
|
381
427
|
runCommand: RunSlitherCommand = runSlitherCommand,
|
|
382
|
-
cwd?: string
|
|
428
|
+
cwd?: string,
|
|
383
429
|
): Promise<SlitherAnalyzeResult> {
|
|
384
|
-
const projectDir = cwd ?? context
|
|
385
|
-
const startedAt = Date.now()
|
|
386
|
-
context.metadata({ title: `Slither analysis: ${args.target}` })
|
|
430
|
+
const projectDir = cwd ?? resolveProjectDir(context)
|
|
431
|
+
const startedAt = Date.now()
|
|
432
|
+
context.metadata({ title: `Slither analysis: ${args.target}` })
|
|
387
433
|
|
|
388
434
|
if (args.via_ir) {
|
|
389
435
|
const fallbackResult = await flattenFallback(args, context, {
|
|
390
436
|
...defaultFlattenDeps,
|
|
391
437
|
runCommand,
|
|
392
438
|
cwd: projectDir,
|
|
393
|
-
})
|
|
394
|
-
if (fallbackResult) return fallbackResult
|
|
439
|
+
})
|
|
440
|
+
if (fallbackResult) return fallbackResult
|
|
395
441
|
return {
|
|
396
442
|
success: false,
|
|
397
443
|
findingsCount: 0,
|
|
398
444
|
findings: [],
|
|
399
445
|
executionTime: Date.now() - startedAt,
|
|
400
|
-
errors: [
|
|
401
|
-
|
|
402
|
-
|
|
446
|
+
errors: [
|
|
447
|
+
"via_ir enabled — flatten fallback failed. Ensure forge and solc-select are installed.",
|
|
448
|
+
],
|
|
449
|
+
error:
|
|
450
|
+
"Project uses via_ir which is incompatible with Slither direct analysis. Flatten fallback also failed.",
|
|
451
|
+
}
|
|
403
452
|
}
|
|
404
453
|
|
|
405
|
-
const command = buildCommand(args)
|
|
454
|
+
const command = buildCommand(args)
|
|
406
455
|
|
|
407
456
|
try {
|
|
408
|
-
const runResult = await runCommand(command, context.abort, projectDir)
|
|
409
|
-
const errors: string[] = []
|
|
457
|
+
const runResult = await runCommand(command, context.abort, projectDir)
|
|
458
|
+
const errors: string[] = []
|
|
410
459
|
|
|
411
460
|
if (runResult.exitCode !== 0) {
|
|
412
|
-
errors.push(`Slither exited with code ${runResult.exitCode}`)
|
|
461
|
+
errors.push(`Slither exited with code ${runResult.exitCode}`)
|
|
413
462
|
}
|
|
414
463
|
if (runResult.stderr.trim().length > 0) {
|
|
415
|
-
errors.push(runResult.stderr.trim())
|
|
464
|
+
errors.push(runResult.stderr.trim())
|
|
416
465
|
}
|
|
417
466
|
|
|
418
|
-
let payload: SlitherPayload
|
|
467
|
+
let payload: SlitherPayload
|
|
419
468
|
try {
|
|
420
|
-
payload = JSON.parse(runResult.stdout) as SlitherPayload
|
|
469
|
+
payload = JSON.parse(runResult.stdout) as SlitherPayload
|
|
421
470
|
} catch (error) {
|
|
422
|
-
const message = error instanceof Error ? error.message : "Unknown parse error"
|
|
471
|
+
const message = error instanceof Error ? error.message : "Unknown parse error"
|
|
423
472
|
if (shouldTryFlattenFallback(errors, runResult.stderr)) {
|
|
424
473
|
const fallbackResult = await flattenFallback(args, context, {
|
|
425
474
|
...defaultFlattenDeps,
|
|
426
475
|
runCommand,
|
|
427
476
|
cwd: projectDir,
|
|
428
|
-
})
|
|
429
|
-
if (fallbackResult) return fallbackResult
|
|
477
|
+
})
|
|
478
|
+
if (fallbackResult) return fallbackResult
|
|
430
479
|
}
|
|
431
480
|
return {
|
|
432
481
|
success: false,
|
|
@@ -435,23 +484,23 @@ export async function executeSlitherAnalyze(
|
|
|
435
484
|
executionTime: Date.now() - startedAt,
|
|
436
485
|
errors,
|
|
437
486
|
error: `Slither output parse error: ${message}`,
|
|
438
|
-
}
|
|
487
|
+
}
|
|
439
488
|
}
|
|
440
489
|
|
|
441
490
|
if (payload.error) {
|
|
442
|
-
errors.push(payload.error)
|
|
491
|
+
errors.push(payload.error)
|
|
443
492
|
}
|
|
444
493
|
|
|
445
|
-
const findings = parseFindings(payload)
|
|
446
|
-
const success = findings.length > 0 || (runResult.exitCode === 0 && payload.success !== false)
|
|
494
|
+
const findings = parseFindings(payload)
|
|
495
|
+
const success = findings.length > 0 || (runResult.exitCode === 0 && payload.success !== false)
|
|
447
496
|
|
|
448
497
|
if (!success && findings.length === 0 && shouldTryFlattenFallback(errors, runResult.stderr)) {
|
|
449
498
|
const fallbackResult = await flattenFallback(args, context, {
|
|
450
499
|
...defaultFlattenDeps,
|
|
451
500
|
runCommand,
|
|
452
501
|
cwd: projectDir,
|
|
453
|
-
})
|
|
454
|
-
if (fallbackResult) return fallbackResult
|
|
502
|
+
})
|
|
503
|
+
if (fallbackResult) return fallbackResult
|
|
455
504
|
}
|
|
456
505
|
|
|
457
506
|
return {
|
|
@@ -460,10 +509,10 @@ export async function executeSlitherAnalyze(
|
|
|
460
509
|
findings,
|
|
461
510
|
executionTime: Date.now() - startedAt,
|
|
462
511
|
errors,
|
|
463
|
-
}
|
|
512
|
+
}
|
|
464
513
|
} catch (error) {
|
|
465
|
-
const message = error instanceof Error ? error.message : "Unknown error"
|
|
466
|
-
const maybeErrno = error as Error & { code?: string; name?: string }
|
|
514
|
+
const message = error instanceof Error ? error.message : "Unknown error"
|
|
515
|
+
const maybeErrno = error as Error & { code?: string; name?: string }
|
|
467
516
|
|
|
468
517
|
if (maybeErrno.code === "ENOENT") {
|
|
469
518
|
return {
|
|
@@ -473,7 +522,7 @@ export async function executeSlitherAnalyze(
|
|
|
473
522
|
executionTime: Date.now() - startedAt,
|
|
474
523
|
errors: [],
|
|
475
524
|
error: "Slither not found. Install with: pip install slither-analyzer",
|
|
476
|
-
}
|
|
525
|
+
}
|
|
477
526
|
}
|
|
478
527
|
|
|
479
528
|
if (maybeErrno.name === "AbortError" || context.abort.aborted) {
|
|
@@ -484,7 +533,7 @@ export async function executeSlitherAnalyze(
|
|
|
484
533
|
executionTime: Date.now() - startedAt,
|
|
485
534
|
errors: ["Slither analysis aborted"],
|
|
486
535
|
error: "Slither analysis aborted",
|
|
487
|
-
}
|
|
536
|
+
}
|
|
488
537
|
}
|
|
489
538
|
|
|
490
539
|
return {
|
|
@@ -494,34 +543,33 @@ export async function executeSlitherAnalyze(
|
|
|
494
543
|
executionTime: Date.now() - startedAt,
|
|
495
544
|
errors: [message],
|
|
496
545
|
error: message,
|
|
497
|
-
}
|
|
546
|
+
}
|
|
498
547
|
}
|
|
499
548
|
}
|
|
500
549
|
|
|
501
550
|
export function detectViaIr(target: string): boolean {
|
|
502
|
-
let dir = resolve(target.endsWith(".sol") ? dirname(target) : target)
|
|
503
|
-
const root = resolve("/")
|
|
551
|
+
let dir = resolve(target.endsWith(".sol") ? dirname(target) : target)
|
|
552
|
+
const root = resolve("/")
|
|
504
553
|
|
|
505
554
|
while (true) {
|
|
506
|
-
const foundryTomlPath = join(dir, "foundry.toml")
|
|
555
|
+
const foundryTomlPath = join(dir, "foundry.toml")
|
|
507
556
|
if (existsSync(foundryTomlPath)) {
|
|
508
557
|
try {
|
|
509
|
-
const content = readFileSync(foundryTomlPath, "utf-8")
|
|
510
|
-
if (/^\s*via[_-]ir\s*=\s*true/m.test(content)) return true
|
|
558
|
+
const content = readFileSync(foundryTomlPath, "utf-8")
|
|
559
|
+
if (/^\s*via[_-]ir\s*=\s*true/m.test(content)) return true
|
|
511
560
|
} catch {
|
|
512
|
-
|
|
561
|
+
logger.debug("Unreadable foundry.toml, continuing directory walk")
|
|
513
562
|
}
|
|
514
563
|
}
|
|
515
|
-
if (dir === root) break
|
|
516
|
-
dir = dirname(dir)
|
|
564
|
+
if (dir === root) break
|
|
565
|
+
dir = dirname(dir)
|
|
517
566
|
}
|
|
518
567
|
|
|
519
|
-
return false
|
|
568
|
+
return false
|
|
520
569
|
}
|
|
521
570
|
|
|
522
571
|
export const slitherTool = tool({
|
|
523
|
-
description:
|
|
524
|
-
"Run Slither static analysis and return normalized findings for Solidity targets.",
|
|
572
|
+
description: "Run Slither static analysis and return normalized findings for Solidity targets.",
|
|
525
573
|
args: {
|
|
526
574
|
target: tool.schema.string(),
|
|
527
575
|
detectors: tool.schema.array(tool.schema.string()).optional(),
|
|
@@ -530,15 +578,15 @@ export const slitherTool = tool({
|
|
|
530
578
|
via_ir: tool.schema.boolean().optional(),
|
|
531
579
|
},
|
|
532
580
|
async execute(args, context) {
|
|
533
|
-
const projectDir = context
|
|
534
|
-
const resolvedTarget = isAbsolute(args.target) ? args.target : resolve(projectDir, args.target)
|
|
535
|
-
const viaIr = args.via_ir ?? detectViaIr(resolvedTarget)
|
|
581
|
+
const projectDir = resolveProjectDir(context)
|
|
582
|
+
const resolvedTarget = isAbsolute(args.target) ? args.target : resolve(projectDir, args.target)
|
|
583
|
+
const viaIr = args.via_ir ?? detectViaIr(resolvedTarget)
|
|
536
584
|
const result = await executeSlitherAnalyze(
|
|
537
585
|
{ ...args, target: resolvedTarget, via_ir: viaIr },
|
|
538
586
|
context,
|
|
539
587
|
runSlitherCommand,
|
|
540
588
|
projectDir,
|
|
541
|
-
)
|
|
542
|
-
return JSON.stringify(result)
|
|
589
|
+
)
|
|
590
|
+
return JSON.stringify(result)
|
|
543
591
|
},
|
|
544
|
-
})
|
|
592
|
+
})
|