solidity-argus 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/AGENTS.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +249 -0
  4. package/package.json +43 -0
  5. package/skills/INVENTORY.md +79 -0
  6. package/skills/README.md +56 -0
  7. package/skills/checklists/cyfrin-best-practices-runtime/SKILL.md +424 -0
  8. package/skills/checklists/cyfrin-best-practices-upgrades/SKILL.md +157 -0
  9. package/skills/checklists/cyfrin-defi-core/SKILL.md +373 -0
  10. package/skills/checklists/cyfrin-defi-integrations/SKILL.md +412 -0
  11. package/skills/checklists/cyfrin-gas/SKILL.md +55 -0
  12. package/skills/checklists/general-audit/SKILL.md +433 -0
  13. package/skills/methodology/audit-workflow/SKILL.md +129 -0
  14. package/skills/methodology/report-template/SKILL.md +190 -0
  15. package/skills/methodology/severity-classification/SKILL.md +179 -0
  16. package/skills/protocol-patterns/amm-dex/SKILL.md +229 -0
  17. package/skills/protocol-patterns/bridges-cross-chain/SKILL.md +317 -0
  18. package/skills/protocol-patterns/dao-governance/SKILL.md +281 -0
  19. package/skills/protocol-patterns/lending-borrowing/SKILL.md +221 -0
  20. package/skills/protocol-patterns/staking-vesting/SKILL.md +247 -0
  21. package/skills/references/exploit-reference/SKILL.md +259 -0
  22. package/skills/references/smartbugs-examples/SKILL.md +296 -0
  23. package/skills/vulnerability-patterns/access-control/SKILL.md +298 -0
  24. package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +59 -0
  25. package/skills/vulnerability-patterns/assert-violation/SKILL.md +59 -0
  26. package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +61 -0
  27. package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +55 -0
  28. package/skills/vulnerability-patterns/default-visibility/SKILL.md +62 -0
  29. package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +60 -0
  30. package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +59 -0
  31. package/skills/vulnerability-patterns/dos-revert/SKILL.md +72 -0
  32. package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +249 -0
  33. package/skills/vulnerability-patterns/floating-pragma/SKILL.md +51 -0
  34. package/skills/vulnerability-patterns/hash-collision/SKILL.md +52 -0
  35. package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +61 -0
  36. package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +60 -0
  37. package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +59 -0
  38. package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +61 -0
  39. package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +61 -0
  40. package/skills/vulnerability-patterns/logic-errors/SKILL.md +333 -0
  41. package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +60 -0
  42. package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +66 -0
  43. package/skills/vulnerability-patterns/off-by-one/SKILL.md +67 -0
  44. package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +252 -0
  45. package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +65 -0
  46. package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +61 -0
  47. package/skills/vulnerability-patterns/reentrancy/SKILL.md +266 -0
  48. package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +72 -0
  49. package/skills/vulnerability-patterns/signature-malleability/SKILL.md +59 -0
  50. package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +63 -0
  51. package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +52 -0
  52. package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +65 -0
  53. package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +61 -0
  54. package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +63 -0
  55. package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +56 -0
  56. package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +80 -0
  57. package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +69 -0
  58. package/skills/vulnerability-patterns/unused-variables/SKILL.md +70 -0
  59. package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +81 -0
  60. package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +77 -0
  61. package/skills/vulnerability-patterns/weird-tokens/SKILL.md +294 -0
  62. package/src/agents/argus-prompt.ts +407 -0
  63. package/src/agents/pythia-prompt.ts +134 -0
  64. package/src/agents/scribe-prompt.ts +87 -0
  65. package/src/agents/sentinel-prompt.ts +133 -0
  66. package/src/cli/cli-program.ts +67 -0
  67. package/src/cli/commands/doctor.ts +83 -0
  68. package/src/cli/commands/init.ts +46 -0
  69. package/src/cli/commands/install.ts +55 -0
  70. package/src/cli/index.ts +13 -0
  71. package/src/cli/tui-prompts.ts +75 -0
  72. package/src/cli/types.ts +9 -0
  73. package/src/config/index.ts +3 -0
  74. package/src/config/loader.ts +36 -0
  75. package/src/config/schema.ts +82 -0
  76. package/src/config/types.ts +4 -0
  77. package/src/constants/defaults.ts +6 -0
  78. package/src/create-hooks.ts +84 -0
  79. package/src/create-managers.ts +26 -0
  80. package/src/create-tools.ts +30 -0
  81. package/src/features/audit-enforcer/audit-enforcer.ts +34 -0
  82. package/src/features/audit-enforcer/index.ts +1 -0
  83. package/src/features/background-agent/background-manager.ts +200 -0
  84. package/src/features/background-agent/index.ts +1 -0
  85. package/src/features/context-monitor/context-monitor.ts +48 -0
  86. package/src/features/context-monitor/index.ts +4 -0
  87. package/src/features/context-monitor/tool-output-truncator.ts +17 -0
  88. package/src/features/error-recovery/index.ts +2 -0
  89. package/src/features/error-recovery/session-recovery.ts +27 -0
  90. package/src/features/error-recovery/tool-error-recovery.ts +35 -0
  91. package/src/features/index.ts +5 -0
  92. package/src/features/persistent-state/audit-state-manager.ts +121 -0
  93. package/src/features/persistent-state/index.ts +1 -0
  94. package/src/hooks/compaction-hook.ts +50 -0
  95. package/src/hooks/config-handler.ts +116 -0
  96. package/src/hooks/event-hook-v2.ts +93 -0
  97. package/src/hooks/event-hook.ts +74 -0
  98. package/src/hooks/hook-system.ts +9 -0
  99. package/src/hooks/index.ts +5 -0
  100. package/src/hooks/knowledge-sync-hook.ts +57 -0
  101. package/src/hooks/safe-create-hook.ts +15 -0
  102. package/src/hooks/system-prompt-hook.ts +126 -0
  103. package/src/hooks/tool-tracking-hook.ts +234 -0
  104. package/src/hooks/types.ts +16 -0
  105. package/src/index.ts +36 -0
  106. package/src/knowledge/scvd-client.ts +242 -0
  107. package/src/knowledge/scvd-index.ts +183 -0
  108. package/src/knowledge/scvd-sync.ts +85 -0
  109. package/src/managers/index.ts +1 -0
  110. package/src/managers/types.ts +85 -0
  111. package/src/plugin-interface.ts +38 -0
  112. package/src/shared/binary-utils.ts +63 -0
  113. package/src/shared/deep-merge.ts +71 -0
  114. package/src/shared/file-utils.ts +56 -0
  115. package/src/shared/index.ts +5 -0
  116. package/src/shared/jsonc-parser.ts +39 -0
  117. package/src/shared/logger.ts +36 -0
  118. package/src/state/audit-state.ts +27 -0
  119. package/src/state/finding-store.ts +126 -0
  120. package/src/state/plugin-state.ts +14 -0
  121. package/src/state/types.ts +61 -0
  122. package/src/tools/contract-analyzer-tool.ts +184 -0
  123. package/src/tools/forge-fuzz-tool.ts +311 -0
  124. package/src/tools/forge-test-tool.ts +397 -0
  125. package/src/tools/pattern-checker-tool.ts +337 -0
  126. package/src/tools/report-generator-tool.ts +308 -0
  127. package/src/tools/slither-tool.ts +465 -0
  128. package/src/tools/solodit-search-tool.ts +131 -0
  129. package/src/tools/sync-knowledge-tool.ts +116 -0
  130. package/src/utils/project-detector.ts +133 -0
  131. package/src/utils/solidity-parser.ts +174 -0
@@ -0,0 +1,133 @@
1
+ import { existsSync } from "fs";
2
+ import { join, resolve } from "path";
3
+
4
+ export interface ProjectConfig {
5
+ type: "foundry" | "hardhat" | "mixed" | "unknown";
6
+ srcDir: string;
7
+ testDir: string;
8
+ solcVersion?: string;
9
+ remappings: string[];
10
+ rootDir: string;
11
+ }
12
+
13
+ /**
14
+ * Detects the Solidity framework (Foundry/Hardhat) from config files
15
+ * @param dir Directory to scan for config files
16
+ * @returns ProjectConfig with detected framework type and settings
17
+ */
18
+ export async function detectProject(dir: string): Promise<ProjectConfig> {
19
+ const rootDir = resolve(dir);
20
+ const foundryTomlPath = join(rootDir, "foundry.toml");
21
+ const hardhatConfigTsPath = join(rootDir, "hardhat.config.ts");
22
+ const hardhatConfigJsPath = join(rootDir, "hardhat.config.js");
23
+
24
+ const hasFoundry = existsSync(foundryTomlPath);
25
+ const hasHardhatTs = existsSync(hardhatConfigTsPath);
26
+ const hasHardhatJs = existsSync(hardhatConfigJsPath);
27
+ const hasHardhat = hasHardhatTs || hasHardhatJs;
28
+
29
+ // Determine project type
30
+ let type: "foundry" | "hardhat" | "mixed" | "unknown";
31
+ if (hasFoundry && hasHardhat) {
32
+ type = "mixed";
33
+ } else if (hasFoundry) {
34
+ type = "foundry";
35
+ } else if (hasHardhat) {
36
+ type = "hardhat";
37
+ } else {
38
+ type = "unknown";
39
+ }
40
+
41
+ // Default values
42
+ let srcDir = "src";
43
+ let testDir = "test";
44
+ let solcVersion: string | undefined;
45
+ let remappings: string[] = [];
46
+
47
+ // Parse Foundry config if present
48
+ if (hasFoundry) {
49
+ const foundryConfig = await parseFoundryToml(foundryTomlPath);
50
+ srcDir = foundryConfig.srcDir || srcDir;
51
+ testDir = foundryConfig.testDir || testDir;
52
+ solcVersion = foundryConfig.solcVersion;
53
+ remappings = foundryConfig.remappings;
54
+ }
55
+
56
+ // Set Hardhat defaults if it's a Hardhat project
57
+ if (hasHardhat && !hasFoundry) {
58
+ srcDir = "contracts";
59
+ }
60
+
61
+ return {
62
+ type,
63
+ srcDir,
64
+ testDir,
65
+ solcVersion,
66
+ remappings,
67
+ rootDir,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Parses foundry.toml file using regex-based parsing
73
+ */
74
+ async function parseFoundryToml(
75
+ filePath: string
76
+ ): Promise<{
77
+ srcDir?: string;
78
+ testDir?: string;
79
+ solcVersion?: string;
80
+ remappings: string[];
81
+ }> {
82
+ const content = await Bun.file(filePath).text();
83
+
84
+ const result = {
85
+ srcDir: undefined as string | undefined,
86
+ testDir: undefined as string | undefined,
87
+ solcVersion: undefined as string | undefined,
88
+ remappings: [] as string[],
89
+ };
90
+
91
+ // Extract [profile.default] section - stop at next section or EOF
92
+ const profileDefaultMatch = content.match(
93
+ /\[profile\.default\]([\s\S]*?)(?:\n\[|$)/
94
+ );
95
+ if (!profileDefaultMatch || !profileDefaultMatch[1]) {
96
+ return result;
97
+ }
98
+
99
+ const profileSection = profileDefaultMatch[1];
100
+
101
+ // Parse src = "..."
102
+ const srcMatch = profileSection.match(/^\s*src\s*=\s*["']([^"']+)["']/m);
103
+ if (srcMatch && srcMatch[1]) {
104
+ result.srcDir = srcMatch[1];
105
+ }
106
+
107
+ // Parse test = "..."
108
+ const testMatch = profileSection.match(/^\s*test\s*=\s*["']([^"']+)["']/m);
109
+ if (testMatch && testMatch[1]) {
110
+ result.testDir = testMatch[1];
111
+ }
112
+
113
+ // Parse solc = "..."
114
+ const solcMatch = profileSection.match(/^\s*solc\s*=\s*["']([^"']+)["']/m);
115
+ if (solcMatch && solcMatch[1]) {
116
+ result.solcVersion = solcMatch[1];
117
+ }
118
+
119
+ // Parse remappings array - handles both single line and multiline
120
+ const remappingsMatch = profileSection.match(
121
+ /remappings\s*=\s*\[([\s\S]*?)\]/
122
+ );
123
+ if (remappingsMatch && remappingsMatch[1]) {
124
+ const remappingsContent = remappingsMatch[1];
125
+ // Extract quoted strings from the array
126
+ const remappingMatches = remappingsContent.match(/["']([^"']+)["']/g);
127
+ if (remappingMatches) {
128
+ result.remappings = remappingMatches.map((m) => m.slice(1, -1));
129
+ }
130
+ }
131
+
132
+ return result;
133
+ }
@@ -0,0 +1,174 @@
1
+ import type { ContractProfile } from "../state/types";
2
+
3
+ interface ABIFunction {
4
+ type: string;
5
+ name: string;
6
+ inputs?: Array<{ name: string; type: string }>;
7
+ outputs?: Array<{ name: string; type: string }>;
8
+ stateMutability?: string;
9
+ }
10
+
11
+ interface StorageLayoutItem {
12
+ label: string;
13
+ type: string;
14
+ slot: string;
15
+ }
16
+
17
+ interface StorageLayout {
18
+ storage: StorageLayoutItem[];
19
+ types: Record<string, { label: string }>;
20
+ }
21
+
22
+ /**
23
+ * Extract contract information using forge inspect
24
+ * Runs forge inspect <contractName> abi and storage-layout
25
+ * Parses ABI to extract functions and state variables
26
+ * Detects access control patterns
27
+ */
28
+ export async function extractContractInfo(
29
+ contractName: string,
30
+ projectDir: string
31
+ ): Promise<ContractProfile> {
32
+ const result: ContractProfile = {
33
+ name: contractName,
34
+ filePath: "",
35
+ functions: [],
36
+ stateVars: [],
37
+ inheritance: [],
38
+ accessControlPattern: "none",
39
+ externalCalls: [],
40
+ riskIndicators: [],
41
+ };
42
+
43
+ try {
44
+ // Run forge inspect abi
45
+ const abiResult = Bun.spawnSync(
46
+ ["forge", "inspect", contractName, "abi"],
47
+ {
48
+ cwd: projectDir,
49
+ stdout: "pipe",
50
+ stderr: "pipe",
51
+ }
52
+ );
53
+
54
+ if (!abiResult.success) {
55
+ const errorMsg = abiResult.stderr?.toString() || "Unknown error";
56
+ result.error = `Failed to inspect ABI: ${errorMsg}`;
57
+ return result;
58
+ }
59
+
60
+ // Run forge inspect storage-layout
61
+ const storageResult = Bun.spawnSync(
62
+ ["forge", "inspect", contractName, "storage-layout"],
63
+ {
64
+ cwd: projectDir,
65
+ stdout: "pipe",
66
+ stderr: "pipe",
67
+ }
68
+ );
69
+
70
+ if (!storageResult.success) {
71
+ const errorMsg = storageResult.stderr?.toString() || "Unknown error";
72
+ result.error = `Failed to inspect storage layout: ${errorMsg}`;
73
+ return result;
74
+ }
75
+
76
+ // Parse ABI
77
+ const abiOutput = abiResult.stdout?.toString() || "[]";
78
+ let abi: ABIFunction[] = [];
79
+ try {
80
+ abi = JSON.parse(abiOutput);
81
+ } catch (e) {
82
+ result.error = `Failed to parse ABI JSON: ${e instanceof Error ? e.message : "Unknown error"}`;
83
+ return result;
84
+ }
85
+
86
+ // Parse storage layout
87
+ const storageOutput = storageResult.stdout?.toString() || "{}";
88
+ let storageLayout: StorageLayout = { storage: [], types: {} };
89
+ try {
90
+ storageLayout = JSON.parse(storageOutput);
91
+ } catch (e) {
92
+ result.error = `Failed to parse storage layout JSON: ${e instanceof Error ? e.message : "Unknown error"}`;
93
+ return result;
94
+ }
95
+
96
+ // Extract functions from ABI
97
+ const functions = abi.filter((item) => item.type === "function");
98
+ result.functions = functions.map((func) => ({
99
+ name: func.name || "",
100
+ visibility: mapStateMutabilityToVisibility(func.stateMutability || "nonpayable"),
101
+ mutability: func.stateMutability || "nonpayable",
102
+ modifiers: [],
103
+ }));
104
+
105
+ // Extract state variables from storage layout
106
+ result.stateVars = storageLayout.storage.map((item) => {
107
+ const typeInfo = storageLayout.types[item.type];
108
+ const typeLabel = typeInfo?.label || item.type;
109
+
110
+ return {
111
+ name: item.label,
112
+ type: typeLabel,
113
+ visibility: "internal", // Default visibility for storage vars
114
+ };
115
+ });
116
+
117
+ // Detect access control pattern
118
+ result.accessControlPattern = detectAccessControlPattern(result.functions);
119
+
120
+ return result;
121
+ } catch (e) {
122
+ result.error = `Unexpected error: ${e instanceof Error ? e.message : "Unknown error"}`;
123
+ return result;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Map Solidity stateMutability to visibility
129
+ * ABI doesn't directly specify visibility, so we infer from mutability
130
+ */
131
+ function mapStateMutabilityToVisibility(
132
+ stateMutability: string
133
+ ): string {
134
+ switch (stateMutability) {
135
+ case "pure":
136
+ case "view":
137
+ return "view";
138
+ case "payable":
139
+ case "nonpayable":
140
+ return "external";
141
+ default:
142
+ return "external";
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Detect access control pattern from function names and signatures
148
+ */
149
+ function detectAccessControlPattern(
150
+ functions: Array<{ name: string; visibility: string; mutability: string; modifiers: string[] }>
151
+ ): "ownable" | "access-control" | "custom" | "none" {
152
+ const functionNames = functions.map((f) => f.name.toLowerCase());
153
+
154
+ // Check for Ownable pattern
155
+ if (functionNames.includes("owner") || functionNames.includes("transferownership")) {
156
+ return "ownable";
157
+ }
158
+
159
+ // Check for AccessControl pattern (OpenZeppelin)
160
+ if (functionNames.includes("hasrole") || functionNames.includes("grantrole")) {
161
+ return "access-control";
162
+ }
163
+
164
+ // Check for custom access control patterns
165
+ if (
166
+ functionNames.some((name) =>
167
+ name.includes("onlyadmin") || name.includes("requireadmin")
168
+ )
169
+ ) {
170
+ return "custom";
171
+ }
172
+
173
+ return "none";
174
+ }