Guide 019 workflow

How to keep rejected claims visible without polluting the fix queue

Keep rejected scanner claims available for later review while giving developers a clear worklist of repairs, investigations and their evidence.

Keep actionable work, unresolved claims and past decisions distinguishable. This article uses three illustrative views: To fix for supported open issues, Investigations for plausible claims with a missing decisive fact, and History for rejected claims with their counterevidence and decision trail. Use your existing tracker's equivalent labels; these names are not a required workflow. Precautionary repair can also be assigned while a claim remains unresolved, provided the task does not present uncertainty as a confirmed defect. A rejected alert belongs in History so a reviewer can understand why it was dismissed, compare it with a future alert and reopen it if the facts change. It must not be counted as an open vulnerability, sent to a developer as an approved fix task or quietly erased from the quality record. A former true issue that was fixed or risk-accepted needs its own disposition; neither is a false positive.

This separation is a useful design choice for any AppSec team, regardless of scanner. NIST's SSDF, PW.7 calls for issues found by code review and analysis to be recorded and triaged in the team's workflow. A preserved rejection is part of that triage record, not evidence that the system is now vulnerability-free.

Treat the disposition as a decision about one claim

A scanner may report “arbitrary file disclosure,” but reviewers must decide a narrower statement: which actor, through which route, could read which protected file at which revision? Rejecting that statement does not certify the entire file-handling subsystem. A new route, a changed control or a different actor may require a new review.

Define a stable claim ID and keep the original alert IDs under it. Bind every decision to an immutable revision and assessed scope. Record who decided, what evidence supported and defeated the claim, which questions remain outside scope, and what would trigger a revisit. If a later scan repeats the exact warning on unchanged code, the old record helps the reviewer answer efficiently. If the code changed, the history is context, not a substitute for a current verdict.

OWASP's Web Security Testing Guide reporting section recommends clear scope, limitations and reference IDs for findings. Rejected claims deserve similarly traceable identifiers, while staying clearly outside the verified findings list. The report guidance concerns testing reports; use it here as an evidence-record pattern, not as a claim that a source-only review was a penetration test.

An illustrative rejection record

Illustrative example, not a customer finding: Maple Ledger's scanner flags GET /exports/{reportId} at revision m58 as arbitrary file read. The cited handler calls exports.open(reportId). The reviewer traces the actual route and discovers that reportId is looked up in an account-scoped export registry. The registry returns a server-generated storage key only when the report belongs to the caller's current account; the client cannot supply a path. A second reviewer confirms that this is the adapter wired to the assessed route. The specific arbitrary-file-read claim is rejected. The review did not assess a separate legacy bulk-export worker.

Here is a compact History entry that someone can understand months later:

Field Recorded value
Claim ID and raw alert ML-CLAIM-0062; scanner run m58-scan-3, rule path-read-7.
Alleged harm Ordinary account user reads a file outside their permitted exports through GET /exports/{reportId}.
Scope and revision Maple Ledger API route and storage adapter at immutable revision m58; legacy worker excluded.
Supporting evidence Handler accepts a request ID and calls exports.open; valid source citation.
Defeating evidence Current account-scoped registry lookup produces a server-generated storage key; reviewer followed the denial path.
Disposition Rejected for this route and revision after independent review on 18 October 2026.
Reopen trigger Registry, route wiring, storage adapter, account binding or export worker changes; new evidence of a bypass.
Access and retention Source-free summary in restricted review history; exact source ranges available only under current authorization.

The record does not say “file access is safe.” It says why one proposed path failed. If the registry lookup is merely suggested by a comment or is not actually called on this route, move the claim to Investigations. If the legacy worker can access the same storage area, give it a separate claim and scope rather than silently folding it into this rejection.

Use history when the alert returns

Continue Maple's constructed case across three events. Counts below concern this one claim family only; History counts retained decision versions, Investigations counts current candidate versions, and To fix counts current approved issues. These views can intentionally contain different versions of the same family.

Event Reviewer comparison History Investigations To fix
m58 rejection Scoped registry and route binding defeat the claim. 1 0 0
Identical rerun Artifact, adapter, account source and rule meaning unchanged; link the new raw alert to the existing decision. 1 0 0
m61 adapter refactor New implementation looks up report ID globally; prior account predicate is absent. 1 1 0
m61 independently approved Supplied review checks actor reachability, foreign report delivery and applicable deployment; assume approval for this exercise. 1 0 1

The m58 decision is not erased when m61 changes the behavior. It was a valid conclusion about its own revision. The new finding should name the narrower supported effect—access to another account's registered report—rather than inherit an unproved “arbitrary filesystem read” label. A global report registry still need not let a client choose any filesystem path.

A recurrence check therefore compares the facts that defeated the original claim, not only the alert title:

previous: trusted account A + report ID -> registry(A, ID) -> server key
current:  report ID -> registry(ID) -> server key
changed:  account predicate removed
result:   prior rejection no longer applies to current path

This is a useful place for a small human review: the raw alert may be byte-for-byte similar while the countercontrol has changed. Conversely, a line-number shift with the same implementation and binding need not trigger a full reinvestigation merely because the line number differs.

Now a reviewer loses access to the source repository. Keep the authorized source-free disposition summary available only under the review system's own permissions; the old link must not grant continued source access. A permitted colleague can obtain current evidence through normal authorization. If no authorized reviewer can inspect m61's source, leave that decision unresolved rather than copying the m58 label forward.

These are suggested record semantics, not a claim about a shipped product. A real tool may model versions differently. Check its state transitions and ensure dashboards label what they count. A history entry is reusable reasoning; it is neither a permanent exemption nor a vulnerability to add to today's fix total.

Design the history view around the next investigation

The most useful history search is usually about a control, route or component, not the old alert title. Maple's future reviewer may search for the export registry, the account predicate or the storage adapter. Include those source-free identifiers in the decision record so the earlier reasoning can be found without storing a second copy of sensitive source.

Keep the current disposition obvious when a search returns several versions. The m58 rejection and m61 finding can both be correct about their respective revisions. A search result should show their scope and version rather than presenting two contradictory status badges. Link the records as a claim family, but make the currently applicable decision explicit for each assessed artifact.

A useful history result might read:

Export registry lookup, m58: rejected cross-account claim because the mandatory query includes the trusted account. Superseded for current assessment by m61 investigation after the account predicate changed. Original rejection retained for historical reporting.

This helps the developer recognize the meaningful change before opening the full packet. It also avoids a misleading headline such as “previously safe code,” which could suggest a broader review than the account-bound lookup actually received.

Separate reusing evidence from reusing a verdict

A prior record can save work even when its verdict no longer applies. The m58 review identifies the relevant route, storage wrapper and delivery path. Those references are good starting points for m61, but the reviewer must compare the changed adapter and any dependent assumptions. Reuse the unchanged evidence with its source and reason; replace the evidence invalidated by the refactor.

If the new alert is only a line-number shift, a bounded comparison may establish that the original control and binding remain unchanged. If the object source or account provenance changed, the previous rejection cannot be copied. This distinction makes history useful without turning it into a permanent suppression list.

The same principle applies when the scanner changes. A broader rule may raise a different allegation at the same call. For example, one alert could concern arbitrary path selection while another concerns cross-account access to registered reports. The registry's server-generated storage key can defeat the first without settling the second. Preserve both claims in their precise form instead of merging them by file and line alone.

Keep reporting counts understandable

Decide whether a dashboard counts raw alerts, claim families, decision versions or current work items. Maple's table intentionally counts history versions separately from current work. That means a family may appear in history and in the current fix queue without being two present vulnerabilities. Label those units next to the numbers.

For a monthly summary, distinguish new rejections from reused historical decisions and from reversed conclusions. A repeated alert resolved by unchanged counterevidence is operational work saved; it is not a newly fixed vulnerability. A mistaken rejection corrected for the same original revision is a quality correction. A valid rejection followed by a later regression is changed software. These events should not share one “reopened” metric without an explanation.

This accounting lets the AppSec lead answer the practical question history is meant to solve: did the team avoid repeating old work while still noticing when the assumptions changed? A growing archive alone does not establish that benefit. Review a small set of recurrences and check whether the next owner found the right decision and used its limits correctly.

Keep enough evidence without copying sensitive source everywhere

The review history should store the claim, sanitized explanation, immutable revision, allowed path and line references, hashes, reviewer, disposition and revisit trigger. Raw source, secrets, private report excerpts and exploit material should not be copied into broad dashboards or notifications. A reviewer who needs original source should access it through the organization's current permission boundary. Restrict History as carefully as open findings: a dismissed claim can still reveal a sensitive route or asset.

If the original evidence later becomes unavailable, say so. A hash and line reference help detect whether a citation changed; they do not reconstruct missing source. Preserve the strongest decision the remaining record can support, and send a changed or contested claim back to Investigations. If a suppression prevents the scanner from emitting future alerts, inventory that suppression alongside History so the absence of a new warning is not mistaken for a fresh clean review.

Start by moving one week's rejected alerts into a searchable restricted History view with the eight fields in the example. Check that none appear in the open verified count, and that each can be reopened at a new revision. Use the false-positive review workflow for the decision itself, then measure precision with the full reviewed cohort.