Anatomy of a finding

The fields of a finding: what the agent writes, what is derived, and what is optional.

The agent records a finding with the add_finding tool call and can change it later in the same run with update_finding. The application fills in some fields itself. When the run completes, the findings are stored against the engagement. After that, the only field that can change is status, which an operator can set in the console; Triage describes it.

Fields

"Agent" means the model supplies the value in a tool call. "Application" means the code sets it and the model has no say.

Field Written by Required Notes
id Application — A UUID assigned when the finding is built.
engagement_id Application — The engagement the run belongs to.
title Agent Yes Free text.
description Agent Yes Free text.
severity Agent Yes One of five values. update_finding can change it. See Severity and taxonomy.
affected_asset Agent Yes Free text naming the host, URL or service.
evidence Agent Yes A list of evidence items, described below. The list can be empty.
remediation Agent Yes Free text advice.
phase Application — The phase the run was in when the finding was added.
status Agent, then operator No Defaults to unverified. update_finding can change it during the run, and an operator can change it in the console afterwards.
cvss_score Agent No A number. update_finding can change it.
cvss_vector Agent No A string. update_finding can change it.
cves Agent No A list of strings.
attack_technique_ids Agent Asked for Checked against the shipped ATT&CK manifest. A missing list is recorded as empty.
attack_tactic_ids Application — Derived from the technique ids.
wstg_ids Agent No A list of OWASP WSTG ids, or absent.
nist_test_category Agent Yes One of four values.
compliance_refs Report pipeline — Replaced every time a report is rendered.
related_finding_ids Agent No Ids of other findings in the run. update_finding can add to it.
created_at Application — The time the server stored the finding.

An add_finding call that lacks a required field, or gives a severity, status or test category outside its set of values, fails. The finding is not recorded, and the failed call counts toward the run's limit of five consecutive failed steps.

Severity and taxonomy covers severity, CVSS, CVE, the ATT&CK and WSTG ids and the test category. The rest are below.

Status

status takes one of four values: unverified, verified, false_positive, not_exploitable. The agent sets it, in the add_finding call or in a later update_finding call, and a finding whose status it never sets stays unverified. After the run, an operator can change it (Triage). The phase prompts ask the agent to use verified for a demonstrated exploit, and false_positive or not_exploitable for findings that did not hold up; Phases describes those prompts. The code does not check the status against the evidence.

The status appears in the CSV status column, in the SARIF result's properties.status, in the console's finding lists, and in the partner API's findings. The PDF does not print it.

Reports do not filter by status

A finding marked false_positive or not_exploitable is rendered and counted like any other. It appears in the PDF's findings, it counts toward the executive summary's risk posture and severity counts, and it marks the controls it maps to as tested. Filter on the status column of the CSV or SARIF export if you need to separate them.

Triage

The console's Findings page lists every finding across the organization's engagements, most severe first. It filters by severity, status and engagement, and searches titles, affected assets and CVE ids. Expanding a finding on that page, or on an engagement's page, shows a Triage status control that sets the status to any of the four values. Any member of the organization can change it.

A change has these effects:

  • The CSV and SARIF exports are built on every download, so the next download carries the new status. A file downloaded earlier keeps the old status.
  • The partner API's findings return the new status.
  • The PDF is unaffected, since it does not print the status.
  • The evidence locker is unaffected. The sealed manifest records which findings an artifact belongs to, not their status, so changing a status after the seal neither breaks verification nor is recorded by it.

The change is not logged. The finding keeps no record of who changed its status, when, or what it was before.

Phase

The Phase enum has eight values: pre_engagement, intelligence, threat_modeling, vuln_analysis, exploitation, post_exploitation, reporting and complete. A finding carries one of the six in between. The agent makes no tool calls in pre_engagement, which renders the rules-of-engagement document and advances, or in complete, which ends the run. update_finding does not change a finding's phase, so a finding added during vuln_analysis and verified during exploitation still reads vuln_analysis.

related_finding_ids lists the ids of other findings, usually the finding an escalation step builds on. The agent supplies them in add_finding or appends them with update_finding. An id that is not a well-formed UUID makes the call fail. In update_finding it fails after the status, severity and CVSS changes and the new evidence have been applied, the evidence captured as artifacts, and the well-formed ids before it appended; those changes stay. The ids are not checked against the run's findings when they are recorded.

The PDF's attack chain is built from these lists. It includes every finding whose list is not empty and follows only the ids that belong to a finding of the same run; an id that matches no finding in the run is dropped. The CSV and SARIF exports do not carry the field.

Control ids

compliance_refs holds the framework control ids a finding maps to. The agent does not supply it, so it is stored empty. Every rendering of a report replaces it with the ids the framework's tables give for the finding, in sorted order. The value in any report is the value computed when that report was rendered.

A control id on a finding means the finding carried an ATT&CK or WSTG id, or matched a keyword pattern, that the framework's tables map to that control. It does not mean the control passed or failed. What the result means explains what each case tells you and what tested and not_tested mean.

Duplicates within a run

A finding's dedup key is the pair (title, affected_asset). When the agent adds a finding whose title and affected asset both match, character for character, a finding already recorded in the run, the new one is dropped. That check runs in add_finding and nowhere else: not at storage, not at report time, and not across runs.

The drop is silent. The agent is told the finding was recorded, and the dropped finding's evidence items are still captured into the evidence locker, linked to the id of a finding that is never stored.

Evidence items and locker artifacts

A finding's evidence list holds items the agent writes into the tool call. An item has a kind, a text content, a timestamp and an optional command. The kind is one of six values: command_output, screenshot, payload, request, response, file. The items are stored with the finding, and the PDF prints their content under the finding.

These are not the evidence locker's artifacts. When a finding is added, or when update_finding supplies additional_evidence, the content of every item is also captured as a locker artifact, and the six kinds collapse into four artifact kinds. Artifacts describes that translation. The artifact is hashed, chained and sealed; the inline item stored with the finding is not. The PDF's Sealed evidence table under a finding, and the artifact_sha256 values in the exports, list the artifacts linked to the finding as the sealed manifest records them.

The command field has one further use: the first word of every command across a run's evidence items makes up the report's Tools used list.

Last updated 2026-10-11