solidity-argus 0.3.7 → 0.5.7

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 (108) hide show
  1. package/AGENTS.md +13 -6
  2. package/README.md +24 -12
  3. package/package.json +7 -3
  4. package/skills/checklists/cyfrin-best-practices-runtime/SKILL.md +1 -0
  5. package/skills/checklists/cyfrin-best-practices-upgrades/SKILL.md +1 -0
  6. package/skills/checklists/cyfrin-defi-core/SKILL.md +1 -0
  7. package/skills/checklists/cyfrin-defi-integrations/SKILL.md +1 -0
  8. package/skills/checklists/cyfrin-gas/SKILL.md +1 -0
  9. package/skills/checklists/general-audit/SKILL.md +1 -0
  10. package/skills/methodology/audit-workflow/SKILL.md +1 -0
  11. package/skills/methodology/report-template/SKILL.md +1 -0
  12. package/skills/methodology/severity-classification/SKILL.md +1 -0
  13. package/skills/protocol-patterns/amm-dex/SKILL.md +1 -0
  14. package/skills/protocol-patterns/bridges-cross-chain/SKILL.md +1 -0
  15. package/skills/protocol-patterns/dao-governance/SKILL.md +1 -0
  16. package/skills/protocol-patterns/lending-borrowing/SKILL.md +1 -0
  17. package/skills/protocol-patterns/staking-vesting/SKILL.md +1 -0
  18. package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +0 -50
  19. package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +0 -63
  20. package/src/agents/argus-prompt.ts +98 -33
  21. package/src/agents/pythia-prompt.ts +24 -2
  22. package/src/agents/scribe-prompt.ts +34 -10
  23. package/src/agents/sentinel-prompt.ts +19 -0
  24. package/src/agents/themis-prompt.ts +110 -0
  25. package/src/cli/commands/doctor.ts +29 -17
  26. package/src/cli/commands/install.ts +74 -33
  27. package/src/config/loader.ts +29 -5
  28. package/src/config/schema.ts +45 -45
  29. package/src/constants/defaults.ts +1 -0
  30. package/src/create-hooks.ts +806 -173
  31. package/src/create-managers.ts +4 -2
  32. package/src/create-tools.ts +5 -1
  33. package/src/features/audit-enforcer/audit-enforcer.ts +1 -11
  34. package/src/features/background-agent/background-manager.ts +32 -5
  35. package/src/features/error-recovery/tool-error-recovery.ts +1 -0
  36. package/src/features/persistent-state/audit-state-manager.ts +272 -29
  37. package/src/features/persistent-state/event-sink.ts +96 -25
  38. package/src/features/persistent-state/findings-materializer.ts +68 -2
  39. package/src/features/persistent-state/global-run-index.ts +86 -8
  40. package/src/features/persistent-state/index.ts +7 -1
  41. package/src/features/persistent-state/run-finalizer.ts +116 -7
  42. package/src/features/persistent-state/run-pruner.ts +93 -0
  43. package/src/hooks/agent-tracker.ts +14 -2
  44. package/src/hooks/compaction-hook.ts +7 -16
  45. package/src/hooks/config-handler.ts +83 -29
  46. package/src/hooks/context-budget.ts +4 -5
  47. package/src/hooks/event-hook.ts +213 -57
  48. package/src/hooks/knowledge-sync-hook.ts +2 -3
  49. package/src/hooks/safe-create-hook.ts +13 -1
  50. package/src/hooks/system-prompt-hook.ts +20 -39
  51. package/src/hooks/tool-tracking-hook.ts +602 -323
  52. package/src/index.ts +15 -1
  53. package/src/knowledge/scvd-client.ts +2 -4
  54. package/src/knowledge/scvd-errors.ts +25 -2
  55. package/src/knowledge/scvd-index.ts +7 -5
  56. package/src/knowledge/scvd-sync.ts +6 -6
  57. package/src/managers/types.ts +20 -2
  58. package/src/shared/agent-names.ts +23 -0
  59. package/src/shared/audit-artifact-resolver.ts +8 -3
  60. package/src/shared/audit-phases.ts +12 -0
  61. package/src/shared/cache-paths.ts +41 -0
  62. package/src/shared/drop-diagnostics.ts +2 -2
  63. package/src/shared/forge-errors.ts +31 -0
  64. package/src/shared/forge-runner.ts +30 -0
  65. package/src/shared/format-error.ts +3 -0
  66. package/src/shared/index.ts +9 -0
  67. package/src/shared/key-tools.ts +39 -0
  68. package/src/shared/logger.ts +7 -7
  69. package/src/shared/path-containment.ts +25 -0
  70. package/src/shared/path-utils.ts +11 -0
  71. package/src/shared/report-path-resolver.ts +4 -2
  72. package/src/shared/safe-emit.ts +24 -0
  73. package/src/shared/token-utils.ts +5 -0
  74. package/src/shared/type-guards.ts +8 -0
  75. package/src/shared/validation-constants.ts +52 -0
  76. package/src/skills/analysis/cluster.ts +1 -114
  77. package/src/skills/analysis/normalize.ts +2 -114
  78. package/src/skills/analysis/stopwords.ts +109 -0
  79. package/src/skills/argus-skill-resolver.ts +6 -3
  80. package/src/solodit-lifecycle.ts +153 -37
  81. package/src/state/adapters.ts +60 -66
  82. package/src/state/finding-aggregation.ts +6 -8
  83. package/src/state/finding-fingerprint.ts +1 -1
  84. package/src/state/finding-store.ts +31 -9
  85. package/src/state/index.ts +1 -1
  86. package/src/state/projectors.ts +27 -19
  87. package/src/state/schemas.ts +8 -32
  88. package/src/state/types.ts +3 -0
  89. package/src/tools/contract-analyzer-tool.ts +4 -6
  90. package/src/tools/forge-coverage-tool.ts +10 -35
  91. package/src/tools/forge-fuzz-tool.ts +21 -51
  92. package/src/tools/forge-test-tool.ts +25 -47
  93. package/src/tools/gas-analysis-tool.ts +12 -41
  94. package/src/tools/pattern-checker-tool.ts +37 -15
  95. package/src/tools/pattern-loader.ts +18 -4
  96. package/src/tools/persist-deduped-tool.ts +94 -0
  97. package/src/tools/proxy-detection-tool.ts +35 -34
  98. package/src/tools/read-findings-tool.ts +390 -0
  99. package/src/tools/record-finding-tool.ts +130 -25
  100. package/src/tools/report-generator-tool.ts +475 -327
  101. package/src/tools/report-preflight.ts +5 -1
  102. package/src/tools/slither-tool.ts +55 -16
  103. package/src/tools/solodit-search-tool.ts +260 -112
  104. package/src/tools/sync-knowledge-tool.ts +2 -3
  105. package/src/utils/solidity-parser.ts +39 -24
  106. package/src/features/migration/index.ts +0 -14
  107. package/src/features/migration/migration-adapter.ts +0 -151
  108. package/src/features/migration/parity-telemetry.ts +0 -133
@@ -1,6 +1,6 @@
1
1
  import { tokenJaccard } from "./similarity"
2
+ import { STOPWORDS } from "./stopwords"
2
3
 
3
- /** Input finding from the PDF extraction pipeline */
4
4
  export interface ClusterFinding {
5
5
  title: string
6
6
  severity: string
@@ -10,7 +10,6 @@ export interface ClusterFinding {
10
10
  source_name?: string
11
11
  }
12
12
 
13
- /** A single cluster of related findings */
14
13
  export interface FindingCluster {
15
14
  id: number
16
15
  category: string
@@ -22,14 +21,12 @@ export interface FindingCluster {
22
21
  size: number
23
22
  }
24
23
 
25
- /** Configuration for clustering */
26
24
  export interface ClusterConfig {
27
25
  linkThreshold: number
28
26
  cohesionMinSimilarity: number
29
27
  minClusterSize: number
30
28
  }
31
29
 
32
- /** Full clustering result */
33
30
  export interface ClusterResult {
34
31
  clusters: FindingCluster[]
35
32
  singletons: ClusterFinding[]
@@ -49,116 +46,6 @@ export const DEFAULT_CLUSTER_CONFIG: ClusterConfig = {
49
46
  minClusterSize: 2,
50
47
  }
51
48
 
52
- const STOPWORDS = new Set([
53
- "the",
54
- "a",
55
- "an",
56
- "is",
57
- "are",
58
- "was",
59
- "were",
60
- "be",
61
- "been",
62
- "being",
63
- "have",
64
- "has",
65
- "had",
66
- "do",
67
- "does",
68
- "did",
69
- "will",
70
- "would",
71
- "shall",
72
- "should",
73
- "may",
74
- "might",
75
- "can",
76
- "could",
77
- "of",
78
- "in",
79
- "to",
80
- "for",
81
- "with",
82
- "on",
83
- "at",
84
- "by",
85
- "from",
86
- "as",
87
- "into",
88
- "through",
89
- "during",
90
- "before",
91
- "after",
92
- "above",
93
- "below",
94
- "between",
95
- "out",
96
- "off",
97
- "over",
98
- "under",
99
- "again",
100
- "further",
101
- "then",
102
- "once",
103
- "here",
104
- "there",
105
- "where",
106
- "when",
107
- "how",
108
- "all",
109
- "each",
110
- "every",
111
- "both",
112
- "few",
113
- "more",
114
- "most",
115
- "other",
116
- "some",
117
- "such",
118
- "no",
119
- "nor",
120
- "not",
121
- "only",
122
- "own",
123
- "same",
124
- "than",
125
- "too",
126
- "very",
127
- "and",
128
- "but",
129
- "or",
130
- "if",
131
- "this",
132
- "that",
133
- "these",
134
- "those",
135
- "it",
136
- "its",
137
- "contract",
138
- "function",
139
- "solidity",
140
- "smart",
141
- "vulnerability",
142
- "attack",
143
- "attacker",
144
- "token",
145
- "address",
146
- "value",
147
- "state",
148
- "require",
149
- "modifier",
150
- "external",
151
- "internal",
152
- "public",
153
- "private",
154
- "mapping",
155
- "uint256",
156
- "bool",
157
- "returns",
158
- "event",
159
- "emit",
160
- ])
161
-
162
49
  class UnionFind {
163
50
  parent: number[]
164
51
  rank: number[]
@@ -1,4 +1,6 @@
1
+ import { isRecord } from "../../shared/type-guards"
1
2
  import { parseFrontmatter } from "../skill-schema"
3
+ import { STOPWORDS } from "./stopwords"
2
4
 
3
5
  export interface SkillDoc {
4
6
  name: string
@@ -11,116 +13,6 @@ export interface SkillDoc {
11
13
  ruleTokens: string[]
12
14
  }
13
15
 
14
- const STOPWORDS = new Set([
15
- "the",
16
- "a",
17
- "an",
18
- "is",
19
- "are",
20
- "was",
21
- "were",
22
- "be",
23
- "been",
24
- "being",
25
- "have",
26
- "has",
27
- "had",
28
- "do",
29
- "does",
30
- "did",
31
- "will",
32
- "would",
33
- "shall",
34
- "should",
35
- "may",
36
- "might",
37
- "can",
38
- "could",
39
- "of",
40
- "in",
41
- "to",
42
- "for",
43
- "with",
44
- "on",
45
- "at",
46
- "by",
47
- "from",
48
- "as",
49
- "into",
50
- "through",
51
- "during",
52
- "before",
53
- "after",
54
- "above",
55
- "below",
56
- "between",
57
- "out",
58
- "off",
59
- "over",
60
- "under",
61
- "again",
62
- "further",
63
- "then",
64
- "once",
65
- "here",
66
- "there",
67
- "where",
68
- "when",
69
- "how",
70
- "all",
71
- "each",
72
- "every",
73
- "both",
74
- "few",
75
- "more",
76
- "most",
77
- "other",
78
- "some",
79
- "such",
80
- "no",
81
- "nor",
82
- "not",
83
- "only",
84
- "own",
85
- "same",
86
- "than",
87
- "too",
88
- "very",
89
- "and",
90
- "but",
91
- "or",
92
- "if",
93
- "this",
94
- "that",
95
- "these",
96
- "those",
97
- "it",
98
- "its",
99
- "contract",
100
- "function",
101
- "solidity",
102
- "smart",
103
- "vulnerability",
104
- "attack",
105
- "attacker",
106
- "token",
107
- "address",
108
- "value",
109
- "state",
110
- "require",
111
- "modifier",
112
- "external",
113
- "internal",
114
- "public",
115
- "private",
116
- "mapping",
117
- "uint256",
118
- "bool",
119
- "returns",
120
- "event",
121
- "emit",
122
- ])
123
-
124
16
  function stripFrontmatter(content: string): string {
125
17
  return content.replace(/^---[ \t]*\r?\n[\s\S]*?\r?\n---[ \t]*\r?\n?/, "")
126
18
  }
@@ -147,10 +39,6 @@ function tokenize(text: string): string[] {
147
39
  .filter((token) => !STOPWORDS.has(token))
148
40
  }
149
41
 
150
- function isRecord(value: unknown): value is Record<string, unknown> {
151
- return typeof value === "object" && value !== null
152
- }
153
-
154
42
  function extractDetectionRules(frontmatter: Record<string, unknown>): string[] {
155
43
  const rawRules = frontmatter.detection_rules
156
44
  if (!Array.isArray(rawRules)) return []
@@ -0,0 +1,109 @@
1
+ export const STOPWORDS = new Set([
2
+ "the",
3
+ "a",
4
+ "an",
5
+ "is",
6
+ "are",
7
+ "was",
8
+ "were",
9
+ "be",
10
+ "been",
11
+ "being",
12
+ "have",
13
+ "has",
14
+ "had",
15
+ "do",
16
+ "does",
17
+ "did",
18
+ "will",
19
+ "would",
20
+ "shall",
21
+ "should",
22
+ "may",
23
+ "might",
24
+ "can",
25
+ "could",
26
+ "of",
27
+ "in",
28
+ "to",
29
+ "for",
30
+ "with",
31
+ "on",
32
+ "at",
33
+ "by",
34
+ "from",
35
+ "as",
36
+ "into",
37
+ "through",
38
+ "during",
39
+ "before",
40
+ "after",
41
+ "above",
42
+ "below",
43
+ "between",
44
+ "out",
45
+ "off",
46
+ "over",
47
+ "under",
48
+ "again",
49
+ "further",
50
+ "then",
51
+ "once",
52
+ "here",
53
+ "there",
54
+ "where",
55
+ "when",
56
+ "how",
57
+ "all",
58
+ "each",
59
+ "every",
60
+ "both",
61
+ "few",
62
+ "more",
63
+ "most",
64
+ "other",
65
+ "some",
66
+ "such",
67
+ "no",
68
+ "nor",
69
+ "not",
70
+ "only",
71
+ "own",
72
+ "same",
73
+ "than",
74
+ "too",
75
+ "very",
76
+ "and",
77
+ "but",
78
+ "or",
79
+ "if",
80
+ "this",
81
+ "that",
82
+ "these",
83
+ "those",
84
+ "it",
85
+ "its",
86
+ "contract",
87
+ "function",
88
+ "solidity",
89
+ "smart",
90
+ "vulnerability",
91
+ "attack",
92
+ "attacker",
93
+ "token",
94
+ "address",
95
+ "value",
96
+ "state",
97
+ "require",
98
+ "modifier",
99
+ "external",
100
+ "internal",
101
+ "public",
102
+ "private",
103
+ "mapping",
104
+ "uint256",
105
+ "bool",
106
+ "returns",
107
+ "event",
108
+ "emit",
109
+ ])
@@ -2,6 +2,7 @@ import { type Dirent, existsSync, readdirSync, readFileSync } from "node:fs"
2
2
  import { homedir } from "node:os"
3
3
  import { basename, extname, join, resolve } from "node:path"
4
4
  import type { ArgusConfig } from "../config/types"
5
+ import { getTrailOfBitsCacheDir } from "../shared/cache-paths"
5
6
  import { createLogger } from "../shared/logger"
6
7
  import { parseFrontmatter, validateSkillFrontmatter } from "./skill-schema"
7
8
 
@@ -21,8 +22,6 @@ const OMO_PROJECT_SKILLS_DIR = [".opencode", "skills"]
21
22
  const OMO_GLOBAL_SKILLS_DIR = [".config", "opencode", "skills"]
22
23
  const CLAUDE_PROJECT_SKILLS_DIR = [".claude", "skills"]
23
24
  const CLAUDE_GLOBAL_SKILLS_DIR = [".claude", "skills"]
24
- const TOB_CACHE_DIR = join(homedir(), ".cache", "solidity-argus", "trailofbits-skills")
25
-
26
25
  const SKILL_NAME_ALIASES: Record<string, string> = {
27
26
  "vulnerability-patterns/reentrancy": "reentrancy",
28
27
  "vulnerability-patterns/oracle-manipulation": "oracle-manipulation",
@@ -55,6 +54,9 @@ function parseSkillDescriptionFromFrontmatter(content: string): string {
55
54
  return raw.replace(/^"|"$/g, "")
56
55
  }
57
56
 
57
+ /** Filenames that are never skills — exclude from resolution and health checks. */
58
+ const NON_SKILL_FILENAMES = new Set(["README.md", "INVENTORY.md", "CHANGELOG.md", "LICENSE.md"])
59
+
58
60
  function collectMarkdownFiles(root: string, maxDepth = 8): string[] {
59
61
  if (!existsSync(root)) return []
60
62
 
@@ -82,6 +84,7 @@ function collectMarkdownFiles(root: string, maxDepth = 8): string[] {
82
84
 
83
85
  if (!entry.isFile()) continue
84
86
  if (extname(entry.name).toLowerCase() !== ".md") continue
87
+ if (NON_SKILL_FILENAMES.has(entry.name)) continue
85
88
  files.push(fullPath)
86
89
  }
87
90
  }
@@ -90,7 +93,7 @@ function collectMarkdownFiles(root: string, maxDepth = 8): string[] {
90
93
  }
91
94
 
92
95
  function getTrailOfBitsRoots(): string[] {
93
- const pluginsDir = join(TOB_CACHE_DIR, "plugins")
96
+ const pluginsDir = join(getTrailOfBitsCacheDir(), "plugins")
94
97
  if (!existsSync(pluginsDir)) return []
95
98
 
96
99
  let entries: Dirent[]