solidity-argus 0.3.7 → 0.5.6
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 +13 -6
- package/README.md +24 -12
- package/package.json +7 -3
- package/skills/checklists/cyfrin-best-practices-runtime/SKILL.md +1 -0
- package/skills/checklists/cyfrin-best-practices-upgrades/SKILL.md +1 -0
- package/skills/checklists/cyfrin-defi-core/SKILL.md +1 -0
- package/skills/checklists/cyfrin-defi-integrations/SKILL.md +1 -0
- package/skills/checklists/cyfrin-gas/SKILL.md +1 -0
- package/skills/checklists/general-audit/SKILL.md +1 -0
- package/skills/methodology/audit-workflow/SKILL.md +1 -0
- package/skills/methodology/report-template/SKILL.md +1 -0
- package/skills/methodology/severity-classification/SKILL.md +1 -0
- package/skills/protocol-patterns/amm-dex/SKILL.md +1 -0
- package/skills/protocol-patterns/bridges-cross-chain/SKILL.md +1 -0
- package/skills/protocol-patterns/dao-governance/SKILL.md +1 -0
- package/skills/protocol-patterns/lending-borrowing/SKILL.md +1 -0
- package/skills/protocol-patterns/staking-vesting/SKILL.md +1 -0
- package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +0 -50
- package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +0 -63
- package/src/agents/argus-prompt.ts +98 -33
- package/src/agents/pythia-prompt.ts +18 -1
- package/src/agents/scribe-prompt.ts +32 -10
- package/src/agents/sentinel-prompt.ts +19 -0
- package/src/agents/themis-prompt.ts +110 -0
- package/src/cli/commands/doctor.ts +29 -17
- package/src/config/loader.ts +29 -5
- package/src/config/schema.ts +45 -45
- package/src/constants/defaults.ts +1 -0
- package/src/create-hooks.ts +797 -148
- package/src/create-managers.ts +4 -2
- package/src/create-tools.ts +5 -1
- package/src/features/audit-enforcer/audit-enforcer.ts +1 -11
- package/src/features/background-agent/background-manager.ts +32 -5
- package/src/features/error-recovery/tool-error-recovery.ts +1 -0
- package/src/features/persistent-state/audit-state-manager.ts +272 -29
- package/src/features/persistent-state/event-sink.ts +96 -25
- package/src/features/persistent-state/findings-materializer.ts +34 -2
- package/src/features/persistent-state/global-run-index.ts +86 -8
- package/src/features/persistent-state/index.ts +7 -1
- package/src/features/persistent-state/run-finalizer.ts +116 -7
- package/src/features/persistent-state/run-pruner.ts +93 -0
- package/src/hooks/agent-tracker.ts +14 -2
- package/src/hooks/compaction-hook.ts +7 -16
- package/src/hooks/config-handler.ts +83 -29
- package/src/hooks/context-budget.ts +4 -5
- package/src/hooks/event-hook.ts +213 -57
- package/src/hooks/knowledge-sync-hook.ts +2 -3
- package/src/hooks/safe-create-hook.ts +13 -1
- package/src/hooks/system-prompt-hook.ts +20 -39
- package/src/hooks/tool-tracking-hook.ts +597 -323
- package/src/index.ts +15 -1
- package/src/knowledge/scvd-client.ts +2 -4
- package/src/knowledge/scvd-errors.ts +25 -2
- package/src/knowledge/scvd-index.ts +7 -5
- package/src/knowledge/scvd-sync.ts +6 -6
- package/src/managers/types.ts +20 -2
- package/src/shared/agent-names.ts +23 -0
- package/src/shared/audit-artifact-resolver.ts +8 -3
- package/src/shared/audit-phases.ts +12 -0
- package/src/shared/cache-paths.ts +41 -0
- package/src/shared/drop-diagnostics.ts +2 -2
- package/src/shared/forge-errors.ts +31 -0
- package/src/shared/forge-runner.ts +30 -0
- package/src/shared/format-error.ts +3 -0
- package/src/shared/index.ts +9 -0
- package/src/shared/key-tools.ts +39 -0
- package/src/shared/logger.ts +7 -7
- package/src/shared/path-containment.ts +25 -0
- package/src/shared/path-utils.ts +11 -0
- package/src/shared/report-path-resolver.ts +4 -2
- package/src/shared/safe-emit.ts +24 -0
- package/src/shared/token-utils.ts +5 -0
- package/src/shared/type-guards.ts +8 -0
- package/src/shared/validation-constants.ts +52 -0
- package/src/skills/analysis/cluster.ts +1 -114
- package/src/skills/analysis/normalize.ts +2 -114
- package/src/skills/analysis/stopwords.ts +109 -0
- package/src/skills/argus-skill-resolver.ts +6 -3
- package/src/solodit-lifecycle.ts +153 -37
- package/src/state/adapters.ts +60 -66
- package/src/state/finding-aggregation.ts +6 -8
- package/src/state/finding-fingerprint.ts +1 -1
- package/src/state/finding-store.ts +31 -9
- package/src/state/index.ts +1 -1
- package/src/state/projectors.ts +27 -19
- package/src/state/schemas.ts +8 -32
- package/src/state/types.ts +3 -0
- package/src/tools/contract-analyzer-tool.ts +4 -6
- package/src/tools/forge-coverage-tool.ts +10 -35
- package/src/tools/forge-fuzz-tool.ts +21 -51
- package/src/tools/forge-test-tool.ts +25 -47
- package/src/tools/gas-analysis-tool.ts +12 -41
- package/src/tools/pattern-checker-tool.ts +37 -15
- package/src/tools/pattern-loader.ts +18 -4
- package/src/tools/persist-deduped-tool.ts +94 -0
- package/src/tools/proxy-detection-tool.ts +35 -34
- package/src/tools/read-findings-tool.ts +390 -0
- package/src/tools/record-finding-tool.ts +120 -25
- package/src/tools/report-generator-tool.ts +394 -328
- package/src/tools/report-preflight.ts +5 -1
- package/src/tools/slither-tool.ts +55 -16
- package/src/tools/solodit-search-tool.ts +260 -112
- package/src/tools/sync-knowledge-tool.ts +2 -3
- package/src/utils/solidity-parser.ts +39 -24
- package/src/features/migration/index.ts +0 -14
- package/src/features/migration/migration-adapter.ts +0 -151
- 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(
|
|
96
|
+
const pluginsDir = join(getTrailOfBitsCacheDir(), "plugins")
|
|
94
97
|
if (!existsSync(pluginsDir)) return []
|
|
95
98
|
|
|
96
99
|
let entries: Dirent[]
|