Finding overrides
that survive every scan cycle
Suppress confirmed false positives, accept residual risk, and override scanner severity with a structured record. Three override types, scoped to a workspace and a scan target, with a reason field, creator attribution, and an upsert key that keeps the decision on the next scan rather than re-applying it by hand.
No credit card required. Free plan available forever.
Override scanner findings without losing the audit trail
Every vulnerability programme produces findings that should not stay in the active backlog as the scanner emitted them. Some are confirmed false positives that manual review proved do not reproduce. Some are real findings the organisation has accepted under a compensating control or a deferred-fix plan. Some are real findings whose scanner-emitted severity does not match the workspace severity policy for the asset class. A platform that handles all three with a single suppress button cannot answer the auditor question about why the finding is no longer in the backlog, and a platform that handles them without a target scope silently leaks suppression decisions across environments.
Finding overrides in SecPortal are a structured record on the workspace operating record. Three override types (false_positive, accepted_risk, severity_override) describe semantically distinct decisions. Every override is scoped to a workspace, a finding identifier, and a scan target via a UNIQUE constraint so the same finding on the same target carries one current decision rather than competing records. Every override captures the original scanner-emitted severity, the rationale, the named creator, and the timestamps so the audit trail names who decided, when, why, and for what scope. The override travels with recurring detections through the scan-diff endpoint so the team does not re-triage the same false positive every cycle.
Three override types the platform records as distinct decisions
Collapsing suppression, acceptance, and severity rerating into one flag is the most common path to an audit conversation about whether the programme operates with intentional control. SecPortal uses three structured override_type values so the audit trail distinguishes the decisions.
false_positive
Purpose. The scanner condition fired, manual review proved the issue does not actually reproduce in this environment, and the finding should not contribute to the active backlog or the next scan diff.
What the record carries. override_type is set to false_positive. original_severity is captured for the audit trail. new_severity stays null because the finding is being suppressed rather than re-rated. reason carries the verification rationale (which evidence was checked, why the scanner condition was misfired, what was tested manually).
Audit reading. Auditors reading PCI DSS 6.3.1, ISO 27001 Annex A 8.8, SOC 2 CC4.1, and NIST SP 800-53 RA-5 can read the suppression record as a documented decision rather than a missing finding. The trail names the actor, the timestamp, the reason, and the (workspace, finding, target) scope the suppression applies to.
accepted_risk
Purpose. The vulnerability is real, the exposure is real, and the organisation has decided not to remediate inside the current SLA window for a defensible business reason (compensating control, deferred fix, cost-versus-residual, vendor dependency, risk transfer).
What the record carries. override_type is set to accepted_risk. original_severity is captured so the original scanner-emitted severity stays on the record. new_severity stays null because the severity is unchanged, only the disposition is. reason carries the acceptance rationale (which control compensates, who owns the residual risk, what triggers re-evaluation).
Audit reading. Auditors read accepted_risk as an exception in the risk register rather than as a missing finding. The trail names the actor, the timestamp, the rationale, and the scope. NIST SP 800-53 PM-9, ISO 27001 A.5.7, and SOC 2 CC9.1 read against this discipline as risk treatment evidence rather than as silent backlog drift.
severity_override
Purpose. The finding is real, but the scanner-emitted severity does not match the workspace severity policy for the asset class, the exploitability context, or the environmental factors that the scanner does not see.
What the record carries. override_type is set to severity_override. original_severity captures the value the scanner emitted (the evidence). new_severity carries the workspace decision (the disposition). reason carries the rationale (why the workspace severity differs: environmental factor, compensating control, asset criticality, exploitability assessment). Both numbers stay on the record so the audit can read both and the reason between them.
Audit reading. Auditors read severity_override as a documented re-rating rather than a silent downgrade. The original-versus-overridden delta and the reason field together form the defensible record. The override travels with the finding through subsequent scan cycles via the (workspace, finding, target) match.
The nine fields every override record carries
The scan_finding_overrides table on the workspace operating record is the durable home of every override. Each field has a specific job in the audit trail and the recurring-scan match logic.
workspace_id
The workspace the override belongs to. Every override is tenant-scoped via row-level security so an override created in workspace A cannot be read or modified from workspace B.
finding_id
The stable finding identifier as it appears in the scan result (for example, ssl-cert-expired, sql-injection-on-login-form, or the importer-assigned identifier for a third-party finding). This is the key the next scan cycle matches against.
target
The scan target the override applies to (a verified domain, a connected repository, or the canonical key the importer used). Overrides are intentionally target-scoped: a suppression on staging.example.com does not silently cover production.example.com.
override_type
One of false_positive, accepted_risk, or severity_override. The three values describe semantically distinct decisions and should not be collapsed into a single suppression flag.
original_severity
The severity the scanner emitted before the override. Captured on every override so the audit can reconstruct what the scanner said and what the workspace decided.
new_severity
The workspace severity after the override. Populated only for severity_override; null for false_positive and accepted_risk because those types do not re-rate the finding.
reason
The free-text rationale captured at override time. Required as a discipline even though the column is nullable, because the audit reads the reason field as the documented decision.
created_by
The workspace user who applied the override. The activity log carries this attribution so the auditor knows which named individual exercised the override authority.
created_at and updated_at
Timestamps for when the override was first applied and when it was last modified. The upsert path updates updated_at so revisions to reason or new_severity stay on the same record without losing the original creation timestamp.
The (workspace, finding, target) uniqueness contract
The override is keyed to the workspace, the finding identifier, and the scan target. This is the safety bound that keeps the override discipline defensible across environments and across scan cycles.
One override per (workspace, finding, target) tuple
The UNIQUE(workspace_id, finding_id, target) constraint means the same finding on the same target cannot have two competing override records inside the workspace. Re-applying an override updates the existing record rather than creating a new one, so the audit trail never carries two contradictory decisions for the same scope.
Upsert keeps history without forking the record
The POST endpoint upserts on the unique tuple. Changing the rationale, the new_severity, or the override type updates the same row, refreshes updated_at, and leaves created_at and the original creator in place. Revisions are observable through updated_at rather than producing parallel records.
Target scope is the safety bound
Because the override is keyed to the target, a workspace operating multiple environments (staging, production, customer-specific domains, distinct connected repositories) cannot accidentally suppress a finding everywhere by accepting it on one environment. Each environment carries its own override decision.
Finding identifier match drives recurrence handling
When a new scan completes against the same target, the scan-diff endpoint annotates each finding with the override status by looking up the (workspace, finding_id, target) tuple. The override travels with recurring detections without manual re-application.
The override API surface
The override surface lives on the platform API rather than only in the UI, so workspace automations can list, create, update, and remove overrides through the same record that the UI writes. The endpoints are RBAC-gated through the initiate_scan permission so override authority is explicit and assignable.
GET /api/scans/overrides
List overrides for the workspace, optionally filtered by target. Returns the full override record (override_type, original_severity, new_severity, reason, created_by, timestamps) ordered by created_at descending. RBAC-gated through the initiate_scan permission.
POST /api/scans/overrides
Create or update an override. Requires finding_id, target, and override_type; optional original_severity, new_severity, reason. Upserts on the (workspace_id, finding_id, target) unique tuple so re-applying an override updates the existing row rather than forking it. Validates override_type against the three allowed values and returns 400 on anything else.
DELETE /api/scans/overrides
Remove an override by id, scoped to the requesting workspace. Removing the override means the next scan-diff comparison treats the finding as un-overridden again; the audit log retains the deletion event.
GET /api/scans/diff?scan_a=...&scan_b=...
Compute the diff between two scan executions for the same target. Each finding in new_findings, fixed_findings, and unchanged_findings is annotated with its current override status from the (workspace, finding_id, target) lookup so the diff reads the disposition alongside the change.
How overrides annotate the scan-diff comparison
Scan-to-scan comparisons surface the change between two scan executions against the same target. The diff endpoint reads the active overrides for the workspace and the target, then annotates every finding in the new, fixed, and unchanged buckets with its current override status. The triage queue reads the disposition alongside the change rather than having to re-evaluate every recurring finding from scratch.
new_findings
A finding that appeared in scan B but not in scan A. If an active override exists for the same (workspace, finding, target) tuple, the diff annotates the new finding with its override status so the triager can see immediately that a previous false-positive or accepted-risk decision applies to the recurrence.
fixed_findings
A finding that appeared in scan A but not in scan B. The override annotation surfaces if the missing finding was previously overridden so the closure can be distinguished from a remediated regression on the live record.
unchanged_findings
A finding present in both scans. Override status carries forward across scan cycles. Severity_override findings continue to read against the new_severity workspace decision, false_positive findings continue to suppress from the active backlog, and accepted_risk findings continue to surface in the exception register.
Six failure modes the structured record prevents
Suppressing through a scanner UI flag with no audit trail
Some teams suppress findings inside a scanner UI by toggling a per-finding flag that does not persist outside the scanner session. The next scan or the next analyst sees the finding again. SecPortal records the override in scan_finding_overrides with a named creator, a timestamp, and a reason so the suppression carries across analysts and scan cycles.
Re-triaging the same false positive every scan
When the override is not keyed to the (workspace, finding, target) tuple, every recurring scan surfaces the same false positive in the new_findings bucket and consumes the same triage time. The platform keys overrides to the recurring identifier so the false-positive decision applies once and travels with the finding.
Silent downgrade with no original severity preserved
Severity overrides that overwrite the scanner-emitted value without preserving the original number lose the evidence the auditor needs to defend the workspace decision. SecPortal stores both original_severity and new_severity on every severity_override record so the delta and the reason are reconstructable.
Collapsing false positive, accepted risk, and severity override into one suppression flag
A single suppress button cannot answer the auditor question that asks why a finding is no longer in the backlog. SecPortal uses three structured override_type values so the audit trail distinguishes a false positive (the issue does not reproduce) from an accepted risk (the issue is real but accepted) from a severity override (the issue is real, the workspace severity differs from the scanner severity).
Suppressing on one environment and quietly covering all
An override scoped only to a finding identifier (without a target) silently applies the suppression to every asset the same finding appears on. SecPortal enforces target scoping through the UNIQUE(workspace_id, finding_id, target) constraint so staging suppressions never leak to production by accident.
Anonymous override authority
Overrides applied through a shared service account, an automation script, or a group inbox hide the named individual who exercised the suppression authority. SecPortal records created_by as a workspace user reference so the activity log carries the named actor.
An eight-item operating checklist
The override feature is only as defensible as the discipline that runs against it. This checklist is the day-to-day operator routine for keeping the override register honest.
- Confirm the override type matches the underlying decision (false_positive for a non-reproducing condition, accepted_risk for a real but accepted exposure, severity_override for a real finding with a rerated severity)
- Capture the original_severity on every override so the audit trail preserves the scanner-emitted value
- Populate the reason field even though the column is nullable; the audit reads the reason as the documented decision
- Scope the target precisely (do not apply a production override to a staging target by reusing the same record)
- Apply severity_override only when the workspace has a documented severity policy that defends the new_severity value
- Review accepted_risk overrides on a defined cadence; long-tail accepted risk is the slowest path to a stale exception register
- Re-evaluate false_positive overrides when the scanner rule version changes; an old false positive can become a true positive after a rule update
- Use DELETE rather than mutate when the underlying condition has changed so the activity log carries the removal event
Five enterprise scenarios the override feature operates against
Internal vulnerability management programme false-positive triage
A vulnerability management team runs weekly external scans against the production estate, reviews the new findings from each cycle, and confirms a handful as false positives caused by scanner heuristics that do not reflect the actual deployment. Each confirmed false positive is recorded with override_type false_positive, original_severity preserved, the reason field explaining what was verified manually, and the (workspace, finding, target) tuple. The next weekly scan annotates the recurring detection with the suppression so the team does not re-triage the same false positives every cycle.
AppSec acceptance of compensating-controlled exposure
An AppSec team running authenticated scans against an internal application has a known SQL injection in an admin-only endpoint protected by network segmentation and an internal-only WAF rule. The fix is scheduled for the next major release but cannot ship inside the current SLA window. The team records override_type accepted_risk with original_severity preserved as high, the reason field naming the compensating control, the named owner, and the review trigger. The exception register reads against the accepted_risk overrides for the workspace and surfaces them on the leadership dashboard alongside the open backlog.
GRC and audit evidence preparation
A GRC team preparing for ISO 27001 surveillance and SOC 2 fieldwork needs to demonstrate that the vulnerability programme operates suppression and risk acceptance as controlled activities rather than silent exception drift. The list of overrides per workspace, with override_type, original_severity, new_severity, reason, created_by, created_at, and updated_at, exports through the activity log and feeds the auditor sample request directly. ISO 27001 Annex A.8.8, SOC 2 CC4.1, and PCI DSS 6.3.1 read against this evidence as a defensible discipline.
Security engineering severity calibration for environmental risk
A security engineering team running code scanning on a connected repository receives a Semgrep finding rated medium for a hardcoded secret in a test fixture file that never ships to production. The team records override_type severity_override with original_severity medium, new_severity low, and the reason field explaining that the secret is fixture-only and not deployed. The override travels with subsequent code scans against the same connected repository so the recurring detection inherits the workspace severity rather than the scanner default.
Multi-environment risk acceptance with environment-specific scope
A security operations team operates the same application across staging, production, and customer-specific tenant subdomains. A known vulnerability has been accepted on the staging environment because the data is synthetic and the fix is staged for the next deploy. Each environment carries its own override record because the (workspace, finding, target) tuple is enforced; the acceptance on staging.example.com does not silently cover production.example.com or customer-a.example.com.
How audit frameworks read the override record
Auditors reading vulnerability programme effectiveness against the common frameworks evaluate three artefacts: the override policy (the matrix of override types and required roles), the per-finding override history (the actor, the timestamp, the reason, the scope), and the review cadence (which overrides were reviewed and which were revoked). The structured record supplies the second artefact directly.
| Framework | How the override record reads as evidence |
|---|---|
| ISO 27001:2022 | Annex A.8.8 Technical Vulnerabilities reads override records as evidence that suppression and acceptance are controlled activities; A.5.7 Threat Intelligence reads against severity_override reasons that cite environmental risk; A.5.4 Management Responsibilities reads created_by attribution as accountability evidence. |
| SOC 2 Trust Services Criteria | CC4.1 Monitoring of Controls reads the override audit trail as monitoring evidence; CC7.1 System Operations Monitoring reads the override-to-scan-diff link as operations evidence; CC9.1 Risk Mitigation reads accepted_risk records and severity_override records as risk-treatment evidence. |
| PCI DSS v4.0 | Requirement 6.3.1 reads override records as ranking-and-treatment evidence; 6.3.3 reads severity_override reasons as evidence the workspace applies a defensible severity rather than blindly trusting scanner output; 11.3 reads the override-annotated scan diff as evidence the team handles recurring detections through documented decisions. |
| NIST SP 800-53 Rev. 5 | RA-5 Vulnerability Monitoring reads false_positive overrides as documented suppression rather than silent omission; SI-2 Flaw Remediation reads accepted_risk records as the deferred-remediation evidence; PM-9 Risk Management Strategy reads the accepted_risk register as the risk-acceptance discipline. |
| NIST CSF 2.0 | ID.RA Risk Assessment reads severity_override reasons as the environmental-context calibration; GV.RR Governance Roles and Responsibilities reads created_by attribution and the RBAC gate as accountability evidence; PR.IP Information Protection reads the suppression-and-acceptance records as part of the operating discipline. |
How finding overrides fit the rest of the platform
The programme workflow that wraps the override records (exception types, named owners, expiry cadence, review triggers, audit-defensible documentation). The override feature is the platform record; the use case is the operating discipline that runs against the record.
The conceptual layer above the feature: why suppression, deferral, and risk acceptance are three different decisions, why each carries different audit weight, and how the suppression policy bounds the override authority.
The triage workflow that produces the override decision in the first place: which scanner output is a true positive, which is a false positive, which carries environmental factors that change the workspace severity. The override is the artefact the triage produces.
The record store overrides annotate. Findings management carries the captured finding with the source-tool severity, the CVSS 3.1 vector, the asset binding, and the structured state enumeration; the override decorates the disposition without rewriting the underlying finding.
The audit trail every override change feeds. The activity log captures the actor, the timestamp, and the action so the override history is reconstructable even after the override itself has been mutated or deleted.
The role-based access control that gates who can apply overrides. The initiate_scan permission gates the override API surface, so override authority is an assignable, observable team capability rather than an implicit privilege.
Scope and honest limits
Finding overrides are a record discipline on the workspace operating record, not an enterprise governance suite. The list below names what the feature does and does not do, so operators choose the right place for the work that sits outside its scope.
- SecPortal records the override on the workspace operating record; it does not synchronise the override decision to Jira, ServiceNow, Linear, Azure DevOps, or any other external ticketing system.
- The override carries no automated expiry; programme operators run their own review cadence against the override list and remove or rewrite overrides when the underlying condition changes.
- Override authority is gated by the initiate_scan team permission; SecPortal does not ship a separate override-only role or an approval-routing workflow for severity_override or accepted_risk decisions. Programmes that require an explicit approval workflow operate that policy outside the feature.
- The override annotation on the scan-diff endpoint reads the current override status at the time of the diff; it does not reconstruct the historical disposition for diffs computed against past scans without re-querying the activity log.
- The reason field is free text; SecPortal does not enforce a structured rationale schema. Programmes that require a structured exception template land the template into the reason field through their own discipline.
Where to read next
For the programme-level workflow that wraps the override records (exception types, named owners, expiry cadence, review triggers), see the vulnerability acceptance and exception management workflow.
For the conceptual layer above the feature (why suppression, deferral, and risk acceptance carry different audit weight, and how the suppression policy bounds override authority), see the scanner finding suppression and deferral controls explainer.
For the triage workflow that produces the override decision (which scanner output is a true positive, which is a false positive, which carries environmental factors that change the workspace severity), see the scanner result triage workflow.
For the analysis of how vulnerability exceptions decay between audits and why a review cadence on accepted_risk overrides matters, see the risk acceptance decay rate research.
For the operational queue that surfaces accepted_risk and aged overrides alongside the open backlog, see the vulnerability backlog management workflow.
For the design rationale behind treating overrides as three distinct structured classes on the operating record (false positive, accepted risk, severity override) rather than as a single suppression flag, and how the override schema sits alongside the seven field classes the finding record carries, see the vulnerability programme data model research.
Underlying platform mechanics worth knowing
RBAC at the API boundary
The override endpoints require the initiate_scan permission on the requester's team role. Viewer roles cannot apply or revoke overrides; the permission attaches to team management so the override authority is an explicit, observable team capability.
Activity log as override history
Every override change feeds the activity log with the actor and the action. Audit teams reconstruct the override history from the log even after a record has been mutated or deleted.
Continuous monitoring honours overrides
Scheduled scans through continuous monitoring feed the same scan-diff endpoint that annotates findings with their override status. Daily, weekly, biweekly, and monthly cadences do not require re-applying overrides every cycle.
Tenant isolation by RLS
Row-level security on the scan_finding_overrides table restricts every read and write to the workspace of the requesting user. Workspace A cannot read or modify an override in workspace B, and the policy is enforced at the database rather than only in the application layer.
Stop re-triaging the same false positive every scan
Capture the override once with a reason and a target scope. The platform keeps the decision on the next scan diff, with the actor, the timestamp, and the rationale on the audit trail.
No credit card required. Free plan available forever.