Skip to content

Vectasec · Concepts

Findings, evidence and compliance

How Vectasec detectors produce findings, what an observation is, how the ledger is tamper-evident, and how posture and compliance are scored.

On this page

Every Vectasec finding is backed by a stored observation: the exact fact a detector read from the target. This page covers how findings are tracked, how evidence is kept and cited, and how findings roll up into posture and compliance.

Detectors#

A detector is a deterministic rule with a rule id such as KAFKA.AUTHZ.NO_AUTHORIZER, a default severity, a title and the resource kinds it runs on. The registry holds 448 detectors: 57 critical, 165 high, 146 medium, 54 low and 26 info. vectasec rules lists them grouped by rule-id prefix.

A finding is identified by a fingerprint of tenant, connection, rule id and resource ext_id, so the same problem on the same resource stays one finding across scans and keeps its status, assignee and history. When a detector returns several hits for one resource, such as one per listener, they share one finding filed at the worst severity, and every hit writes its own observation.

Observations#

An observation states a fact read from the target, including the observed value, such as authorizer.class.name is not set. It never states a conclusion like "the broker is insecure". It also records its provenance, such as the config key or API call.

A scan's results land in one transaction: resources are upserted, edges are rebuilt, detectors run, findings and their observations are written together, and compliance is re-evaluated. The scan never records a finding without its evidence. Resources that disappear are marked stale, not deleted. Observations are the only thing AI triage may cite.

Three outcomes per check#

OutcomeWhat it meansEffect on mapped controls
FindingThe detector read evidence of a problemfail
No findingThe detector read the evidence and found nothing wrongNone on its own
Not assessableThe detector could not read the evidence it needsunknown

A not-assessable finding has assessment set to not_assessable and an assessment_reason, for example an AWS AccessDenied, a Kubernetes 403, or a Kafka config value the broker redacts. It is a coverage gap, not a vulnerability, and the console marks it "Not assessed". To close one, grant the read the reason names and rescan.

Finding lifecycle#

POST /findings/{id}/status sets open, triaged, in_progress, resolved or accepted, with an optional note. POST /findings/{id}/assign sets an assignee, and null unassigns. Both need the analyst role or higher, and each change writes an audit entry with the previous and new value.

A rescan keeps the status a person set and never recreates a resolved finding as a new one. While the rule still fires, each scan updates last_seen and appends observations, so if last_seen still advances after you rescan, the fix has not landed.

To silence a rule on purpose, create a suppression. suppressed cannot be set as a plain status.

json
{
  "rule_id": "KAFKA.LISTENER.PLAINTEXT",
  "scope": { "ext_id": "broker-1" },
  "reason": "Isolated lab broker with no production data",
  "expires_at": "2026-12-31T00:00:00Z"
}

Send this to POST /suppressions with the analyst role or higher. reason is required and cannot be blank. scope is {} for every resource in the tenant, ext_id for one resource, or kind for every resource of a kind; other keys are rejected. An ext_id scope matches that id on every connection in the tenant. expires_at is optional. Suppressions take effect at the next scan, and a suppressed finding returns to open at the first scan after expiry in which the rule still fires.

The evidence ledger#

  • The database rejects any UPDATE to the audit log, and any UPDATE that changes an observation's claim, tenant, finding, provenance, timestamp or hashes.
  • Each observation carries a hash: SHA-256 over the previous observation's hash, the tenant, the finding and the claim, chained to the previous one for the same finding in write order.
  • vectasec doctor --verify-ledger recomputes the chain for every tenant and prints chain intact, or lists the breaks (up to 20) with their finding, observation and reason. It exits non-zero on a break or any other wiring problem, so you can run it on a schedule or in CI.
  • A signed-in member can also run the ledger check for their own tenant.

The citation gate#

AI outputs and their citations are stored alongside the evidence ledger. Deferred constraint triggers check at commit that every output cites at least one observation, and that each cited observation belongs to the output's own tenant. Removing an output's last citation is also rejected. An uncited triage fails at commit and is never stored.

Grounded AI triage#

POST /findings/{id}/triage returns a summary, business risk, exploitability, a priority from P0 to P3, a confidence of high, medium or low, the model, and the citations: each cited observation's id, claim, provenance and time. It needs the analyst role or higher, and returns 422 when a finding has no observations.

The model sees only this finding's newest 25 distinct claims, numbered and marked as untrusted data, each capped at 2,000 characters. A JSON schema requires citations, and each is range-checked. Providers are tried in order depending on which keys you set: Anthropic, then OpenAI, then Gemini (see Configuration). With no key, when every provider fails, when a model refuses, or when an answer cites anything invalid, a deterministic summary that quotes the newest evidence is used instead, labeled rule-based-fallback.

Results are cached on the finding's fingerprint plus a hash of its evidence, so changed evidence regenerates the text. A rule-based summary is cached the same way, so after you add a provider key, call with ?force=true to bypass the cache.

The rule's severity sets a priority floor. A model can raise urgency but never lower it:

SeverityFloor
criticalP0
highP1
mediumP2
low, infoP3

The response always carries the model's own answer in model_priority, and sets priority_floor_applied to true when the floor moved it.

Posture score#

The score is computed only from findings in status open, including open not-assessable ones. Triaged, in-progress and accepted findings do not lower it.

text
weighted = 10 * critical + 4 * high + 1.5 * medium + 0.5 * low
score    = 100 if weighted is 0
score    = max(1, round(100 * (1 - weighted / (weighted + 40)))) otherwise

Info findings carry no weight. Grades are A at score >= 90, B at >= 75, C at >= 60, D at >= 40, and F below that. Each completed scan records a snapshot, at most one per tenant every 5 minutes, shown as a trend on the console Overview. vectasec posture prints the score and its breakdown.

Compliance mapping#

Findings map to 30 controls in 5 frameworks. Use the key as {framework} in GET /compliance/{framework}.

FrameworkKeyControls
PCI DSS 4.0.1PCI_DSS_4_0_117
NIS2NIS24
DORADORA3
HIPAAHIPAA3
MCP specification 2025-11-25MCP_SPEC_2025_11_253

Controls are re-evaluated inside every scan's transaction and after each attestation. A status change on a finding, such as marking it resolved, reaches the controls at the next scan or attestation.

  • fail: an assessed finding in open, triaged, in_progress or accepted maps to the control. Accepting a risk does not clear the failure. Resolved and suppressed findings do not count.
  • unknown: nothing assessed fails the control and no live attestation exists. Not-assessable findings land here.
  • pass: only through POST /compliance/{framework}/{control_id}/attest by a signed-in user with the analyst role or higher, with a required scope naming what was attested over which systems and period, and an optional note and expiry. Expired attestations are ignored, and a live failing finding overrides an attestation.
  • not_applicable: meant for frameworks a tenant is not subject to. There is no setting to declare them yet, so every framework is evaluated today.

Only mappings rated A (the control text describes the misconfiguration) or B (defensible once a condition holds) are included; weaker mappings are left out. Some B mappings also require an asset scope label, such as ePHI, cardholder data or public-facing. No connector or setting applies those labels yet, so those mappings are skipped. Every HIPAA mapping is gated on ePHI, so HIPAA controls do not fail from scan findings today.

Next steps#