Skip to content

Findings and evidence

A finding records a bounded modeled source-to-sink observation or candidate code risk. It does not confirm a vulnerability, malicious intent, or successful exploitation. The following behavior is backed by released Java, Node.js, and Python versions; it does not imply equivalent analysis in PHP or .NET. See .NET Security for the released .NET scope.

Released versions and event formats

Language Per-request occurrences and attack evidence introduced Schema v2 introduced Version used by this guide
Java 1.3.0 1.3.3 1.3.4
Node.js 1.3.0 2.0.0 2.0.1
Python 1.2.0 1.2.3 1.2.4

Read schema_version before interpreting a record. The event name remains beacon.security.finding and fingerprint version remains 1. A shared schema does not establish runtime coverage. Older producers may omit optional occurrence, sampling, or attack-evidence fields; missing fields are not no_evidence.

Code risk, request evidence, and outcome

In schema v1, assessment=observation or candidate_risk describes code risk, validation=unvalidated says it has not been validated, and the optional attack_evidence describes this request. These are separate dimensions.

attack_evidence.status Meaning
unknown Analysis is unavailable, unsupported, imprecise, over budget, or needs missing boundary policy.
no_evidence Supported analysis found no attack evidence. This is not a safety verdict and does not remove a code risk.
suspected Supported analysis found possible exploitation evidence. This proves neither intent nor success.

attack_evidence.reason is a bounded machine-readable explanation, without raw request values. In v1, attack_evidence.outcome=not_assessed. Schema v2 omits fixed assessment/validation placeholders and the constant outcome field; their omission does not promote the finding to confirmed exploitation. An invocation, normal return, or HTTP status alone cannot establish successful exploitation.

With precise taint tracking and supported ANSI SQL analysis, these examples have different meanings. They describe sink context, not rules for matching keywords:

Scenario Interpretation
Ordinary input concatenated inside a quoted value in dynamic SQL A candidate code risk can remain even when supported analysis returns no_evidence.
Fixed SQL template with separately bound values Ordinary bound values do not create SQL-template injection findings. Building an untrusted template before preparation is different.
Untrusted input controls supported SQL structural operators or comment delimiters May yield suspected; it does not establish that the database executed an exploit.
Unconfigured dialect, unsupported syntax, imprecise taint, or exhausted analysis budget Remains unknown, rather than being declared safe.

See the language pages for the opt-in ANSI setting and analysis limits. Network allowlists and filesystem-root policies are not implemented by the versions listed above; boundary-dependent attack assessment remains unknown. Normalization alone does not establish path safety.

One finding, several requests

finding_id groups a stable code-risk fingerprint. occurrence_id identifies a request's detection of that finding. In Java 1.3.0 schema v1 it equals that request's evidence_id. Repeated detections of the same fingerprint in one request count once. Stronger evidence can update that occurrence with the same ID.

This symbolic Java 1.3.0 example assumes aggregation is available and all three requests occur inside one full-evidence interval. IDs below are labels, not a literal event payload. Each request has its own current trace/span context.

Request finding_id evidence_id = occurrence_id Trace / span Evidence retained occurrences_total
A F A1 trace-A / span-A Yes, first sample 1
B F B1 trace-B / span-B No, interval sampling 2
C F C1 trace-C / span-C No, interval sampling 3

Detailed evidence sampling is independent of request-occurrence export and OpenTelemetry trace sampling. Sampling out B's details does not remove B's detection. A missing or unsampled trace does not prevent a finding; a trace ID in a finding does not guarantee that the trace reached or remains in the backend.

Within one request, evidence precedence is suspected > unknown > no_evidence. An upgrade moves that request's status bucket without adding an occurrence and can bypass the ordinary evidence interval. Across requests, classification stays independent: ordinary → suspected → ordinary does not make the last request suspected. A retained strongest representative can describe the middle request while last_trace_id describes the latest one. In v2, full representatives and cumulative local counters require explicit local diagnostic output.

V1 attack_evidence_counts partitions aggregate request occurrences into the three statuses. Those are evidence-status counts, never confirmed attack counts. The v2 online body has no cumulative occurrence total; consumers deduplicate received records by instance_id + occurrence_id and group by finding_id, merging same-occurrence upgrades rather than counting each update as a request.

Sampling and migration

Detail Java 1.3.0–1.3.2 schema v1 Schema v2 versions listed above
Occurrence identity evidence_id and its alias occurrence_id occurrence_id; evidence_id removed
Sampled-out evidence evidence_sampled=false, required sources, propagation, ranges, stack arrays empty evidence_sampled=false; empty evidence arrays omitted
Sampling reason evidence_sample_reason, with truncated=true, truncation_reason=evidence_sampled_out for omitted details evidence_sample_reason; sampling alone does not mean truncation
Collection limits Truncation fields describe incomplete evidence collection_complete describes observation quality; truncated=true denotes actual collection or encoding truncation
Aggregation aggregation_status=tracked with process-local occurrences_total; capacity_exceeded omits the total while bounded occurrence export continues Online totals and independent summary/health/loss/truncated-record events removed; backend aggregates received occurrences

Empty or omitted evidence arrays never prove absence of taint. A v1 process-local counter is not a cluster-wide or durable total. Do not require a v1 event schema to validate v2 records. See the released Java v1 reference and Java v2 reference.

Export policy and delivery limits

Java 1.3.4, Node.js 2.0.1, and Python 1.2.4 exclude no_evidence findings from OpenTelemetry Logs and evidence JSONL by default. Set BEACON_SECURITY_EXPORT_NO_EVIDENCE_ENABLED=true to restore their export. unknown and suspected remain eligible, and SBOM is unaffected. Filtering happens before queue admission, consumes no queue or export-rate budget, and is not export loss. Internal detection, deduplication, and same-request upgrades still run. Java 1.3.3 predates the switch; Java 1.3.4 adds it. The A/B/C v1 example above predates this export policy.

Repeated findings can increase log volume even when details are sampled. Queues, memory, record bytes, and rate budgets remain bounded. V1 has observable loss counters and may use a minimal truncation envelope when a normal record cannot fit; that envelope need not retain occurrence or trace fields. V2 removes those separate diagnostic events and does not generate replay events to replace lost occurrences. Missing records may reflect policy, collection limits, queue loss, transport failure, or retention. Counts from received records describe received observations, not a complete census. Neither an occurrence nor an OpenTelemetry API call guarantees durable delivery or backend acknowledgement.