solidity-argus 0.3.6 → 0.3.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solidity-argus",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Solidity smart contract security auditing plugin for OpenCode — 4 specialized agents, 12 tools (11 core + optional Solodit), and a curated vulnerability knowledge base",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"solidity",
|
package/src/create-hooks.ts
CHANGED
|
@@ -41,6 +41,31 @@ export type AgentTrackerRef = {
|
|
|
41
41
|
|
|
42
42
|
let _agentTrackerRef: AgentTrackerRef | undefined
|
|
43
43
|
|
|
44
|
+
const REPORT_METADATA_REGEX = /<!-- argus:report_metadata (.+?) -->/
|
|
45
|
+
|
|
46
|
+
function extractRunIdFromReportToolOutput(result: string): string | undefined {
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(result) as Record<string, unknown>
|
|
49
|
+
if (typeof parsed.run_id === "string" && parsed.run_id.length > 0) {
|
|
50
|
+
return parsed.run_id
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof parsed.report === "string") {
|
|
54
|
+
const match = parsed.report.match(REPORT_METADATA_REGEX)
|
|
55
|
+
if (match?.[1]) {
|
|
56
|
+
const metadata = JSON.parse(match[1]) as Record<string, unknown>
|
|
57
|
+
if (typeof metadata.run_id === "string" && metadata.run_id.length > 0) {
|
|
58
|
+
return metadata.run_id
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
44
69
|
export function getAgentForSession(sessionID: string): string | undefined {
|
|
45
70
|
return _agentTrackerRef?.getAgentForSession(sessionID)
|
|
46
71
|
}
|
|
@@ -326,6 +351,20 @@ export function createHooks(args: {
|
|
|
326
351
|
return agent
|
|
327
352
|
}
|
|
328
353
|
|
|
354
|
+
return "unknown"
|
|
355
|
+
},
|
|
356
|
+
getAgentNameForSession: (sessionId: string) => {
|
|
357
|
+
const agent = agentTracker.getAgentForSession(sessionId)
|
|
358
|
+
if (
|
|
359
|
+
agent === "argus" ||
|
|
360
|
+
agent === "sentinel" ||
|
|
361
|
+
agent === "pythia" ||
|
|
362
|
+
agent === "scribe" ||
|
|
363
|
+
agent === "unknown"
|
|
364
|
+
) {
|
|
365
|
+
return agent
|
|
366
|
+
}
|
|
367
|
+
|
|
329
368
|
return "unknown"
|
|
330
369
|
},
|
|
331
370
|
},
|
|
@@ -334,30 +373,31 @@ export function createHooks(args: {
|
|
|
334
373
|
)
|
|
335
374
|
: undefined
|
|
336
375
|
|
|
337
|
-
const
|
|
376
|
+
const materializeFindingsForRun = async (
|
|
377
|
+
runId: string,
|
|
378
|
+
projectDirForRun: string,
|
|
379
|
+
sessionIdForRun: string | undefined,
|
|
338
380
|
trigger: "session.idle" | "tool.execute.after",
|
|
339
381
|
failFast = false,
|
|
340
382
|
): Promise<void> => {
|
|
341
|
-
|
|
342
|
-
if (!state || state.sessionId.length === 0) {
|
|
383
|
+
if (!runId || runId.length === 0) {
|
|
343
384
|
return
|
|
344
385
|
}
|
|
345
386
|
|
|
346
387
|
try {
|
|
347
|
-
await materializeFindings(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
)
|
|
388
|
+
await materializeFindings(runId, projectDirForRun, sessionIdForRun, {
|
|
389
|
+
validateSessionId: sessionIdForRun != null && sessionIdForRun.length > 0,
|
|
390
|
+
requireEvents: true,
|
|
391
|
+
})
|
|
352
392
|
} catch (error) {
|
|
353
393
|
if (failFast) {
|
|
354
394
|
throw new Error(
|
|
355
|
-
`Failed to materialize findings artifact on ${trigger} for run ${
|
|
395
|
+
`Failed to materialize findings artifact on ${trigger} for run ${runId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
356
396
|
)
|
|
357
397
|
}
|
|
358
398
|
|
|
359
399
|
logger.warn(
|
|
360
|
-
`Failed to materialize findings artifact on ${trigger} for run ${
|
|
400
|
+
`Failed to materialize findings artifact on ${trigger} for run ${runId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
361
401
|
)
|
|
362
402
|
}
|
|
363
403
|
}
|
|
@@ -378,7 +418,13 @@ export function createHooks(args: {
|
|
|
378
418
|
|
|
379
419
|
if (hasNewFinalization && finalizationResult.runId.length > 0) {
|
|
380
420
|
try {
|
|
381
|
-
await
|
|
421
|
+
await materializeFindingsForRun(
|
|
422
|
+
finalizationResult.runId,
|
|
423
|
+
projectDir,
|
|
424
|
+
input.event.sessionId,
|
|
425
|
+
"session.idle",
|
|
426
|
+
true,
|
|
427
|
+
)
|
|
382
428
|
} catch (error) {
|
|
383
429
|
logger.warn(
|
|
384
430
|
`Failed to materialize findings artifact for run ${finalizationResult.runId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -437,10 +483,24 @@ export function createHooks(args: {
|
|
|
437
483
|
tool: input.tool,
|
|
438
484
|
args: input.args,
|
|
439
485
|
result: output.output,
|
|
486
|
+
sessionID: input.sessionID,
|
|
487
|
+
callID: input.callID,
|
|
440
488
|
})
|
|
441
489
|
|
|
442
490
|
if (input.tool === "argus_generate_report") {
|
|
443
|
-
|
|
491
|
+
const state = getAuditState()
|
|
492
|
+
if (!state || state.sessionId.length === 0) {
|
|
493
|
+
throw new Error("argus_generate_report completed without active audit state")
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const runId = extractRunIdFromReportToolOutput(output.output) ?? state.sessionId
|
|
497
|
+
await materializeFindingsForRun(
|
|
498
|
+
runId,
|
|
499
|
+
state.projectDir,
|
|
500
|
+
input.sessionID,
|
|
501
|
+
"tool.execute.after",
|
|
502
|
+
true,
|
|
503
|
+
)
|
|
444
504
|
}
|
|
445
505
|
|
|
446
506
|
const outputWithHint = recoveryHint ? `${output.output}${recoveryHint}` : output.output
|
|
@@ -20,12 +20,34 @@ export interface FindingsArtifact {
|
|
|
20
20
|
toolsExecuted: CanonicalToolExecution[]
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export interface FindingsMaterializeOptions {
|
|
24
|
+
validateSessionId?: boolean
|
|
25
|
+
requireEvents?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
export async function materializeFindings(
|
|
24
29
|
runId: string,
|
|
25
30
|
projectDir: string,
|
|
26
31
|
sessionId?: string,
|
|
32
|
+
options: FindingsMaterializeOptions = {},
|
|
27
33
|
): Promise<FindingsArtifact> {
|
|
28
34
|
const events = await readEvents(runId, projectDir)
|
|
35
|
+
if (options.requireEvents && events.length === 0) {
|
|
36
|
+
throw new Error(`No events found for run ${runId}`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const sessionIdFromEvents = events[0]?.session_id ?? ""
|
|
40
|
+
if (
|
|
41
|
+
options.validateSessionId &&
|
|
42
|
+
sessionId &&
|
|
43
|
+
sessionIdFromEvents.length > 0 &&
|
|
44
|
+
sessionId !== sessionIdFromEvents
|
|
45
|
+
) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Session mismatch for run ${runId}: provided ${sessionId}, event stream has ${sessionIdFromEvents}`,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
29
51
|
const findings = dedupeFindingsForFinalOutput(projectFindings(events))
|
|
30
52
|
const toolsExecuted = projectToolExecutions(events)
|
|
31
53
|
const contentHash = stableHash(JSON.stringify(findings))
|
|
@@ -33,7 +55,7 @@ export async function materializeFindings(
|
|
|
33
55
|
|
|
34
56
|
const artifact: FindingsArtifact = {
|
|
35
57
|
run_id: runId,
|
|
36
|
-
session_id: sessionId ??
|
|
58
|
+
session_id: sessionId ?? sessionIdFromEvents,
|
|
37
59
|
schema_version: SCHEMA_VERSION,
|
|
38
60
|
seq_first: events[0]?.seq ?? 0,
|
|
39
61
|
seq_last: events.at(-1)?.seq ?? 0,
|
package/src/hooks/event-hook.ts
CHANGED
|
@@ -113,7 +113,6 @@ export function createEventHook(
|
|
|
113
113
|
|
|
114
114
|
case "session.deleted": {
|
|
115
115
|
preDeleteState = currentAuditState
|
|
116
|
-
currentAuditState = null
|
|
117
116
|
break
|
|
118
117
|
}
|
|
119
118
|
|
|
@@ -179,6 +178,7 @@ export function createEventHook(
|
|
|
179
178
|
}
|
|
180
179
|
}
|
|
181
180
|
}
|
|
181
|
+
currentAuditState = null
|
|
182
182
|
eventSink = null
|
|
183
183
|
break
|
|
184
184
|
}
|
|
@@ -27,6 +27,8 @@ type ToolHookInput = {
|
|
|
27
27
|
tool: string
|
|
28
28
|
args: unknown
|
|
29
29
|
result: string
|
|
30
|
+
sessionID?: string
|
|
31
|
+
callID?: string
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
type ToolExecutionMetadata = {
|
|
@@ -38,6 +40,7 @@ export type ToolTrackingOptions = {
|
|
|
38
40
|
getEventSink?: () => EventSink | null
|
|
39
41
|
getSessionId?: () => string
|
|
40
42
|
getAgentName?: () => ArgusAgentName | undefined
|
|
43
|
+
getAgentNameForSession?: (sessionId: string) => ArgusAgentName | undefined
|
|
41
44
|
dropPolicy?: DropPolicy
|
|
42
45
|
onChildSessionDetected?: (parentSessionId: string, childSessionId: string) => void
|
|
43
46
|
}
|
|
@@ -502,7 +505,7 @@ export function createToolTrackingHook(
|
|
|
502
505
|
const correlationId = randomUUID()
|
|
503
506
|
const resolved = resolveStateAndStore()
|
|
504
507
|
const sink = options?.getEventSink?.()
|
|
505
|
-
const sessionId = options?.getSessionId?.() ?? ""
|
|
508
|
+
const sessionId = input.sessionID ?? options?.getSessionId?.() ?? ""
|
|
506
509
|
const toolCallId = randomUUID()
|
|
507
510
|
|
|
508
511
|
if (childSessionId) {
|
|
@@ -576,8 +579,11 @@ export function createToolTrackingHook(
|
|
|
576
579
|
const { state: auditState, store } = resolved
|
|
577
580
|
const sink = options?.getEventSink?.()
|
|
578
581
|
const runId = auditState.sessionId
|
|
579
|
-
const sessionId = options?.getSessionId?.() ?? ""
|
|
580
|
-
const reportedByAgent =
|
|
582
|
+
const sessionId = input.sessionID ?? options?.getSessionId?.() ?? ""
|
|
583
|
+
const reportedByAgent =
|
|
584
|
+
(input.sessionID ? options?.getAgentNameForSession?.(input.sessionID) : undefined) ??
|
|
585
|
+
options?.getAgentName?.() ??
|
|
586
|
+
"unknown"
|
|
581
587
|
const findingMetadata = {
|
|
582
588
|
reportedByAgent,
|
|
583
589
|
reportedBySessionId: sessionId,
|
|
@@ -44,6 +44,7 @@ export type ReportGenerationResult = {
|
|
|
44
44
|
report: string
|
|
45
45
|
findingsCount: FindingsCount
|
|
46
46
|
filename: string
|
|
47
|
+
run_id: string
|
|
47
48
|
contentHash: string
|
|
48
49
|
qualityGates: ReportQualityValidation
|
|
49
50
|
contractDiagnostics: DropDiagnostic[]
|
|
@@ -1258,6 +1259,7 @@ export async function executeReportGeneration(
|
|
|
1258
1259
|
report: reportMarkdown,
|
|
1259
1260
|
findingsCount: counts,
|
|
1260
1261
|
filename: canonicalFilename,
|
|
1262
|
+
run_id: runId,
|
|
1261
1263
|
contentHash,
|
|
1262
1264
|
qualityGates,
|
|
1263
1265
|
contractDiagnostics: diagnostics,
|