Guide 006 decision guide

How to separate a vulnerability from a hardening recommendation

Decide whether a code issue is a vulnerability candidate, hardening opportunity, or unresolved claim using a specific actor and harmful effect.

A vulnerability candidate is a supported claim that a weakness may let an actor break a security rule and cause a harmful effect in the assessed system. Call it a verified vulnerability finding when the path, controls and effect have been corroborated within the stated scope, with review proportionate to the consequence and uncertainty. A hardening recommendation improves a control or reduces dependence on one, but the reviewer has not established that this particular system has an unauthorized path. A missing control is a reason to investigate; it is not, by itself, proof that an attack works. MITRE's CWE glossary distinguishes a weakness that could contribute to vulnerabilities from a vulnerability that can be exploited with a negative effect.

The answer changes when you find another entry path, a deployment condition, or a stronger compensating control. Record those facts and revisit the classification. If the evidence is incomplete, label the claim unresolved and name the check that would settle it. Do not hide an uncertain candidate in a hardening ticket, or put a recommendation in a confirmed vulnerability queue just because the suggested change is sensible.

Ask what rule could actually be broken

Start with a security rule expressed in application terms: a member of workspace A may export only workspace A's invoices. Then trace the claimed path: who can make the request, what input they control, which code accepts it, which controls apply, and what data or action follows. This makes the classification about a concrete behavior rather than a scanner rule name.

For authorization, the relevant question is whether the current actor can perform the current action on the current object. A login check alone does not establish that. The OWASP Authorization Cheat Sheet calls for permission checks on every request and a default denial when no rule grants access. Its guidance is a sound design criterion, but a deviation from guidance still needs system-specific evidence before a particular attack is described as real.

Use three tests in order:

  1. Boundary: Identify the protected asset and the actor who should be denied. If no security property or affected asset can be named, the item may be maintainability, configuration hygiene, or an unscoped concern.
  2. Path: Identify a reachable entry and the decisions between input and effect. A code line that lacks a check may sit behind an effective gateway, or it may be reachable from a second path that bypasses that gateway.
  3. Effect: Explain the unauthorized read, write, privilege change, or disruption that the path permits. If the only statement is “this could be safer,” record a recommendation. If effect depends on an unknown deployment fact, hold an investigation.

This is a classification method, not a requirement to perform an exploit. Source review can support a path and identify conditions. It cannot establish every fact about a running deployment.

Illustrative case: an invoice export worker

In this fictional B2B application, an HTTP gateway checks that a signed-in user belongs to a workspace before accepting an invoice export request. A worker later retrieves the invoice file. A scanner flags the worker because it accepts workspace_id from a queued message and contains no user membership check.

The engineer reads the gateway, the two producers that can publish to the queue, the worker's retrieval call, and the deployment policy. She records the following decision sheet. These are illustrative facts, not a report of an assessed product.

Question Evidence in the illustrative review Decision effect
Who can request work? The public gateway verifies current workspace membership; one internal retry producer republishes a previously accepted job. No unauthorized actor is yet shown entering through either producer.
Can a caller replace the workspace? Both producers construct the message from a server-side job record; the HTTP request's workspace field is not copied into it. The flagged parameter is not shown to be attacker controlled.
Can the worker be called another way? The deployment review finds no third producer, but a pending migration may add one. Recheck after the migration; the inventory is time bound.
What would happen if a foreign workspace ID reached it? The worker's lookup would read that workspace's export without another membership check. There is a serious missing defense at this boundary.

On those stated facts, add an authorization check in the worker is a hardening recommendation. It removes reliance on every producer preserving the gateway's decision. The reviewer does not claim a current cross-workspace vulnerability because no route for an unauthorized user to place a foreign ID on the queue has been established. The finding note still names the possible consequence, the producer inventory, and the migration that would change the decision.

Change the facts, then change the classification

The worker's missing check is the same in all three variations below. The distinguishing facts are supplied for this exercise; no queued job or export was executed.

Variation Actor-to-effect evidence Classification and work
Original producers Both use an authorized server-side job record; no foreign workspace selector can enter through the inventoried paths. Hardening: add a worker boundary before the planned producer migration. Preserve the producer inventory supporting today's decision.
New retry route A member may retry their own job, but the producer replaces its workspace with a request-supplied workspace; the worker uses that replacement and returns the export through the member's job. Supported vulnerability candidate: the retry route supplies the missing unauthorized path. Independently check the foreign read and delivery before approval.
Incomplete producer inventory HTTP producer is checked; retry and migration publishers are unavailable. Unresolved: the reviewer cannot establish either a complete defeating control or an unauthorized producer. Obtain those two publisher definitions.

There is a subtler fourth question: when must permission still hold? Consider a member who legitimately requests an export at 09:00, loses membership at 09:05, and receives the result at 09:10. A server-created job record proves authorization at acceptance. It does not answer whether the product promises authorization at delivery.

Two explicit policies produce different conclusions:

  • Under an acceptance-time policy, already accepted jobs may finish for their original recipients. The delayed result does not violate that policy merely because membership changed; assess whether that policy is appropriate as a separate design question.
  • Under a delivery-time policy, revoked members must receive no new export. A worker or retrieval layer that checks only the old job approval may violate the rule. Trace the actual delivery permission and revocation behavior before classifying it.

The filled decision for a product whose written policy requires delivery-time membership is: the original “user can submit a foreign workspace” claim remains unsupported, but the delayed-delivery path is a separate candidate if no current permission check applies. Ask the product owner for the policy and the developer for the result-delivery path. Do not smuggle a newly invented security requirement into the original scanner verdict.

This distinction keeps hardening useful: the team can improve a fragile boundary immediately while remaining precise about which unauthorized behavior has actually been established.

Separate classification from priority

Do not turn “hardening” into “low priority” or “vulnerability” into “fix today.” A hardening change can be urgent before a new producer launches. A supported vulnerability can require rare privileges or configuration and rank below another issue with wider exposure. Keep these decisions in separate fields:

Field Question it answers
Classification Is a harmful unauthorized path supported, merely possible, or currently absent on known paths?
Technical severity What could the flaw do under the stated conditions?
Practical difficulty What access, knowledge, timing, controls and effort would an attacker need?
Business priority Given affected customers and plans, when should this team act?

CVSS v4.0 explicitly separates intrinsic vulnerability characteristics from threat and environment metrics, and says organizational considerations outside CVSS also inform remediation. Use a score only after the underlying scenario is clear. An unexplained score cannot settle whether the original claim is a vulnerability.

An unmet OWASP ASVS verification requirement is useful evidence of a control gap. ASVS provides security requirements and a basis for testing controls; it does not automatically establish that a specific actor can exploit a specific application. Record the requirement version and the tested scope, then describe the actual path or the missing evidence. Avoid saying “ASVS compliant” merely because one item passes.

Leave a decision another reviewer can reverse

Use this short record in the ticket or review log:

  • Security rule: Who must be denied which action on which asset?
  • Actor and input: What can the actor control?
  • Observed path: Entry, controls, effect, with revision and evidence references.
  • Counterevidence: Controls or scope limits that could break the path.
  • Disposition: Vulnerability candidate for independent verification / hardening recommendation / unresolved.
  • Change trigger: The fact or future change that would require a new decision. Owner and next check: Who will check it, against what deployment or revision, and when?

If a worker boundary is missing a check but the only producer inventory is incomplete, use unresolved. Ask the platform owner for the current producer list and route configuration, and ask the code owner to trace message construction. If that evidence cannot be obtained, retain the uncertainty and consider a preventive change on its own merits. Do not turn the absence of a demonstrated path into proof of safety.

Write useful hardening work without borrowing a vulnerability label

For the original worker design, a good hardening ticket can be substantial without alleging a current cross-workspace exploit. Describe the dependency being reduced: every authorized producer must construct the workspace from the trusted job record, and the worker currently relies on that behavior. The proposed change moves a check closer to the protected export and makes a future producer error less likely to become a data-access error.

A filled task might say:

Add a worker-side check that the requested export belongs to the server-created job's authorized workspace before retrieval. Preserve the existing producer checks. Review ordinary exports and legitimate retries, including jobs accepted before the worker release. The current source review did not identify an unauthorized producer; the reason for prioritizing this change is the planned producer migration.

That is a clear engineering objective. It does not require an inflated severity label to explain why the work belongs before launch. It also names a compatibility question: a new worker should not reject legitimate older jobs simply because a new field is missing without an explicit migration decision. The implementation owner must choose how older jobs carry trustworthy context or are safely retired.

Do not close the task by counting the new check. Follow the field used by that check back to its source. Comparing two request-derived workspace values supplies no independent authority. In this case the comparison must use the server-created job's authorized scope or another enforcement point whose trust is established. Review the no-match behavior as carefully as the normal branch.

Finally, keep the classification history separate from the code history. If the new producer later reveals a real unauthorized path, create or update the vulnerability claim with that new evidence and its affected versions. The older hardening decision was based on a narrower inventoried producer set. Its recorded migration trigger explains why the conclusion can change without pretending the original review had discovered the later defect.

Next step: Classify one open alert with the seven-line record. Use the uncertainty template if a deployment fact is missing, or the dispute workflow when a developer has counterevidence that changes the claim.