Vuln vs Exploit Agent Pairs in Shannon: Understanding the Difference in Penetration Testing Pipelines
The *-vuln agent scans for vulnerabilities and writes an exploitation queue, while the *-exploit agent reads that queue and conditionally attempts exploitation only when shouldExploit returns true.
Shannon’s penetration-testing pipeline, maintained in the KeygraphHQ/shannon repository, uses a dual-agent architecture where vulnerability discovery and exploitation are handled by distinct but paired agents. Understanding the difference between these vuln and exploit agent pairs is essential for extending the pipeline or debugging execution flow.
What Are Vuln and Exploit Agent Pairs?
Shannon organizes its testing logic into five vulnerability classes: injection, XSS, authentication, SSRF, and authorization. For each class, the pipeline defines a paired agent set:
*-vuln– The vulnerability-analysis agent*-exploit– The exploitation agent
These pairs operate as independent pipelines that run in parallel with other pairs, but sequentially within their own pair.
The Vulnerability Analysis Agent (*-vuln)
The *-vuln agent serves as the discovery phase of the pair. According to the implementation in src/temporal/workflows.ts, this agent:
- Scans the target using reconnaissance data
- Produces a markdown deliverable containing the analysis report
- Writes an exploitation-queue JSON file that lists discovered issues with metadata
This agent always executes first within its pair and runs immediately after the Recon phase completes.
The Exploitation Agent (*-exploit)
The *-exploit agent serves as the execution phase that acts on the vuln agent's findings. As defined in src/temporal/workflows.ts and src/queue-validation.ts, this agent:
- Reads the exploitation queue generated by its paired
*-vulnagent - Checks the
shouldExploitboolean in theExploitationDecisioninterface - Attempts exploitation only if the queue indicates exploitable findings exist
- Writes an exploit-specific deliverable upon successful execution
Unlike the vuln agent, the exploit agent runs conditionally and does not wait for other vulnerability agents to complete.
Key Differences Between Vuln and Exploit Agents
The architectural separation creates distinct operational characteristics:
| Aspect | *-vuln Agent |
*-exploit Agent |
|---|---|---|
| Input | Target URL and reconnaissance data | Queue JSON generated by paired *-vuln agent |
| Output | Markdown report + exploitation queue | Exploit deliverable (conditional) |
| Execution Trigger | Always runs after Recon phase | Runs only when shouldExploit: true |
| Parallelism Model | All vuln agents start simultaneously in parallel group | Each exploit starts immediately after its paired vuln completes; no global barrier |
| Metric Label | Recorded as <type>-vuln (e.g., injection-vuln) |
Recorded as <type>-exploit (e.g., injection-exploit) |
How the Pipeline Orchestrates Vuln and Exploit Pairs
The coordination logic resides in src/temporal/workflows.ts, specifically within the runVulnExploitPipeline helper function (lines 68-86):
async function runVulnExploitPipeline(
vulnType: VulnType,
runVulnAgent: () => Promise<AgentMetrics>,
runExploitAgent: () => Promise<AgentMetrics>
): Promise<VulnExploitPipelineResult> {
const vulnMetrics = await runVulnAgent(); // ← vuln agent execution
const decision = await a.checkExploitationQueue(activityInput, vulnType);
let exploitMetrics: AgentMetrics | null = null;
if (decision.shouldExploit) { // ← conditional check
exploitMetrics = await runExploitAgent(); // ← exploit agent execution
}
// ...
}
The decision logic is implemented in src/queue-validation.ts (lines 11-28), which validates that both the deliverable and queue file exist (symmetry rule) before returning the ExploitationDecision:
export interface ExploitationDecision {
shouldExploit: boolean;
vulnerabilityCount: number;
// ...
}
Parallel execution groups are defined in src/session-manager.ts (lines 104-107):
export const getParallelGroups = () => Object.freeze({
vuln: ['injection-vuln','xss-vuln','auth-vuln','ssrf-vuln','authz-vuln'],
exploit: ['injection-exploit','xss-exploit','auth-exploit','ssrf-exploit','authz-exploit']
});
Practical Example: Adding a New Agent Pair
To extend Shannon with a new vulnerability class (e.g., open-redirect), you must define both agents in src/session-manager.ts and src/types/agents.ts:
// src/types/agents.ts – extend the AgentName union
export type AgentName =
| 'injection-vuln' | 'injection-exploit'
| 'xss-vuln' | 'xss-exploit'
| // ... existing pairs
| 'open-redirect-vuln'
| 'open-redirect-exploit';
// src/session-manager.ts – add agent definitions
export const AGENTS = Object.freeze({
// ... existing agents
'open-redirect-vuln': {
name: 'open-redirect-vuln',
displayName: 'Open‑Redirect vuln agent',
prerequisites: ['recon']
},
'open-redirect-exploit': {
name: 'open-redirect-exploit',
displayName: 'Open‑Redirect exploit agent',
prerequisites: ['open-redirect-vuln']
},
});
// src/session-manager.ts – register in parallel groups
export const getParallelGroups = () => Object.freeze({
vuln: [..., 'open-redirect-vuln'],
exploit: [..., 'open-redirect-exploit']
});
The new pair automatically follows the vuln → queue → conditional exploit pattern managed by runVulnExploitPipeline.
Summary
- Vuln agents (
*-vuln) always run first, scanning targets and writing exploitation queues. - Exploit agents (
*-exploit) run conditionally based onshouldExploitdecisions fromsrc/queue-validation.ts. - The architecture uses independent pipelines where vuln agents run in parallel groups, and exploit agents trigger immediately after their paired vuln agent completes.
- Extension requires defining both agents in
src/session-manager.tsand registering them ingetParallelGroups.
Frequently Asked Questions
Can exploit agents run without their corresponding vuln agents?
No. According to the source code in src/session-manager.ts, every *-exploit agent lists its *-vuln counterpart as a prerequisite in the AGENTS configuration object. The runVulnExploitPipeline function in src/temporal/workflows.ts also enforces this sequence by executing the vuln agent before checking the exploitation queue.
How does Shannon decide whether to run an exploit agent?
The decision logic resides in src/queue-validation.ts. After a vuln agent completes, the checkExploitationQueue activity validates that both the deliverable and queue JSON file exist (the symmetry rule). It returns an ExploitationDecision object containing shouldExploit: boolean and vulnerabilityCount: number. The exploit agent runs only when shouldExploit is true.
Are vuln and exploit agents executed sequentially or in parallel?
Vuln agents execute in parallel with each other as a group, defined in getParallelGroups() in src/session-manager.ts. However, within each vulnerability class, the vuln and exploit agents run sequentially: the exploit agent starts only after its paired vuln agent finishes and the queue validation succeeds. Different exploit agents run independently and do not wait for other vuln agents to complete.
Where are the exploitation decisions logged?
Decision metadata is captured in the ExploitationDecision interface defined in src/queue-validation.ts, which includes shouldExploit, vulnerabilityCount, and validation status. The runVulnExploitPipeline function in src/temporal/workflows.ts records these metrics alongside agent execution data, storing them under the respective agent names (e.g., injection-exploit) in the pipeline state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →