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.
- package/AGENTS.md +3 -3
- package/README.md +229 -13
- package/package.json +37 -8
- package/skills/INVENTORY.md +88 -57
- package/skills/README.md +72 -6
- 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/checklists/cyfrin-defi-core/SKILL.md +3 -0
- package/skills/manifests/cyfrin.json +16 -0
- package/skills/manifests/defifofum.json +25 -0
- package/skills/manifests/kadenzipfel.json +48 -0
- package/skills/manifests/scvd.json +9 -0
- package/skills/manifests/smartbugs.json +9 -0
- package/skills/manifests/solodit.json +9 -0
- package/skills/manifests/sunweb3sec.json +9 -0
- package/skills/manifests/trailofbits.json +9 -0
- package/skills/methodology/audit-workflow/SKILL.md +3 -0
- package/skills/protocol-patterns/amm-dex/SKILL.md +3 -0
- package/skills/references/exploit-reference/SKILL.md +3 -0
- package/skills/vulnerability-patterns/access-control/SKILL.md +27 -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 +8 -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 +8 -1
- package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +8 -1
- package/skills/vulnerability-patterns/dos-revert/SKILL.md +14 -1
- 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 +13 -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 +22 -0
- package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +8 -1
- package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +11 -1
- package/skills/vulnerability-patterns/proxy-vulnerabilities/SKILL.md +209 -0
- package/skills/vulnerability-patterns/reentrancy/SKILL.md +22 -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 +11 -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 +13 -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 +27 -10
- package/src/agents/pythia-prompt.ts +7 -8
- package/src/agents/scribe-prompt.ts +10 -5
- package/src/agents/sentinel-prompt.ts +36 -7
- package/src/cli/cli-output.ts +16 -0
- package/src/cli/cli-program.ts +29 -22
- package/src/cli/commands/check-skills.ts +135 -0
- package/src/cli/commands/doctor.ts +303 -23
- package/src/cli/commands/init.ts +8 -6
- package/src/cli/commands/install.ts +10 -8
- package/src/cli/commands/lint-skills.ts +118 -0
- package/src/cli/index.ts +5 -5
- package/src/cli/tui-prompts.ts +4 -2
- 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 +225 -29
- package/src/create-managers.ts +10 -8
- package/src/create-tools.ts +14 -8
- 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 +79 -19
- package/src/features/index.ts +5 -5
- package/src/features/persistent-state/audit-state-manager.ts +158 -52
- 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/agent-tracker.ts +53 -0
- package/src/hooks/compaction-hook.ts +46 -37
- package/src/hooks/config-handler.ts +31 -11
- package/src/hooks/context-budget.ts +42 -0
- package/src/hooks/event-hook.ts +48 -23
- package/src/hooks/hook-system.ts +4 -4
- package/src/hooks/index.ts +5 -5
- package/src/hooks/knowledge-sync-hook.ts +19 -21
- package/src/hooks/recon-context-builder.ts +66 -0
- package/src/hooks/safe-create-hook.ts +9 -11
- package/src/hooks/system-prompt-hook.ts +128 -0
- package/src/hooks/tool-tracking-hook.ts +162 -29
- package/src/hooks/types.ts +2 -1
- package/src/index.ts +23 -13
- package/src/knowledge/retry.ts +53 -0
- package/src/knowledge/scvd-client.ts +103 -83
- package/src/knowledge/scvd-errors.ts +89 -0
- package/src/knowledge/scvd-index.ts +110 -62
- package/src/knowledge/scvd-sync.ts +223 -47
- package/src/knowledge/source-manifest.ts +102 -0
- package/src/managers/index.ts +1 -1
- package/src/managers/types.ts +19 -14
- package/src/plugin-interface.ts +19 -8
- package/src/shared/binary-utils.ts +44 -34
- 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 +91 -17
- 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 +237 -0
- package/src/skills/skill-schema.ts +99 -0
- package/src/solodit-lifecycle.ts +202 -0
- package/src/state/audit-state.ts +10 -8
- package/src/state/finding-store.ts +68 -55
- package/src/state/types.ts +96 -44
- package/src/tools/argus-skill-load-tool.ts +78 -0
- package/src/tools/contract-analyzer-tool.ts +60 -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 +153 -157
- package/src/tools/gas-analysis-tool.ts +264 -0
- package/src/tools/pattern-checker-tool.ts +206 -167
- package/src/tools/pattern-loader.ts +77 -0
- package/src/tools/pattern-schema.ts +51 -0
- package/src/tools/proxy-detection-tool.ts +224 -0
- package/src/tools/report-generator-tool.ts +333 -142
- package/src/tools/slither-tool.ts +300 -210
- package/src/tools/solodit-search-tool.ts +255 -80
- package/src/tools/sync-knowledge-tool.ts +7 -11
- package/src/utils/audit-artifact-detector.ts +118 -0
- package/src/utils/dependency-scanner.ts +93 -0
- package/src/utils/project-detector.ts +175 -86
- package/src/utils/solidity-parser.ts +112 -67
- package/src/utils/solodit-health.ts +29 -0
- package/src/hooks/event-hook-v2.ts +0 -99
- package/src/state/plugin-state.ts +0 -14
|
@@ -1,132 +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
|
-
signal: AbortSignal
|
|
48
|
-
|
|
55
|
+
signal: AbortSignal,
|
|
56
|
+
cwd: string,
|
|
57
|
+
) => Promise<SlitherRunResult>
|
|
49
58
|
|
|
50
59
|
export type SlitherAnalyzeResult = {
|
|
51
|
-
success: boolean
|
|
52
|
-
findingsCount: number
|
|
53
|
-
findings: Finding[]
|
|
54
|
-
executionTime: number
|
|
55
|
-
errors: string[]
|
|
56
|
-
error?: string
|
|
57
|
-
}
|
|
60
|
+
success: boolean
|
|
61
|
+
findingsCount: number
|
|
62
|
+
findings: Finding[]
|
|
63
|
+
executionTime: number
|
|
64
|
+
errors: string[]
|
|
65
|
+
error?: string
|
|
66
|
+
}
|
|
58
67
|
|
|
59
68
|
function mapSeverity(impact?: string): FindingSeverity {
|
|
60
69
|
switch (impact) {
|
|
61
70
|
case "High":
|
|
62
|
-
return "High"
|
|
71
|
+
return "High"
|
|
63
72
|
case "Medium":
|
|
64
|
-
return "Medium"
|
|
73
|
+
return "Medium"
|
|
65
74
|
case "Low":
|
|
66
|
-
return "Low"
|
|
75
|
+
return "Low"
|
|
67
76
|
case "Informational":
|
|
68
|
-
return "Informational"
|
|
77
|
+
return "Informational"
|
|
69
78
|
default:
|
|
70
|
-
return "Informational"
|
|
79
|
+
return "Informational"
|
|
71
80
|
}
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
function mapConfidence(confidence?: string): "High" | "Medium" | "Low" {
|
|
75
84
|
switch (confidence) {
|
|
76
85
|
case "High":
|
|
77
|
-
return "High"
|
|
86
|
+
return "High"
|
|
78
87
|
case "Medium":
|
|
79
|
-
return "Medium"
|
|
88
|
+
return "Medium"
|
|
80
89
|
case "Low":
|
|
81
|
-
return "Low"
|
|
90
|
+
return "Low"
|
|
82
91
|
default:
|
|
83
|
-
return "Low"
|
|
92
|
+
return "Low"
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
function findingLines(lines?: number[]): [number, number] {
|
|
88
97
|
if (!lines || lines.length === 0) {
|
|
89
|
-
return [1, 1]
|
|
98
|
+
return [1, 1]
|
|
90
99
|
}
|
|
91
100
|
|
|
92
101
|
if (lines.length === 1) {
|
|
93
|
-
const only = lines[0] ?? 1
|
|
94
|
-
return [only, only]
|
|
102
|
+
const only = lines[0] ?? 1
|
|
103
|
+
return [only, only]
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
const start = lines[0] ?? 1
|
|
98
|
-
const end = lines[lines.length - 1] ?? start
|
|
99
|
-
return [start, end]
|
|
106
|
+
const start = lines[0] ?? 1
|
|
107
|
+
const end = lines[lines.length - 1] ?? start
|
|
108
|
+
return [start, end]
|
|
100
109
|
}
|
|
101
110
|
|
|
102
111
|
function createFindingID(check: string, file: string, lines: [number, number]): string {
|
|
103
|
-
const key = `${check}:${file}:${lines[0]}-${lines[1]}
|
|
104
|
-
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)
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
function buildCommand(args: SlitherArgs): string[] {
|
|
108
|
-
const command = [
|
|
109
|
-
"slither",
|
|
110
|
-
args.target,
|
|
111
|
-
"--json",
|
|
112
|
-
"-",
|
|
113
|
-
"--filter-paths",
|
|
114
|
-
"node_modules",
|
|
115
|
-
];
|
|
117
|
+
const command = ["slither", args.target, "--json", "-", "--filter-paths", "node_modules"]
|
|
116
118
|
|
|
117
119
|
if (args.detectors && args.detectors.length > 0) {
|
|
118
|
-
command.push("--detect", args.detectors.join(","))
|
|
120
|
+
command.push("--detect", args.detectors.join(","))
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
if (args.exclude && args.exclude.length > 0) {
|
|
122
|
-
command.push("--exclude-detectors", args.exclude.join(","))
|
|
124
|
+
command.push("--exclude-detectors", args.exclude.join(","))
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
if (args.solc_version) {
|
|
126
|
-
command.push("--solc", `solc:${args.solc_version}`)
|
|
128
|
+
command.push("--solc", `solc:${args.solc_version}`)
|
|
127
129
|
}
|
|
128
130
|
|
|
129
|
-
return command
|
|
131
|
+
return command
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
const FALLBACK_TRIGGERS = [
|
|
@@ -142,59 +144,91 @@ const FALLBACK_TRIGGERS = [
|
|
|
142
144
|
"YulException",
|
|
143
145
|
"StackTooDeep",
|
|
144
146
|
"Stack too deep",
|
|
145
|
-
]
|
|
147
|
+
]
|
|
146
148
|
|
|
147
149
|
function shouldTryFlattenFallback(errors: string[], stderr: string): boolean {
|
|
148
|
-
const combined = [...errors, stderr].join(" ")
|
|
149
|
-
return FALLBACK_TRIGGERS.some((trigger) => combined.includes(trigger))
|
|
150
|
+
const combined = [...errors, stderr].join(" ")
|
|
151
|
+
return FALLBACK_TRIGGERS.some((trigger) => combined.includes(trigger))
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
const parseSolcVersion = parseSolcVersionShared
|
|
153
155
|
const extractContractNames = extractContractNamesShared
|
|
154
156
|
const hasBinary = hasBinaryShared
|
|
155
157
|
|
|
156
|
-
function ensureSolc(version: string): boolean {
|
|
157
|
-
if (hasBinary("solc")) return true
|
|
158
|
-
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
|
|
159
161
|
try {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
|
165
177
|
} catch (_e) {
|
|
166
|
-
return false
|
|
178
|
+
return false
|
|
167
179
|
}
|
|
168
180
|
}
|
|
169
181
|
|
|
170
|
-
export const runSlitherCommand: RunSlitherCommand = async (command, signal) => {
|
|
182
|
+
export const runSlitherCommand: RunSlitherCommand = async (command, signal, cwd) => {
|
|
171
183
|
const child = Bun.spawn(command, {
|
|
184
|
+
cwd,
|
|
172
185
|
stdout: "pipe",
|
|
173
186
|
stderr: "pipe",
|
|
174
187
|
signal,
|
|
175
|
-
})
|
|
188
|
+
})
|
|
176
189
|
|
|
177
190
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
178
191
|
child.exited,
|
|
179
192
|
new Response(child.stdout).text(),
|
|
180
193
|
new Response(child.stderr).text(),
|
|
181
|
-
])
|
|
194
|
+
])
|
|
182
195
|
|
|
183
196
|
return {
|
|
184
197
|
stdout,
|
|
185
198
|
stderr,
|
|
186
199
|
exitCode,
|
|
187
|
-
}
|
|
188
|
-
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export type SpawnFn = (
|
|
204
|
+
command: string[],
|
|
205
|
+
options?: { cwd?: string; timeout?: number },
|
|
206
|
+
) => Promise<{ stdout: string; exitCode: number }>
|
|
189
207
|
|
|
190
208
|
export type FlattenFallbackDeps = {
|
|
191
|
-
runCommand: RunSlitherCommand
|
|
192
|
-
hasBinary: (name: string) => boolean
|
|
193
|
-
ensureSolc: (version: string) => boolean
|
|
194
|
-
parseSolcVersion: (target: string) => string | undefined
|
|
195
|
-
extractContractNames: (filePath: string) => string[]
|
|
196
|
-
|
|
197
|
-
|
|
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
|
+
}
|
|
198
232
|
|
|
199
233
|
const defaultFlattenDeps: FlattenFallbackDeps = {
|
|
200
234
|
runCommand: runSlitherCommand,
|
|
@@ -202,119 +236,148 @@ const defaultFlattenDeps: FlattenFallbackDeps = {
|
|
|
202
236
|
ensureSolc,
|
|
203
237
|
parseSolcVersion,
|
|
204
238
|
extractContractNames,
|
|
205
|
-
|
|
206
|
-
|
|
239
|
+
spawnFn: defaultSpawnFn,
|
|
240
|
+
cwd: process.cwd(),
|
|
241
|
+
}
|
|
207
242
|
|
|
208
243
|
export async function flattenFallback(
|
|
209
244
|
args: SlitherArgs,
|
|
210
245
|
context: ToolContext,
|
|
211
246
|
deps: FlattenFallbackDeps = defaultFlattenDeps,
|
|
212
247
|
): Promise<SlitherAnalyzeResult | undefined> {
|
|
213
|
-
const startedAt = Date.now()
|
|
248
|
+
const startedAt = Date.now()
|
|
214
249
|
|
|
215
250
|
if (!deps.hasBinary("forge")) {
|
|
216
|
-
return
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
findingsCount: 0,
|
|
254
|
+
findings: [],
|
|
255
|
+
executionTime: Date.now() - startedAt,
|
|
256
|
+
errors: ["forge binary not found — required for via_ir flatten fallback"],
|
|
257
|
+
error: "forge binary not found — required for via_ir flatten fallback",
|
|
258
|
+
}
|
|
217
259
|
}
|
|
218
260
|
|
|
219
|
-
const solcVersion = args.solc_version ?? deps.parseSolcVersion(args.target)
|
|
261
|
+
const solcVersion = args.solc_version ?? (await deps.parseSolcVersion(args.target))
|
|
220
262
|
if (!solcVersion) {
|
|
221
|
-
return
|
|
263
|
+
return {
|
|
264
|
+
success: false,
|
|
265
|
+
findingsCount: 0,
|
|
266
|
+
findings: [],
|
|
267
|
+
executionTime: Date.now() - startedAt,
|
|
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
|
+
}
|
|
222
274
|
}
|
|
223
275
|
|
|
224
|
-
if (!deps.ensureSolc(solcVersion)) {
|
|
276
|
+
if (!(await deps.ensureSolc(solcVersion))) {
|
|
225
277
|
return {
|
|
226
278
|
success: false,
|
|
227
279
|
findingsCount: 0,
|
|
228
280
|
findings: [],
|
|
229
281
|
executionTime: Date.now() - startedAt,
|
|
230
282
|
errors: ["solc not available and solc-select not found"],
|
|
231
|
-
error:
|
|
232
|
-
|
|
283
|
+
error:
|
|
284
|
+
"Flatten fallback requires solc on PATH. Install with: pipx install solc-select && solc-select install " +
|
|
285
|
+
solcVersion,
|
|
286
|
+
}
|
|
233
287
|
}
|
|
234
288
|
|
|
235
|
-
const srcDir = join(args.target, "src")
|
|
236
|
-
let solFiles: string[] = []
|
|
289
|
+
const srcDir = join(args.target, "src")
|
|
290
|
+
let solFiles: string[] = []
|
|
237
291
|
if (args.target.endsWith(".sol")) {
|
|
238
|
-
solFiles = [args.target]
|
|
292
|
+
solFiles = [args.target]
|
|
239
293
|
} else if (existsSync(srcDir)) {
|
|
240
294
|
try {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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)
|
|
248
314
|
} catch (_e) {
|
|
249
|
-
return undefined
|
|
315
|
+
return undefined
|
|
250
316
|
}
|
|
251
317
|
}
|
|
252
318
|
|
|
253
|
-
if (solFiles.length === 0) return undefined
|
|
319
|
+
if (solFiles.length === 0) return undefined
|
|
254
320
|
|
|
255
|
-
const tmpDir = mkdtempSync(join(tmpdir(), "argus-slither-"))
|
|
256
|
-
const allFindings: Finding[] = []
|
|
257
|
-
const errors: string[] = []
|
|
321
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "argus-slither-"))
|
|
322
|
+
const allFindings: Finding[] = []
|
|
323
|
+
const errors: string[] = []
|
|
258
324
|
|
|
259
325
|
try {
|
|
260
326
|
for (const solFile of solFiles) {
|
|
261
|
-
if (context.abort.aborted) break
|
|
327
|
+
if (context.abort.aborted) break
|
|
262
328
|
|
|
263
|
-
const baseName = solFile.split("/").pop()?.replace(".sol", "") ?? "Contract"
|
|
264
|
-
const flatFile = join(tmpDir, `${baseName}.flat.sol`)
|
|
265
|
-
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)
|
|
266
332
|
|
|
267
333
|
try {
|
|
268
|
-
const
|
|
269
|
-
|
|
334
|
+
const flatResult = await deps.spawnFn(["forge", "flatten", solFile], {
|
|
335
|
+
cwd: deps.cwd,
|
|
270
336
|
timeout: 30_000,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
337
|
+
})
|
|
338
|
+
if (flatResult.exitCode !== 0) {
|
|
339
|
+
errors.push(`forge flatten failed for ${solFile}`)
|
|
340
|
+
continue
|
|
341
|
+
}
|
|
342
|
+
writeFileSync(flatFile, flatResult.stdout)
|
|
274
343
|
} catch (_e) {
|
|
275
|
-
errors.push(`forge flatten failed for ${solFile}`)
|
|
276
|
-
continue
|
|
344
|
+
errors.push(`forge flatten failed for ${solFile}`)
|
|
345
|
+
continue
|
|
277
346
|
}
|
|
278
347
|
|
|
279
|
-
const command = [
|
|
280
|
-
"slither",
|
|
281
|
-
flatFile,
|
|
282
|
-
"--json",
|
|
283
|
-
"-",
|
|
284
|
-
"--solc-solcs-select",
|
|
285
|
-
solcVersion,
|
|
286
|
-
];
|
|
348
|
+
const command = ["slither", flatFile, "--json", "-", "--solc-solcs-select", solcVersion]
|
|
287
349
|
|
|
288
350
|
try {
|
|
289
|
-
const runResult = await deps.runCommand(command, context.abort)
|
|
351
|
+
const runResult = await deps.runCommand(command, context.abort, deps.cwd)
|
|
290
352
|
|
|
291
|
-
let payload: SlitherPayload
|
|
353
|
+
let payload: SlitherPayload
|
|
292
354
|
try {
|
|
293
|
-
payload = JSON.parse(runResult.stdout) as SlitherPayload
|
|
355
|
+
payload = JSON.parse(runResult.stdout) as SlitherPayload
|
|
294
356
|
} catch (_e) {
|
|
295
|
-
if (runResult.stderr.trim()) errors.push(runResult.stderr.trim())
|
|
296
|
-
continue
|
|
357
|
+
if (runResult.stderr.trim()) errors.push(runResult.stderr.trim())
|
|
358
|
+
continue
|
|
297
359
|
}
|
|
298
360
|
|
|
299
|
-
const rawFindings = parseFindings(payload)
|
|
300
|
-
const filtered =
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
|
308
371
|
|
|
309
372
|
const remapped = filtered.map((f) => ({
|
|
310
373
|
...f,
|
|
311
|
-
file: f.file.includes(".flat.sol") ? solFile.replace(args.target
|
|
312
|
-
}))
|
|
374
|
+
file: f.file.includes(".flat.sol") ? solFile.replace(`${args.target}/`, "") : f.file,
|
|
375
|
+
}))
|
|
313
376
|
|
|
314
|
-
allFindings.push(...remapped)
|
|
377
|
+
allFindings.push(...remapped)
|
|
315
378
|
} catch (e) {
|
|
316
|
-
const msg = e instanceof Error ? e.message : String(e)
|
|
317
|
-
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}`)
|
|
318
381
|
}
|
|
319
382
|
}
|
|
320
383
|
|
|
@@ -323,24 +386,27 @@ export async function flattenFallback(
|
|
|
323
386
|
findingsCount: allFindings.length,
|
|
324
387
|
findings: allFindings,
|
|
325
388
|
executionTime: Date.now() - startedAt,
|
|
326
|
-
errors:
|
|
327
|
-
|
|
389
|
+
errors:
|
|
390
|
+
errors.length > 0
|
|
391
|
+
? [`[flatten-fallback] ${errors.join("; ")}`]
|
|
392
|
+
: ["[flatten-fallback] Analysis completed via forge flatten"],
|
|
393
|
+
}
|
|
328
394
|
} finally {
|
|
329
395
|
try {
|
|
330
|
-
rmSync(tmpDir, { recursive: true, force: true })
|
|
396
|
+
rmSync(tmpDir, { recursive: true, force: true })
|
|
331
397
|
} catch (_cleanupErr) {
|
|
332
|
-
|
|
398
|
+
logger.debug("Failed to clean up temp directory")
|
|
333
399
|
}
|
|
334
400
|
}
|
|
335
401
|
}
|
|
336
402
|
|
|
337
403
|
function parseFindings(payload: SlitherPayload): Finding[] {
|
|
338
|
-
const detectors = payload.results?.detectors ?? []
|
|
404
|
+
const detectors = payload.results?.detectors ?? []
|
|
339
405
|
|
|
340
406
|
return detectors.map((detector) => {
|
|
341
|
-
const file = detector.elements?.[0]?.source_mapping?.filename_relative ?? "unknown"
|
|
342
|
-
const lines = findingLines(detector.elements?.[0]?.source_mapping?.lines)
|
|
343
|
-
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"
|
|
344
410
|
|
|
345
411
|
return {
|
|
346
412
|
id: createFindingID(check, file, lines),
|
|
@@ -351,58 +417,65 @@ function parseFindings(payload: SlitherPayload): Finding[] {
|
|
|
351
417
|
file,
|
|
352
418
|
lines,
|
|
353
419
|
source: "slither",
|
|
354
|
-
}
|
|
355
|
-
})
|
|
420
|
+
}
|
|
421
|
+
})
|
|
356
422
|
}
|
|
357
423
|
|
|
358
424
|
export async function executeSlitherAnalyze(
|
|
359
425
|
args: SlitherArgs,
|
|
360
426
|
context: ToolContext,
|
|
361
|
-
runCommand: RunSlitherCommand = runSlitherCommand
|
|
427
|
+
runCommand: RunSlitherCommand = runSlitherCommand,
|
|
428
|
+
cwd?: string,
|
|
362
429
|
): Promise<SlitherAnalyzeResult> {
|
|
363
|
-
const
|
|
364
|
-
|
|
430
|
+
const projectDir = cwd ?? resolveProjectDir(context)
|
|
431
|
+
const startedAt = Date.now()
|
|
432
|
+
context.metadata({ title: `Slither analysis: ${args.target}` })
|
|
365
433
|
|
|
366
434
|
if (args.via_ir) {
|
|
367
435
|
const fallbackResult = await flattenFallback(args, context, {
|
|
368
436
|
...defaultFlattenDeps,
|
|
369
437
|
runCommand,
|
|
370
|
-
|
|
371
|
-
|
|
438
|
+
cwd: projectDir,
|
|
439
|
+
})
|
|
440
|
+
if (fallbackResult) return fallbackResult
|
|
372
441
|
return {
|
|
373
442
|
success: false,
|
|
374
443
|
findingsCount: 0,
|
|
375
444
|
findings: [],
|
|
376
445
|
executionTime: Date.now() - startedAt,
|
|
377
|
-
errors: [
|
|
378
|
-
|
|
379
|
-
|
|
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
|
+
}
|
|
380
452
|
}
|
|
381
453
|
|
|
382
|
-
const command = buildCommand(args)
|
|
454
|
+
const command = buildCommand(args)
|
|
383
455
|
|
|
384
456
|
try {
|
|
385
|
-
const runResult = await runCommand(command, context.abort)
|
|
386
|
-
const errors: string[] = []
|
|
457
|
+
const runResult = await runCommand(command, context.abort, projectDir)
|
|
458
|
+
const errors: string[] = []
|
|
387
459
|
|
|
388
460
|
if (runResult.exitCode !== 0) {
|
|
389
|
-
errors.push(`Slither exited with code ${runResult.exitCode}`)
|
|
461
|
+
errors.push(`Slither exited with code ${runResult.exitCode}`)
|
|
390
462
|
}
|
|
391
463
|
if (runResult.stderr.trim().length > 0) {
|
|
392
|
-
errors.push(runResult.stderr.trim())
|
|
464
|
+
errors.push(runResult.stderr.trim())
|
|
393
465
|
}
|
|
394
466
|
|
|
395
|
-
let payload: SlitherPayload
|
|
467
|
+
let payload: SlitherPayload
|
|
396
468
|
try {
|
|
397
|
-
payload = JSON.parse(runResult.stdout) as SlitherPayload
|
|
469
|
+
payload = JSON.parse(runResult.stdout) as SlitherPayload
|
|
398
470
|
} catch (error) {
|
|
399
|
-
const message = error instanceof Error ? error.message : "Unknown parse error"
|
|
471
|
+
const message = error instanceof Error ? error.message : "Unknown parse error"
|
|
400
472
|
if (shouldTryFlattenFallback(errors, runResult.stderr)) {
|
|
401
473
|
const fallbackResult = await flattenFallback(args, context, {
|
|
402
474
|
...defaultFlattenDeps,
|
|
403
475
|
runCommand,
|
|
404
|
-
|
|
405
|
-
|
|
476
|
+
cwd: projectDir,
|
|
477
|
+
})
|
|
478
|
+
if (fallbackResult) return fallbackResult
|
|
406
479
|
}
|
|
407
480
|
return {
|
|
408
481
|
success: false,
|
|
@@ -411,22 +484,23 @@ export async function executeSlitherAnalyze(
|
|
|
411
484
|
executionTime: Date.now() - startedAt,
|
|
412
485
|
errors,
|
|
413
486
|
error: `Slither output parse error: ${message}`,
|
|
414
|
-
}
|
|
487
|
+
}
|
|
415
488
|
}
|
|
416
489
|
|
|
417
490
|
if (payload.error) {
|
|
418
|
-
errors.push(payload.error)
|
|
491
|
+
errors.push(payload.error)
|
|
419
492
|
}
|
|
420
493
|
|
|
421
|
-
const findings = parseFindings(payload)
|
|
422
|
-
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)
|
|
423
496
|
|
|
424
497
|
if (!success && findings.length === 0 && shouldTryFlattenFallback(errors, runResult.stderr)) {
|
|
425
498
|
const fallbackResult = await flattenFallback(args, context, {
|
|
426
499
|
...defaultFlattenDeps,
|
|
427
500
|
runCommand,
|
|
428
|
-
|
|
429
|
-
|
|
501
|
+
cwd: projectDir,
|
|
502
|
+
})
|
|
503
|
+
if (fallbackResult) return fallbackResult
|
|
430
504
|
}
|
|
431
505
|
|
|
432
506
|
return {
|
|
@@ -435,10 +509,10 @@ export async function executeSlitherAnalyze(
|
|
|
435
509
|
findings,
|
|
436
510
|
executionTime: Date.now() - startedAt,
|
|
437
511
|
errors,
|
|
438
|
-
}
|
|
512
|
+
}
|
|
439
513
|
} catch (error) {
|
|
440
|
-
const message = error instanceof Error ? error.message : "Unknown error"
|
|
441
|
-
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 }
|
|
442
516
|
|
|
443
517
|
if (maybeErrno.code === "ENOENT") {
|
|
444
518
|
return {
|
|
@@ -448,7 +522,7 @@ export async function executeSlitherAnalyze(
|
|
|
448
522
|
executionTime: Date.now() - startedAt,
|
|
449
523
|
errors: [],
|
|
450
524
|
error: "Slither not found. Install with: pip install slither-analyzer",
|
|
451
|
-
}
|
|
525
|
+
}
|
|
452
526
|
}
|
|
453
527
|
|
|
454
528
|
if (maybeErrno.name === "AbortError" || context.abort.aborted) {
|
|
@@ -459,7 +533,7 @@ export async function executeSlitherAnalyze(
|
|
|
459
533
|
executionTime: Date.now() - startedAt,
|
|
460
534
|
errors: ["Slither analysis aborted"],
|
|
461
535
|
error: "Slither analysis aborted",
|
|
462
|
-
}
|
|
536
|
+
}
|
|
463
537
|
}
|
|
464
538
|
|
|
465
539
|
return {
|
|
@@ -469,34 +543,50 @@ export async function executeSlitherAnalyze(
|
|
|
469
543
|
executionTime: Date.now() - startedAt,
|
|
470
544
|
errors: [message],
|
|
471
545
|
error: message,
|
|
472
|
-
}
|
|
546
|
+
}
|
|
473
547
|
}
|
|
474
548
|
}
|
|
475
549
|
|
|
476
550
|
export function detectViaIr(target: string): boolean {
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
551
|
+
let dir = resolve(target.endsWith(".sol") ? dirname(target) : target)
|
|
552
|
+
const root = resolve("/")
|
|
553
|
+
|
|
554
|
+
while (true) {
|
|
555
|
+
const foundryTomlPath = join(dir, "foundry.toml")
|
|
556
|
+
if (existsSync(foundryTomlPath)) {
|
|
557
|
+
try {
|
|
558
|
+
const content = readFileSync(foundryTomlPath, "utf-8")
|
|
559
|
+
if (/^\s*via[_-]ir\s*=\s*true/m.test(content)) return true
|
|
560
|
+
} catch {
|
|
561
|
+
logger.debug("Unreadable foundry.toml, continuing directory walk")
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (dir === root) break
|
|
565
|
+
dir = dirname(dir)
|
|
485
566
|
}
|
|
567
|
+
|
|
568
|
+
return false
|
|
486
569
|
}
|
|
487
570
|
|
|
488
571
|
export const slitherTool = tool({
|
|
489
|
-
description:
|
|
490
|
-
"Run Slither static analysis and return normalized findings for Solidity targets.",
|
|
572
|
+
description: "Run Slither static analysis and return normalized findings for Solidity targets.",
|
|
491
573
|
args: {
|
|
492
574
|
target: tool.schema.string(),
|
|
493
575
|
detectors: tool.schema.array(tool.schema.string()).optional(),
|
|
494
576
|
exclude: tool.schema.array(tool.schema.string()).optional(),
|
|
495
577
|
solc_version: tool.schema.string().optional(),
|
|
578
|
+
via_ir: tool.schema.boolean().optional(),
|
|
496
579
|
},
|
|
497
580
|
async execute(args, context) {
|
|
498
|
-
const
|
|
499
|
-
const
|
|
500
|
-
|
|
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)
|
|
584
|
+
const result = await executeSlitherAnalyze(
|
|
585
|
+
{ ...args, target: resolvedTarget, via_ir: viaIr },
|
|
586
|
+
context,
|
|
587
|
+
runSlitherCommand,
|
|
588
|
+
projectDir,
|
|
589
|
+
)
|
|
590
|
+
return JSON.stringify(result)
|
|
501
591
|
},
|
|
502
|
-
})
|
|
592
|
+
})
|