solidity-argus 0.5.10 → 0.6.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 +8 -1
- package/README.md +27 -21
- package/package.json +2 -2
- package/skills/INVENTORY.md +14 -1
- package/skills/README.md +4 -2
- package/skills/references/attack-vector-deck/SKILL.md +62 -0
- package/skills/specialist-profiles/access-control-specialist/SKILL.md +31 -0
- package/skills/specialist-profiles/economic-security/SKILL.md +31 -0
- package/skills/specialist-profiles/execution-trace/SKILL.md +31 -0
- package/skills/specialist-profiles/first-principles/SKILL.md +31 -0
- package/skills/specialist-profiles/invariant/SKILL.md +31 -0
- package/skills/specialist-profiles/math-precision/SKILL.md +31 -0
- package/skills/specialist-profiles/periphery/SKILL.md +31 -0
- package/skills/specialist-profiles/vector-scan/SKILL.md +28 -0
- package/src/agents/argus-prompt.ts +37 -1
- package/src/agents/audit-specialist-prompt.ts +76 -0
- package/src/agents/pythia-prompt.ts +1 -1
- package/src/agents/scribe-prompt.ts +5 -0
- package/src/agents/sentinel-prompt.ts +5 -0
- package/src/agents/themis-prompt.ts +2 -0
- package/src/config/schema.ts +2 -0
- package/src/constants/defaults.ts +1 -0
- package/src/create-hooks.ts +9 -1
- package/src/features/persistent-state/run-finalizer.ts +18 -0
- package/src/hooks/config-handler.ts +23 -0
- package/src/hooks/system-prompt-hook.ts +56 -2
- package/src/hooks/tool-tracking-hook.ts +50 -6
- package/src/shared/agent-names.ts +1 -0
- package/src/state/adapters.ts +1 -1
- package/src/state/projectors.ts +50 -0
- package/src/state/schemas.ts +86 -1
- package/src/state/types.ts +24 -1
- package/src/tools/record-finding-tool.ts +7 -1
- package/src/tools/report-generator-tool.ts +1 -0
package/src/state/schemas.ts
CHANGED
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
import type {
|
|
9
9
|
ArgusAgentName,
|
|
10
10
|
AuditPhase,
|
|
11
|
+
CoverageAttemptState,
|
|
11
12
|
Finding,
|
|
13
|
+
FindingCounts,
|
|
12
14
|
FindingSeverity,
|
|
13
15
|
FuzzCounterexample,
|
|
14
16
|
SoloditResult,
|
|
@@ -111,6 +113,7 @@ export interface ReportInput {
|
|
|
111
113
|
projectDir: string
|
|
112
114
|
findings: CanonicalFinding[]
|
|
113
115
|
toolsExecuted: CanonicalToolExecution[]
|
|
116
|
+
findingCounts?: FindingCounts
|
|
114
117
|
scope: string[]
|
|
115
118
|
soloditResults?: SoloditResult[]
|
|
116
119
|
fuzzCounterexamples?: FuzzCounterexample[]
|
|
@@ -120,6 +123,82 @@ export interface ReportInput {
|
|
|
120
123
|
patternVersion?: string
|
|
121
124
|
skillsLoaded?: string[]
|
|
122
125
|
unavailableTools?: string[]
|
|
126
|
+
coverageAttempt?: CoverageAttemptState
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const FINDING_COUNT_FIELDS = [
|
|
130
|
+
"rawObservations",
|
|
131
|
+
"recordedFindings",
|
|
132
|
+
"dedupedFindings",
|
|
133
|
+
"actionableFindings",
|
|
134
|
+
"nonActionableFindings",
|
|
135
|
+
] as const
|
|
136
|
+
|
|
137
|
+
const COVERAGE_ATTEMPT_STATUSES = new Set(["pending", "run", "skipped", "failed"])
|
|
138
|
+
|
|
139
|
+
function pushFindingCountsErrors(errors: ValidationError[], raw: unknown, prefix: string): void {
|
|
140
|
+
if (raw == null) return
|
|
141
|
+
if (!isRecord(raw)) {
|
|
142
|
+
errors.push({
|
|
143
|
+
field: prefix,
|
|
144
|
+
code: "invalid",
|
|
145
|
+
message: `${prefix} must be an object when provided`,
|
|
146
|
+
})
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const field of FINDING_COUNT_FIELDS) {
|
|
151
|
+
const value = raw[field]
|
|
152
|
+
if (value == null) continue
|
|
153
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
154
|
+
errors.push({
|
|
155
|
+
field: `${prefix}.${field}`,
|
|
156
|
+
code: "invalid",
|
|
157
|
+
message: `${prefix}.${field} must be a non-negative integer when provided`,
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function pushCoverageAttemptErrors(errors: ValidationError[], raw: unknown): void {
|
|
164
|
+
if (raw == null) return
|
|
165
|
+
if (!isRecord(raw)) {
|
|
166
|
+
errors.push({
|
|
167
|
+
field: "coverageAttempt",
|
|
168
|
+
code: "invalid",
|
|
169
|
+
message: "coverageAttempt must be an object when provided",
|
|
170
|
+
})
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (typeof raw.status !== "string" || !COVERAGE_ATTEMPT_STATUSES.has(raw.status)) {
|
|
175
|
+
errors.push({
|
|
176
|
+
field: "coverageAttempt.status",
|
|
177
|
+
code: "enum",
|
|
178
|
+
message: "coverageAttempt.status must be one of: pending, run, skipped, failed",
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
raw.attemptedAt != null &&
|
|
184
|
+
(typeof raw.attemptedAt !== "number" ||
|
|
185
|
+
!Number.isInteger(raw.attemptedAt) ||
|
|
186
|
+
raw.attemptedAt <= 0)
|
|
187
|
+
) {
|
|
188
|
+
errors.push({
|
|
189
|
+
field: "coverageAttempt.attemptedAt",
|
|
190
|
+
code: "invalid",
|
|
191
|
+
message: "coverageAttempt.attemptedAt must be a positive integer when provided",
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (raw.reason != null && (typeof raw.reason !== "string" || raw.reason.trim().length === 0)) {
|
|
196
|
+
errors.push({
|
|
197
|
+
field: "coverageAttempt.reason",
|
|
198
|
+
code: "invalid",
|
|
199
|
+
message: "coverageAttempt.reason must be a non-empty string when provided",
|
|
200
|
+
})
|
|
201
|
+
}
|
|
123
202
|
}
|
|
124
203
|
|
|
125
204
|
function pushRequiredRootStringError(
|
|
@@ -253,7 +332,8 @@ export function validateCanonicalFinding(raw: unknown): ValidationResult<Canonic
|
|
|
253
332
|
errors.push({
|
|
254
333
|
field: "reported_by_agent",
|
|
255
334
|
code: "enum",
|
|
256
|
-
message:
|
|
335
|
+
message:
|
|
336
|
+
"reported_by_agent must be one of: argus, sentinel, pythia, audit-specialist, scribe, unknown",
|
|
257
337
|
})
|
|
258
338
|
}
|
|
259
339
|
|
|
@@ -346,6 +426,8 @@ export function validateCanonicalToolExecution(
|
|
|
346
426
|
})
|
|
347
427
|
}
|
|
348
428
|
|
|
429
|
+
pushFindingCountsErrors(errors, raw.findingCounts, "findingCounts")
|
|
430
|
+
|
|
349
431
|
if (typeof raw.run_id !== "string" || raw.run_id.trim().length === 0) {
|
|
350
432
|
errors.push({
|
|
351
433
|
field: "run_id",
|
|
@@ -400,6 +482,9 @@ export function validateReportInput(raw: unknown): ValidationResult<ReportInput>
|
|
|
400
482
|
})
|
|
401
483
|
}
|
|
402
484
|
|
|
485
|
+
pushFindingCountsErrors(errors, raw.findingCounts, "findingCounts")
|
|
486
|
+
pushCoverageAttemptErrors(errors, raw.coverageAttempt)
|
|
487
|
+
|
|
403
488
|
if (!Array.isArray(raw.scope) || !raw.scope.every((item) => typeof item === "string")) {
|
|
404
489
|
errors.push({
|
|
405
490
|
field: "scope",
|
package/src/state/types.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export type FindingSeverity = "Critical" | "High" | "Medium" | "Low" | "Informational"
|
|
2
|
-
export type ArgusAgentName =
|
|
2
|
+
export type ArgusAgentName =
|
|
3
|
+
| "argus"
|
|
4
|
+
| "sentinel"
|
|
5
|
+
| "pythia"
|
|
6
|
+
| "audit-specialist"
|
|
7
|
+
| "scribe"
|
|
8
|
+
| "unknown"
|
|
3
9
|
export type AuditPhase =
|
|
4
10
|
| "reconnaissance"
|
|
5
11
|
| "scanning"
|
|
@@ -88,6 +94,21 @@ export interface ToolExecution {
|
|
|
88
94
|
endTime?: number
|
|
89
95
|
success: boolean
|
|
90
96
|
findingsCount: number
|
|
97
|
+
findingCounts?: FindingCounts
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface FindingCounts {
|
|
101
|
+
rawObservations?: number
|
|
102
|
+
recordedFindings?: number
|
|
103
|
+
dedupedFindings?: number
|
|
104
|
+
actionableFindings?: number
|
|
105
|
+
nonActionableFindings?: number
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface CoverageAttemptState {
|
|
109
|
+
status: "pending" | "run" | "skipped" | "failed"
|
|
110
|
+
attemptedAt?: number
|
|
111
|
+
reason?: string
|
|
91
112
|
}
|
|
92
113
|
|
|
93
114
|
export interface AuditState {
|
|
@@ -105,7 +126,9 @@ export interface AuditState {
|
|
|
105
126
|
skillsLoaded?: string[]
|
|
106
127
|
unavailableTools?: string[]
|
|
107
128
|
reportGenerated?: boolean
|
|
129
|
+
findingCounts?: FindingCounts
|
|
108
130
|
knowledgeSynced?: { success: boolean; timestamp: number }
|
|
131
|
+
coverageAttempt?: CoverageAttemptState
|
|
109
132
|
coverageReport?: {
|
|
110
133
|
files: Array<{
|
|
111
134
|
path: string
|
|
@@ -63,7 +63,13 @@ function parseFindingObject(raw: string, label: "finding" | "findings"): ParseRe
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function normalizeAgent(value: string): ArgusAgentName {
|
|
66
|
-
if (
|
|
66
|
+
if (
|
|
67
|
+
value === "argus" ||
|
|
68
|
+
value === "sentinel" ||
|
|
69
|
+
value === "pythia" ||
|
|
70
|
+
value === "audit-specialist" ||
|
|
71
|
+
value === "scribe"
|
|
72
|
+
) {
|
|
67
73
|
return value
|
|
68
74
|
}
|
|
69
75
|
|