solidity-argus 0.2.0 → 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 +93 -37
- package/package.json +33 -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 +24 -7
- package/src/agents/pythia-prompt.ts +3 -4
- package/src/agents/scribe-prompt.ts +7 -2
- package/src/agents/sentinel-prompt.ts +32 -3
- 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 +4 -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/tool-tracking-hook.ts +104 -50
- package/src/hooks/types.ts +2 -1
- package/src/index.ts +23 -36
- 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 +202 -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 +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 +185 -190
- package/src/tools/pattern-loader.ts +5 -111
- package/src/tools/proxy-detection-tool.ts +224 -0
- package/src/tools/report-generator-tool.ts +268 -200
- package/src/tools/slither-tool.ts +266 -218
- package/src/tools/solodit-search-tool.ts +216 -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 +103 -74
- 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,6 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: weird-tokens
|
|
3
3
|
description: Non-standard ERC20 behaviors, integration pitfalls, and token-handling safeguards.
|
|
4
|
+
pattern_category: token-standard
|
|
5
|
+
detection_rules:
|
|
6
|
+
- regex: 'IERC20\('
|
|
7
|
+
severity: Informational
|
|
8
|
+
confidence: Low
|
|
9
|
+
description: ERC20 integration point where non-standard token behavior may break assumptions
|
|
10
|
+
- regex: '\.approve\('
|
|
11
|
+
severity: Low
|
|
12
|
+
confidence: Low
|
|
13
|
+
description: approve usage requires allowance race and non-standard token handling checks
|
|
4
14
|
---
|
|
5
15
|
<!-- Source: DeFiFoFum/fofum-solidity-skills (MIT) -->
|
|
6
16
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zero-address-misconfiguration
|
|
3
|
+
description: "Critical addresses are set to address(0), causing hard reverts, fund loss paths, or permanently broken flows."
|
|
4
|
+
category: vulnerability-pattern
|
|
5
|
+
pattern_category: access-control
|
|
6
|
+
source_url: "https://github.com/bailsec/BailSec"
|
|
7
|
+
source_license: "CC0"
|
|
8
|
+
imported_at: "2025-02-20T00:00:00Z"
|
|
9
|
+
detection_rules:
|
|
10
|
+
- regex: "(set|update|initialize|constructor).*(address|receiver|collector|team).*=\\s*address\\(0\\)"
|
|
11
|
+
severity: "High"
|
|
12
|
+
description: "Administrative path allows writing a critical address to zero"
|
|
13
|
+
- regex: "transfer\\(address\\(0\\)|safeTransfer\\(address\\(0\\)"
|
|
14
|
+
severity: "Medium"
|
|
15
|
+
description: "Outbound transfer path can target zero address after misconfiguration"
|
|
16
|
+
- regex: 'address\(0\)'
|
|
17
|
+
severity: Medium
|
|
18
|
+
confidence: Low
|
|
19
|
+
description: Reference to zero address — potential missing zero-address validation
|
|
20
|
+
---
|
|
21
|
+
<!-- Source: BailSec audit reports (CC0) -->
|
|
22
|
+
|
|
23
|
+
# Zero Address Misconfiguration Vulnerabilities
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
Zero-address handling is an input validation and configuration integrity problem: critical system variables are set to `address(0)` even though downstream logic assumes a live recipient. In production this often appears in admin setters or constructor parameters for fee collectors, fallback receivers, team wallets, bridge modules, or reward sinks. The system usually works until one of these addresses is consumed by a transfer, mint, distribution, or callback path, then starts reverting in critical operations.
|
|
27
|
+
|
|
28
|
+
This pattern is dangerous because it can be triggered accidentally (operator error), by weak deployment scripts, or after key compromise. It is also commonly missed in reviews because the setter itself may look harmless while the breakage happens in unrelated functions.
|
|
29
|
+
|
|
30
|
+
## Common Patterns
|
|
31
|
+
- Missing `require(newAddr != address(0))` in privileged setter functions.
|
|
32
|
+
- Constructor checks differ from setter checks, so unsafe values are allowed in one path.
|
|
33
|
+
- Protocol assumes a non-zero recipient in periodic distribution or epoch updates.
|
|
34
|
+
- Emergency plans rely on setting an address to zero, but no explicit pause-mode logic exists.
|
|
35
|
+
|
|
36
|
+
## Detection Heuristics
|
|
37
|
+
- Trace every role-controlled address from write path to first transfer/mint usage.
|
|
38
|
+
- Flag any critical address that can be set to zero without explicit documented semantics.
|
|
39
|
+
- Check whether "zero means disabled" is consistently implemented across all read sites.
|
|
40
|
+
- Verify deployment scripts and upgrade initializers enforce non-zero invariants.
|
|
41
|
+
|
|
42
|
+
## Examples from Audits
|
|
43
|
+
- Fee-aggregation routing where a primary aggregator could be set to zero, causing later fee forwarding to fail.
|
|
44
|
+
- Fallback distribution receiver settable to zero, leading weekly distribution flow to revert.
|
|
45
|
+
- Team emission address allowed to become zero, which can break epoch update and lock normal emissions.
|
|
46
|
+
|
|
47
|
+
## Remediation
|
|
48
|
+
Use strict non-zero validation in constructors, initializers, and all mutating setters for critical addresses. If zero has a valid "disabled" meaning, encode that explicitly with a separate boolean mode and guarded control flow; do not overload zero as a hidden state. Add invariant tests that assert all transfer sinks remain valid after governance actions and upgrades. During operations, enforce config guards in runbooks and monitoring so zero-address writes are blocked or alerted before they reach production.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
export const ARGUS_PROMPT = `You are **Argus Panoptes**, the All-Seeing Guardian — an autonomous Solidity smart contract security auditor. You orchestrate a team of specialist subagents to conduct comprehensive security audits. Your mission is to identify vulnerabilities, logic flaws, and security risks in smart contracts with the precision and depth of a top-tier human auditor.
|
|
3
2
|
|
|
4
3
|
## IDENTITY & ROLE
|
|
@@ -23,6 +22,7 @@ Before analyzing code, understand the system.
|
|
|
23
22
|
- Determine the "crown jewels" (e.g., user funds, admin privileges).
|
|
24
23
|
- Map trust boundaries: Who is trusted? What external calls are made?
|
|
25
24
|
- Define the scope: Which contracts are in scope? Which are out of scope?
|
|
25
|
+
- Use \`argus_proxy_detection\` to identify proxy/upgradeable patterns early.
|
|
26
26
|
- **Key Questions**:
|
|
27
27
|
- What is the intended business logic?
|
|
28
28
|
- Who are the actors (users, admins, keepers)?
|
|
@@ -90,6 +90,8 @@ Prove the existence of vulnerabilities.
|
|
|
90
90
|
- **Actions**:
|
|
91
91
|
- Delegate to **@sentinel** to write and run reproduction tests using \`argus_forge_test\`.
|
|
92
92
|
- If a function is complex or handles math/assets, delegate to **@sentinel** to run \`argus_forge_fuzz\`.
|
|
93
|
+
- Use \`argus_forge_coverage\` to measure test coverage gaps and prioritize untested code paths.
|
|
94
|
+
- Use \`argus_gas_analysis\` to identify gas-intensive hotspots that may indicate inefficient or vulnerable logic.
|
|
93
95
|
- Verify that the fix (remediation) actually works.
|
|
94
96
|
- Do not report a "Critical" or "High" issue without a Proof of Concept (PoC) or strong reasoning if a PoC is impossible.
|
|
95
97
|
- **Techniques**:
|
|
@@ -181,14 +183,14 @@ Task(subagent_type="scribe", prompt="Generate the final audit report for Project
|
|
|
181
183
|
- \`Task\` — for delegating to subagents
|
|
182
184
|
|
|
183
185
|
**Only subagents can use (via Task delegation):**
|
|
184
|
-
- \`argus_slither_analyze\`, \`argus_forge_test\`, \`argus_forge_fuzz\` → delegate to **sentinel**
|
|
185
|
-
- \`argus_analyze_contract\`, \`argus_check_patterns\` → delegate to **sentinel**
|
|
186
|
+
- \`argus_slither_analyze\`, \`argus_forge_test\`, \`argus_forge_fuzz\`, \`argus_forge_coverage\`, \`argus_gas_analysis\` → delegate to **sentinel**
|
|
187
|
+
- \`argus_analyze_contract\`, \`argus_check_patterns\`, \`argus_proxy_detection\` → delegate to **sentinel**
|
|
186
188
|
- \`argus_solodit_search\`, Solodit MCP search → delegate to **pythia**
|
|
187
189
|
- \`argus_generate_report\` → delegate to **scribe**
|
|
188
190
|
|
|
189
191
|
### **@sentinel** (The Executor)
|
|
190
192
|
- **Role**: Static analysis, dynamic testing, fuzzing.
|
|
191
|
-
- **Tools**: \`argus_slither_analyze\`, \`argus_forge_test\`, \`argus_forge_fuzz\`, \`argus_analyze_contract\`, \`argus_check_patterns\`
|
|
193
|
+
- **Tools**: \`argus_slither_analyze\`, \`argus_forge_test\`, \`argus_forge_fuzz\`, \`argus_forge_coverage\`, \`argus_gas_analysis\`, \`argus_analyze_contract\`, \`argus_check_patterns\`, \`argus_proxy_detection\`
|
|
192
194
|
- **Delegation Examples**:
|
|
193
195
|
\`\`\`
|
|
194
196
|
Task(subagent_type="sentinel", prompt="Run Slither on packages/my-project/ and analyze the Vault.sol contract in detail. Report all findings with severity.")
|
|
@@ -267,9 +269,24 @@ Your subagents have access to these specialized tools. Know when to delegate eac
|
|
|
267
269
|
- **Purpose**: Updates the local vulnerability database (SCVD).
|
|
268
270
|
- **Note**: Run if you suspect your knowledge base is stale or if the tool reports it's offline.
|
|
269
271
|
|
|
272
|
+
- **\`argus_forge_coverage\`**:
|
|
273
|
+
- **Use**: During Testing & Verification.
|
|
274
|
+
- **Purpose**: Measures test coverage per file (lines, statements, branches, functions).
|
|
275
|
+
- **Note**: Use to identify untested code paths that may harbor hidden vulnerabilities. Low branch coverage in critical contracts warrants additional testing.
|
|
276
|
+
|
|
277
|
+
- **\`argus_proxy_detection\`**:
|
|
278
|
+
- **Use**: During Reconnaissance.
|
|
279
|
+
- **Purpose**: Detects proxy patterns (ERC1967, UUPS, transparent, beacon, diamond) with confidence scoring.
|
|
280
|
+
- **Note**: Run early to identify upgradeability risks. Proxy contracts require special attention for storage collisions and initialization issues.
|
|
281
|
+
|
|
282
|
+
- **\`argus_gas_analysis\`**:
|
|
283
|
+
- **Use**: During Testing & Verification.
|
|
284
|
+
- **Purpose**: Runs gas report analysis and identifies high-gas hotspots above configurable threshold.
|
|
285
|
+
- **Note**: Gas-intensive functions often indicate complex logic that may be vulnerable or cause DoS under certain conditions.
|
|
286
|
+
|
|
270
287
|
## SKILL SYSTEM
|
|
271
288
|
|
|
272
|
-
Instruct subagents to use \`argus_skill_load\` only when domain-specific context is needed. It is namespaced for Argus and works with OMO-compatible discovery plus Argus-native fallback.
|
|
289
|
+
Instruct subagents to use \`argus_skill_load\` only when domain-specific context is needed. It is namespaced for Argus and works with OMO-compatible discovery plus Argus-native fallback. The knowledge base includes 75+ curated SKILL.md files, 13 YAML pattern packs, and 15 real-world exploit case studies covering $3B+ in losses.
|
|
273
290
|
|
|
274
291
|
- **Curated skill map (load these first)**:
|
|
275
292
|
- **Reconnaissance**: \`amm-dex\`, \`lending-borrowing\`, \`bridges-cross-chain\`
|
|
@@ -420,8 +437,8 @@ You do NOT need to pass raw JSON or serialized audit state. Just pass your findi
|
|
|
420
437
|
**If you have zero findings, still invoke Scribe** with an empty findings list. A clean report is still a report.
|
|
421
438
|
|
|
422
439
|
You are the guardian. Nothing escapes your gaze. Begin the audit.
|
|
423
|
-
|
|
440
|
+
`
|
|
424
441
|
|
|
425
442
|
export function getArgusPrompt(): string {
|
|
426
|
-
return ARGUS_PROMPT
|
|
443
|
+
return ARGUS_PROMPT
|
|
427
444
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
export const PYTHIA_PROMPT = `You are **Pythia**, the Oracle — a specialized research subagent of Argus Panoptes. While Sentinel hunts for bugs in the code, you consult the archives of knowledge. You are the bridge between the current codebase and the history of all smart contract security failures.
|
|
3
2
|
|
|
4
3
|
## IDENTITY & ROLE
|
|
@@ -87,7 +86,7 @@ You have two primary tools. Master them.
|
|
|
87
86
|
|
|
88
87
|
## SKILLS SYSTEM
|
|
89
88
|
|
|
90
|
-
OpenCode has a powerful **Skills** system that allows you to load specialized knowledge modules.
|
|
89
|
+
OpenCode has a powerful **Skills** system that allows you to load specialized knowledge modules. The Argus knowledge base includes 75+ curated SKILL.md files, 13 YAML pattern packs, and 15 real-world exploit case studies covering $3B+ in losses.
|
|
91
90
|
|
|
92
91
|
**How to use**:
|
|
93
92
|
- Load a relevant skill before deep research when protocol context is non-trivial.
|
|
@@ -139,8 +138,8 @@ Report your findings to Argus using this Markdown structure. Focus on **Preceden
|
|
|
139
138
|
- **False Positives**: If \`argus_check_patterns\` returns noise, filter it out. Do not report false positives to Argus.
|
|
140
139
|
|
|
141
140
|
You are Pythia. The past is your map, and the code is the territory. Guide us to safety.
|
|
142
|
-
|
|
141
|
+
`
|
|
143
142
|
|
|
144
143
|
export function getPythiaPrompt(): string {
|
|
145
|
-
return PYTHIA_PROMPT
|
|
144
|
+
return PYTHIA_PROMPT
|
|
146
145
|
}
|
|
@@ -24,6 +24,11 @@ Your output must always follow this professional structure:
|
|
|
24
24
|
5. **Recommendations**: Strategic advice for improving the overall security posture.
|
|
25
25
|
6. **Appendix**: Tool execution logs or supplementary data.
|
|
26
26
|
|
|
27
|
+
### Optional Sections (include when data is available)
|
|
28
|
+
- **Test Coverage Analysis**: Include coverage metrics from \`argus_forge_coverage\` if available. Highlight files with low branch/statement coverage.
|
|
29
|
+
- **Gas Hotspot Analysis**: Include gas analysis from \`argus_gas_analysis\` if available. Flag functions exceeding gas thresholds.
|
|
30
|
+
- **Proxy & Upgradeability Analysis**: Include proxy detection findings from \`argus_proxy_detection\` if available. Document proxy patterns identified and associated risks.
|
|
31
|
+
|
|
27
32
|
## WRITING STYLE GUIDE
|
|
28
33
|
|
|
29
34
|
You must adhere to these strict writing standards:
|
|
@@ -92,8 +97,8 @@ Write the full report in Markdown. Use the standard finding format:
|
|
|
92
97
|
\`\`\`
|
|
93
98
|
|
|
94
99
|
You are Scribe. Your words define the security of the protocol. Write with precision.
|
|
95
|
-
|
|
100
|
+
`
|
|
96
101
|
|
|
97
102
|
export function getScribePrompt(): string {
|
|
98
|
-
return SCRIBE_PROMPT
|
|
103
|
+
return SCRIBE_PROMPT
|
|
99
104
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
export const SENTINEL_PROMPT = `You are **Sentinel**, the Tactical Guardian — a specialized subagent of Argus Panoptes. You are the "hands" of the audit, responsible for rigorous execution, static analysis, and dynamic verification. While Argus strategizes, you hunt.
|
|
3
2
|
|
|
4
3
|
## IDENTITY & ROLE
|
|
@@ -18,6 +17,7 @@ You operate in a loop of **Scan -> Analyze -> Verify**.
|
|
|
18
17
|
1. **Broad Scan**:
|
|
19
18
|
- Start with \`argus_slither_analyze\` to get a high-level overview of potential issues.
|
|
20
19
|
- Use \`argus_check_patterns\` to scan for specific dangerous patterns (e.g., read-only reentrancy).
|
|
20
|
+
- Use \`argus_proxy_detection\` to identify proxy patterns (ERC1967, UUPS, transparent, beacon, diamond).
|
|
21
21
|
|
|
22
22
|
2. **Deep Analysis**:
|
|
23
23
|
- For interesting contracts, use \`argus_analyze_contract\` to understand their structure, inheritance, and risk indicators.
|
|
@@ -27,6 +27,8 @@ You operate in a loop of **Scan -> Analyze -> Verify**.
|
|
|
27
27
|
- If you suspect a bug, write a reproduction test case.
|
|
28
28
|
- Use \`argus_forge_test\` to run this test.
|
|
29
29
|
- If the logic is complex (e.g., math, state transitions), use \`argus_forge_fuzz\` to hammer it with inputs.
|
|
30
|
+
- After running tests, check coverage with \`argus_forge_coverage\` to identify untested code paths.
|
|
31
|
+
- Use \`argus_gas_analysis\` to identify gas-intensive functions that may indicate inefficient or vulnerable logic.
|
|
30
32
|
|
|
31
33
|
4. **Reporting**:
|
|
32
34
|
- Format your findings strictly according to the Output Format section.
|
|
@@ -87,6 +89,33 @@ You have access to a specific set of tools. Use them effectively.
|
|
|
87
89
|
**Interpretation**:
|
|
88
90
|
- Look at the \`counterexamples\`. They tell you exactly what inputs broke the code.
|
|
89
91
|
|
|
92
|
+
### 6. \`argus_forge_coverage\`
|
|
93
|
+
**Purpose**: Measure test coverage to find untested code paths.
|
|
94
|
+
**When to use**: After running tests, to identify gaps in coverage.
|
|
95
|
+
**Arguments**:
|
|
96
|
+
- \`target\` (string): Path to the project directory (default ".").
|
|
97
|
+
**Interpretation**:
|
|
98
|
+
- Focus on low branch coverage in critical contracts (vaults, token transfers, access control).
|
|
99
|
+
- Untested code paths are prime candidates for hidden vulnerabilities.
|
|
100
|
+
|
|
101
|
+
### 7. \`argus_proxy_detection\`
|
|
102
|
+
**Purpose**: Detect proxy/upgradeable contract patterns.
|
|
103
|
+
**When to use**: During initial scanning to identify upgradeability risks early.
|
|
104
|
+
**Arguments**:
|
|
105
|
+
- \`file_path\` (string): Path to the .sol file to analyze.
|
|
106
|
+
**Interpretation**:
|
|
107
|
+
- Identifies ERC1967, UUPS, transparent, beacon, and diamond proxy patterns.
|
|
108
|
+
- Proxy contracts require special attention for storage collisions and initialization issues.
|
|
109
|
+
|
|
110
|
+
### 8. \`argus_gas_analysis\`
|
|
111
|
+
**Purpose**: Identify gas-intensive functions that may indicate complex or vulnerable logic.
|
|
112
|
+
**When to use**: During verification, to flag functions with abnormally high gas usage.
|
|
113
|
+
**Arguments**:
|
|
114
|
+
- \`target\` (string): Path to the project directory (default ".").
|
|
115
|
+
**Interpretation**:
|
|
116
|
+
- High gas consumption often correlates with complex logic, unbounded loops, or storage-heavy operations.
|
|
117
|
+
- Gas hotspots are prime candidates for DoS vulnerabilities.
|
|
118
|
+
|
|
90
119
|
## SKILL SYSTEM
|
|
91
120
|
|
|
92
121
|
Use \`argus_skill_load\` only when specialized context is needed before deep verification work.
|
|
@@ -139,8 +168,8 @@ Return your findings to Argus in this structured Markdown format. Do not deviate
|
|
|
139
168
|
- **Be Precise**: A vague finding is useless. Point to the line, the variable, the specific interaction.
|
|
140
169
|
|
|
141
170
|
You are the Sentinel. The code cannot hide its secrets from you.
|
|
142
|
-
|
|
171
|
+
`
|
|
143
172
|
|
|
144
173
|
export function getSentinelPrompt(): string {
|
|
145
|
-
return SENTINEL_PROMPT
|
|
174
|
+
return SENTINEL_PROMPT
|
|
146
175
|
}
|
package/src/cli/cli-program.ts
CHANGED
|
@@ -1,49 +1,52 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { cliOutput } from "./cli-output"
|
|
2
|
+
import { checkSkillsCommand } from "./commands/check-skills"
|
|
3
|
+
import { doctorCommand } from "./commands/doctor"
|
|
4
|
+
import { initCommand } from "./commands/init"
|
|
5
|
+
import { installCommand } from "./commands/install"
|
|
6
|
+
import { lintSkillsCommand } from "./commands/lint-skills"
|
|
7
|
+
import type { CliCommand } from "./types"
|
|
7
8
|
|
|
8
9
|
const HELP_TEXT = `argus — Solidity Security Auditor for OpenCode
|
|
9
10
|
|
|
10
11
|
Commands:
|
|
11
|
-
doctor
|
|
12
|
-
init
|
|
13
|
-
install
|
|
14
|
-
lint-skills
|
|
15
|
-
|
|
12
|
+
doctor Check Slither/Foundry installation and config health
|
|
13
|
+
init Create solidity-argus config file
|
|
14
|
+
install Configure argus plugin in opencode config
|
|
15
|
+
lint-skills Validate SKILL.md files against schema
|
|
16
|
+
check-skills Analyze skills for duplicates, near-duplicates, and conflicts
|
|
17
|
+
`
|
|
16
18
|
|
|
17
19
|
export class CliProgram {
|
|
18
|
-
private commands: Map<string, CliCommand> = new Map()
|
|
20
|
+
private commands: Map<string, CliCommand> = new Map()
|
|
19
21
|
|
|
20
22
|
registerCommand(command: CliCommand): void {
|
|
21
|
-
this.commands.set(command.name, command)
|
|
23
|
+
this.commands.set(command.name, command)
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
async dispatch(args: string[]): Promise<number> {
|
|
25
|
-
const subcommand = args[0]
|
|
27
|
+
const subcommand = args[0]
|
|
26
28
|
|
|
27
29
|
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
28
|
-
cliOutput.log(HELP_TEXT)
|
|
29
|
-
return 0
|
|
30
|
+
cliOutput.log(HELP_TEXT)
|
|
31
|
+
return 0
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
const command = this.commands.get(subcommand)
|
|
34
|
+
const command = this.commands.get(subcommand)
|
|
33
35
|
if (!command) {
|
|
34
|
-
cliOutput.error(`Unknown command '${subcommand}'. Run 'argus' for help.`)
|
|
35
|
-
return 1
|
|
36
|
+
cliOutput.error(`Unknown command '${subcommand}'. Run 'argus' for help.`)
|
|
37
|
+
return 1
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
return command.execute(args.slice(1))
|
|
40
|
+
return command.execute(args.slice(1))
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export function createCliProgram(): CliProgram {
|
|
43
|
-
const program = new CliProgram()
|
|
44
|
-
program.registerCommand(doctorCommand)
|
|
45
|
-
program.registerCommand(initCommand)
|
|
46
|
-
program.registerCommand(installCommand)
|
|
47
|
-
program.registerCommand(lintSkillsCommand)
|
|
48
|
-
|
|
45
|
+
const program = new CliProgram()
|
|
46
|
+
program.registerCommand(doctorCommand)
|
|
47
|
+
program.registerCommand(initCommand)
|
|
48
|
+
program.registerCommand(installCommand)
|
|
49
|
+
program.registerCommand(lintSkillsCommand)
|
|
50
|
+
program.registerCommand(checkSkillsCommand)
|
|
51
|
+
return program
|
|
49
52
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { loadArgusConfig } from "../../config/loader"
|
|
4
|
+
import { createLogger } from "../../shared/logger"
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_GATE_CONFIG,
|
|
7
|
+
formatReportJson,
|
|
8
|
+
formatReportText,
|
|
9
|
+
type GateConfig,
|
|
10
|
+
generateReport,
|
|
11
|
+
type SkillReport,
|
|
12
|
+
} from "../../skills/analysis/gates"
|
|
13
|
+
import { normalizeSkill, type SkillDoc } from "../../skills/analysis/normalize"
|
|
14
|
+
import { buildTfidfCorpus, computeAllPairs } from "../../skills/analysis/similarity"
|
|
15
|
+
import { resolveSkillRoots } from "../../skills/argus-skill-resolver"
|
|
16
|
+
import { cliOutput } from "../cli-output"
|
|
17
|
+
import type { CliCommand } from "../types"
|
|
18
|
+
|
|
19
|
+
const logger = createLogger()
|
|
20
|
+
|
|
21
|
+
function findSkillFiles(dir: string, maxDepth = 8): string[] {
|
|
22
|
+
const files: string[] = []
|
|
23
|
+
const stack: Array<{ path: string; depth: number }> = [{ path: dir, depth: 0 }]
|
|
24
|
+
|
|
25
|
+
while (stack.length > 0) {
|
|
26
|
+
const current = stack.pop()
|
|
27
|
+
if (!current || current.depth > maxDepth) continue
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const entries = readdirSync(current.path, { withFileTypes: true })
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const fullPath = join(current.path, entry.name)
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
stack.push({ path: fullPath, depth: current.depth + 1 })
|
|
35
|
+
} else if (entry.isFile() && entry.name.toUpperCase() === "SKILL.MD") {
|
|
36
|
+
files.push(fullPath)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} catch {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return files
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseFormatArg(args: string[]): "text" | "json" {
|
|
46
|
+
const formatIdx = args.indexOf("--format")
|
|
47
|
+
if (formatIdx !== -1 && args[formatIdx + 1] === "json") {
|
|
48
|
+
return "json"
|
|
49
|
+
}
|
|
50
|
+
return "text"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseThresholdArg(args: string[], flag: string, fallback: number): number {
|
|
54
|
+
const idx = args.indexOf(flag)
|
|
55
|
+
if (idx === -1) return fallback
|
|
56
|
+
const raw = args[idx + 1]
|
|
57
|
+
if (!raw) return fallback
|
|
58
|
+
const parsed = Number.parseFloat(raw)
|
|
59
|
+
return Number.isFinite(parsed) && parsed >= 0 && parsed <= 1 ? parsed : fallback
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function loadAndNormalizeSkills(cwd: string): SkillDoc[] {
|
|
63
|
+
let config: ReturnType<typeof loadArgusConfig> | undefined
|
|
64
|
+
try {
|
|
65
|
+
config = loadArgusConfig(cwd)
|
|
66
|
+
} catch {
|
|
67
|
+
logger.debug("Config load failed, using defaults")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const roots = resolveSkillRoots(cwd, config)
|
|
71
|
+
const docs: SkillDoc[] = []
|
|
72
|
+
|
|
73
|
+
for (const root of roots) {
|
|
74
|
+
const files = findSkillFiles(root.path)
|
|
75
|
+
for (const file of files) {
|
|
76
|
+
try {
|
|
77
|
+
const content = readFileSync(file, "utf8")
|
|
78
|
+
const doc = normalizeSkill(content)
|
|
79
|
+
if (doc) {
|
|
80
|
+
docs.push(doc)
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
logger.debug("Skipping unreadable skill file")
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return docs
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function runAnalysis(docs: SkillDoc[], config: GateConfig): SkillReport {
|
|
92
|
+
const corpus = buildTfidfCorpus(docs)
|
|
93
|
+
const pairs = computeAllPairs(docs, corpus)
|
|
94
|
+
return generateReport(docs, pairs, config)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const checkSkillsCommand: CliCommand = {
|
|
98
|
+
name: "check-skills",
|
|
99
|
+
description:
|
|
100
|
+
"Analyze SKILL.md files for duplicates, near-duplicates, and detection rule conflicts",
|
|
101
|
+
async execute(args: string[]): Promise<number> {
|
|
102
|
+
const cwd = process.cwd()
|
|
103
|
+
const format = parseFormatArg(args)
|
|
104
|
+
|
|
105
|
+
const gateConfig: GateConfig = {
|
|
106
|
+
blockThreshold: parseThresholdArg(
|
|
107
|
+
args,
|
|
108
|
+
"--block-threshold",
|
|
109
|
+
DEFAULT_GATE_CONFIG.blockThreshold,
|
|
110
|
+
),
|
|
111
|
+
warnThreshold: parseThresholdArg(args, "--warn-threshold", DEFAULT_GATE_CONFIG.warnThreshold),
|
|
112
|
+
infoThreshold: parseThresholdArg(args, "--info-threshold", DEFAULT_GATE_CONFIG.infoThreshold),
|
|
113
|
+
blockExactRegexConflict: !args.includes("--no-regex-conflict"),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const docs = loadAndNormalizeSkills(cwd)
|
|
117
|
+
|
|
118
|
+
if (docs.length === 0) {
|
|
119
|
+
cliOutput.log("No SKILL.md files found.")
|
|
120
|
+
return 0
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
cliOutput.log(`Analyzing ${docs.length} skills...`)
|
|
124
|
+
|
|
125
|
+
const report = runAnalysis(docs, gateConfig)
|
|
126
|
+
|
|
127
|
+
if (format === "json") {
|
|
128
|
+
cliOutput.log(formatReportJson(report))
|
|
129
|
+
} else {
|
|
130
|
+
cliOutput.log(formatReportText(report))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return report.summary.block > 0 ? 1 : 0
|
|
134
|
+
},
|
|
135
|
+
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { execSync } from "node:child_process"
|
|
2
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs"
|
|
3
2
|
import { basename, dirname, extname, join } from "node:path"
|
|
4
|
-
import type { CliCommand } from "../types"
|
|
5
|
-
import type { ArgusConfig } from "../../config/types"
|
|
6
3
|
import { loadArgusConfig } from "../../config/loader"
|
|
4
|
+
import type { ArgusConfig } from "../../config/types"
|
|
5
|
+
import { createLogger } from "../../shared/logger"
|
|
7
6
|
import {
|
|
8
7
|
getRequiredAuditSkills,
|
|
9
8
|
normalizeSkillName,
|
|
9
|
+
type ResolvedSkill,
|
|
10
10
|
resolveArgusSkills,
|
|
11
11
|
resolveSkillRoots,
|
|
12
|
-
type ResolvedSkill,
|
|
13
12
|
} from "../../skills/argus-skill-resolver"
|
|
14
13
|
import { parseFrontmatter, validateSkillFrontmatter } from "../../skills/skill-schema"
|
|
15
14
|
import { detectViaIr } from "../../tools/slither-tool"
|
|
16
15
|
import { checkSoloditHealth } from "../../utils/solodit-health"
|
|
17
16
|
import { cliOutput } from "../cli-output"
|
|
17
|
+
import type { CliCommand } from "../types"
|
|
18
|
+
|
|
19
|
+
const logger = createLogger()
|
|
18
20
|
|
|
19
21
|
const GREEN = "\x1b[32m"
|
|
20
22
|
const RED = "\x1b[31m"
|
|
@@ -23,13 +25,15 @@ const RESET = "\x1b[0m"
|
|
|
23
25
|
|
|
24
26
|
function checkBinary(name: string): { found: boolean; version: string | null } {
|
|
25
27
|
try {
|
|
26
|
-
const
|
|
28
|
+
const result = Bun.spawnSync([name, "--version"], {
|
|
29
|
+
stdout: "pipe",
|
|
30
|
+
stderr: "pipe",
|
|
27
31
|
timeout: 5000,
|
|
28
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
29
32
|
})
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
if (result.exitCode !== 0) {
|
|
34
|
+
return { found: false, version: null }
|
|
35
|
+
}
|
|
36
|
+
const version = new TextDecoder().decode(result.stdout).trim().split("\n")[0] ?? null
|
|
33
37
|
return { found: true, version }
|
|
34
38
|
} catch {
|
|
35
39
|
return { found: false, version: null }
|
|
@@ -70,7 +74,8 @@ export function findDuplicateSkills(
|
|
|
70
74
|
const nameToSources = new Map<string, Set<string>>()
|
|
71
75
|
for (const { name, source } of entries) {
|
|
72
76
|
if (!nameToSources.has(name)) nameToSources.set(name, new Set())
|
|
73
|
-
nameToSources.get(name)
|
|
77
|
+
const sources = nameToSources.get(name)
|
|
78
|
+
if (sources) sources.add(source)
|
|
74
79
|
}
|
|
75
80
|
return Array.from(nameToSources)
|
|
76
81
|
.filter(([, sources]) => sources.size > 1)
|
|
@@ -112,9 +117,7 @@ export function buildSkillHealthReport(
|
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
const duplicates = duplicateEntries ? findDuplicateSkills(duplicateEntries) : []
|
|
115
|
-
const missingCategories = REQUIRED_CATEGORIES.filter(
|
|
116
|
-
(cat) => (categoryBreakdown[cat] ?? 0) === 0,
|
|
117
|
-
)
|
|
120
|
+
const missingCategories = REQUIRED_CATEGORIES.filter((cat) => (categoryBreakdown[cat] ?? 0) === 0)
|
|
118
121
|
|
|
119
122
|
return {
|
|
120
123
|
categoryBreakdown,
|
|
@@ -146,6 +149,7 @@ function scanMarkdownFiles(dir: string, maxDepth = 8): string[] {
|
|
|
146
149
|
}
|
|
147
150
|
}
|
|
148
151
|
} catch {
|
|
152
|
+
logger.debug("Failed to read directory during skill scan")
|
|
149
153
|
}
|
|
150
154
|
}
|
|
151
155
|
return files
|
|
@@ -175,6 +179,7 @@ function collectAllSkillNames(
|
|
|
175
179
|
const name = normalizeSkillName(rawName)
|
|
176
180
|
if (name) entries.push({ name, source: root.source })
|
|
177
181
|
} catch {
|
|
182
|
+
logger.debug("Failed to parse skill file frontmatter")
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
}
|
|
@@ -184,7 +189,7 @@ function collectAllSkillNames(
|
|
|
184
189
|
export const doctorCommand: CliCommand = {
|
|
185
190
|
name: "doctor",
|
|
186
191
|
description: "Check tool dependencies and configuration",
|
|
187
|
-
async execute(
|
|
192
|
+
async execute(_args: string[]): Promise<number> {
|
|
188
193
|
const cwd = process.cwd()
|
|
189
194
|
let hasFailure = false
|
|
190
195
|
|
|
@@ -202,7 +207,9 @@ export const doctorCommand: CliCommand = {
|
|
|
202
207
|
if (forge.found) {
|
|
203
208
|
cliOutput.log(`${GREEN}✓${RESET} Forge: installed (${forge.version})`)
|
|
204
209
|
} else {
|
|
205
|
-
cliOutput.log(
|
|
210
|
+
cliOutput.log(
|
|
211
|
+
`${RED}✗${RESET} Forge: not found — curl -L https://foundry.paradigm.xyz | bash`,
|
|
212
|
+
)
|
|
206
213
|
hasFailure = true
|
|
207
214
|
}
|
|
208
215
|
|
|
@@ -210,7 +217,9 @@ export const doctorCommand: CliCommand = {
|
|
|
210
217
|
if (solcSelect.found) {
|
|
211
218
|
cliOutput.log(`${GREEN}✓${RESET} solc-select: installed (${solcSelect.version})`)
|
|
212
219
|
} else {
|
|
213
|
-
cliOutput.log(
|
|
220
|
+
cliOutput.log(
|
|
221
|
+
`${YELLOW}⚠${RESET} solc-select: not found — pipx install solc-select (needed for via_ir flatten fallback)`,
|
|
222
|
+
)
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
const projectType = checkSolidityProject(cwd)
|
|
@@ -221,9 +230,13 @@ export const doctorCommand: CliCommand = {
|
|
|
221
230
|
}
|
|
222
231
|
|
|
223
232
|
if (projectType === "foundry" && detectViaIr(cwd)) {
|
|
224
|
-
cliOutput.log(
|
|
233
|
+
cliOutput.log(
|
|
234
|
+
`${YELLOW}⚠${RESET} via_ir: enabled in foundry.toml — Slither will use flatten fallback`,
|
|
235
|
+
)
|
|
225
236
|
if (!forge.found) {
|
|
226
|
-
cliOutput.log(
|
|
237
|
+
cliOutput.log(
|
|
238
|
+
`${RED}✗${RESET} forge is required for via_ir flatten fallback but is missing`,
|
|
239
|
+
)
|
|
227
240
|
hasFailure = true
|
|
228
241
|
}
|
|
229
242
|
if (!solcSelect.found) {
|
|
@@ -241,9 +254,13 @@ export const doctorCommand: CliCommand = {
|
|
|
241
254
|
const missingSkills = requiredSkills.filter((skillName) => !resolvedSkills.has(skillName))
|
|
242
255
|
|
|
243
256
|
if (missingSkills.length === 0) {
|
|
244
|
-
cliOutput.log(
|
|
257
|
+
cliOutput.log(
|
|
258
|
+
`${GREEN}✓${RESET} Skills: required audit skills resolvable (${requiredSkills.join(", ")})`,
|
|
259
|
+
)
|
|
245
260
|
} else {
|
|
246
|
-
cliOutput.log(
|
|
261
|
+
cliOutput.log(
|
|
262
|
+
`${RED}✗${RESET} Skills: missing required skills (${missingSkills.join(", ")})`,
|
|
263
|
+
)
|
|
247
264
|
hasFailure = true
|
|
248
265
|
}
|
|
249
266
|
} catch {
|
|
@@ -254,15 +271,21 @@ export const doctorCommand: CliCommand = {
|
|
|
254
271
|
const missingSkills = requiredSkills.filter((skillName) => !resolvedSkills.has(skillName))
|
|
255
272
|
|
|
256
273
|
if (missingSkills.length === 0) {
|
|
257
|
-
cliOutput.log(
|
|
274
|
+
cliOutput.log(
|
|
275
|
+
`${GREEN}✓${RESET} Skills: required audit skills resolvable (${requiredSkills.join(", ")})`,
|
|
276
|
+
)
|
|
258
277
|
} else {
|
|
259
|
-
cliOutput.log(
|
|
278
|
+
cliOutput.log(
|
|
279
|
+
`${RED}✗${RESET} Skills: missing required skills (${missingSkills.join(", ")})`,
|
|
280
|
+
)
|
|
260
281
|
hasFailure = true
|
|
261
282
|
}
|
|
262
283
|
}
|
|
263
284
|
|
|
264
285
|
try {
|
|
265
|
-
const response = await fetch("https://api.scvd.dev/stats", {
|
|
286
|
+
const response = await fetch("https://api.scvd.dev/stats", {
|
|
287
|
+
signal: AbortSignal.timeout(5000),
|
|
288
|
+
})
|
|
266
289
|
if (response.ok) {
|
|
267
290
|
cliOutput.log(`${GREEN}✓${RESET} SCVD API: reachable`)
|
|
268
291
|
} else {
|
|
@@ -296,9 +319,7 @@ export const doctorCommand: CliCommand = {
|
|
|
296
319
|
const allEntries = collectAllSkillNames(cwd, config)
|
|
297
320
|
const report = buildSkillHealthReport(healthSkills, allEntries)
|
|
298
321
|
|
|
299
|
-
const catParts = ALL_CATEGORIES.map(
|
|
300
|
-
(cat) => `${cat}: ${report.categoryBreakdown[cat] ?? 0}`,
|
|
301
|
-
)
|
|
322
|
+
const catParts = ALL_CATEGORIES.map((cat) => `${cat}: ${report.categoryBreakdown[cat] ?? 0}`)
|
|
302
323
|
cliOutput.log(`${GREEN}✓${RESET} Categories: ${catParts.join(", ")}`)
|
|
303
324
|
|
|
304
325
|
const tierParts = Object.entries(report.trustTierBreakdown).map(
|
|
@@ -334,6 +355,7 @@ export const doctorCommand: CliCommand = {
|
|
|
334
355
|
}
|
|
335
356
|
} catch {
|
|
336
357
|
cliOutput.log(`${RED}✗${RESET} Could not analyze skill health`)
|
|
358
|
+
hasFailure = true
|
|
337
359
|
}
|
|
338
360
|
|
|
339
361
|
return hasFailure ? 1 : 0
|