parse_output vs report_finding¶
The one-line answer: if a parser exists for your tool, use parse_output. Otherwise, use report_finding.
Decision tree¶
flowchart TD
A{Where did the data<br/>come from?} -->|Supported tool's<br/>raw output| B[parse_output]
A -->|Manual observation,<br/>unsupported tool, or<br/>analyst conclusion| C[report_finding]
A -->|Already structured<br/>nodes & edges| C
classDef parse fill:#22c55e,stroke:#15803d,color:#fff
classDef report fill:#3b82f6,stroke:#1e40af,color:#fff
class B parse
class C report
Supported parsers (parse_output)¶
| Category | Parsers |
|---|---|
| Network scanning | nmap, nxc (alias netexec) |
| AD enumeration | ldapsearch (aliases: ldapdomaindump, ldap), enum4linux (alias enum4linux-ng) |
| AD attacks | kerbrute, rubeus, certipy, getnpusers, getuserspns, gettgt, getst |
| Credential extraction | secretsdump (alias impacket-secretsdump), responder, hashcat |
| SMB/Lateral | smbclient, wmiexec, psexec |
| Web | gobuster, feroxbuster, ffuf, dirbuster, nuclei, nikto, wpscan, sqlmap, burp, zap |
| TLS | testssl, sslscan |
| Linux privesc | linpeas, linenum |
| Cloud | pacu, prowler |
Run parse_output list_parsers=true to get the live list.
Why prefer parse_output¶
- Deterministic — same input, same nodes/edges, every time.
- Token-efficient — the AI doesn't burn context interpreting raw output.
- Auditable — original output is stored as evidence, linked to the action.
Pass context when you have it¶
For parsers that don't always include attribution (secretsdump, hashcat, linpeas):
{
"tool_name": "secretsdump",
"output": "Administrator:500:aad3b...",
"context": {
"domain": "corp.local",
"source_host": "10.10.10.5"
}
}
domain— soft hint forcred_domainwhen the output lacks domain prefixes.source_host— addsDUMPED_FROMedges so the credential traces back to where it came from.
When to use report_finding¶
Anything that isn't a supported parser's output:
- Manual observations ("the login portal at
/adminacceptsadmin:admin"). - Output from custom scripts or one-off tools.
- Analyst conclusions drawn from multiple data points.
- Already-structured nodes/edges you want to add directly.
{
"nodes": [
{ "id": "host-10-10-10-5", "type": "host",
"properties": { "ip": "10.10.10.5", "os": "Windows Server 2019", "alive": true } }
],
"edges": [
{ "source": "host-10-10-10-5", "target": "domain-target-local",
"type": "MEMBER_OF_DOMAIN", "confidence": 1.0 }
]
}
Quick pattern lookup¶
| Scenario | Use |
|---|---|
| Nmap XML output | parse_output tool_name=nmap |
| nxc/netexec SMB enum | parse_output tool_name=nxc |
| Manual web app discovery | report_finding with host + service nodes |
Certipy find results |
parse_output tool_name=certipy |
| Spotted a login prompt during recon | report_finding with service node |
| Secretsdump hashes | parse_output tool_name=secretsdump + context.source_host |
| Custom shell-script output | report_finding with structured nodes/edges |
| Hashcat cracked hashes | parse_output tool_name=hashcat |
| Responder log | parse_output tool_name=responder (+ via_mock_service_id for BAITED edge) |
| Linpeas | parse_output tool_name=linpeas + context.source_host |
Tips¶
ingest: false— preview what would be parsed without modifying the graph. Useful when you're unsure.- Always pass
action_idif you have one (fromvalidate_action) — it links the finding to the action that produced it. - Always pass
frontier_item_idif the action came from a frontier candidate — required for retrospective attribution. - Node IDs follow conventions —
host-<ip>,svc-<ip>-<port>,user-<domain>-<name>. The parsers do this automatically; forreport_findingyou should match the pattern.