Guide 037 workflow

How to record compensating controls without calling the code fixed

Record a compensating control’s coverage, evidence, failure response and expiry while keeping the underlying defect and unfinished repair visible to its owner.

Keep the vulnerability open and record the compensating control as a separate, time-bound mitigation. Describe the exact path it protects, its owner, the evidence that it works, how it can fail, and when the underlying fix will be reassessed. A web application firewall rule, reduced permission, feature flag or network restriction may lower exposure without removing the unsafe code. If the control covers only one ingress path, say so. If its operation has not been verified, call it proposed or unverified, not effective. Close the vulnerability only after a new assessment supports that the unsafe behavior is gone or prevented at its proper control point.

OWASP defines a virtual patch as a policy enforcement layer that blocks matching exploitation attempts without changing the vulnerable code. That distinction is the heart of the status record. A mitigation can buy time; it cannot rewrite the code history or prove that every route to the weakness is blocked.

One illustrative mitigation, three different truths

Illustrative case, not a real customer finding: MasonLedger has an older report-builder service. An independently reviewed finding at revision R7 identifies a query assembled from a user-controlled filter value. The team cannot replace the query builder before a contractual reporting deadline. Operations prepares a rule for the public application gateway to reject the request pattern associated with the finding. A security engineer checks that the gateway denies the agreed synthetic request in staging.

Three statements can now be true at once:

  1. The vulnerable query construction remains in revision R7.
  2. The public gateway may block a defined set of requests to the report route while the rule is active.
  3. An internal scheduled-report worker reaches the query builder without going through that gateway; its exposure and permissions require separate review.

Calling the ticket “fixed” would collapse those statements and conceal the worker path. Calling the gateway rule useless would discard a potentially valuable reduction in exposure. At this stage the useful status is open — gateway pattern checked in staging; production enforcement unverified; internal path under investigation. OWASP's SQL injection prevention guidance describes parameterized queries as a primary way to separate data from query structure. MasonLedger's planned code change should address the query construction, while the gateway rule is tracked as a temporary outer control.

Record the control in a repeatable workflow

1. Freeze the underlying claim. Start from an independently supported finding: affected revision, actor, entry point, unsafe operation, expected security rule and business effect. Do not treat a scanner alert as a confirmed vulnerability without review. Keep that finding ID stable while mitigation state changes. Output: the original technical claim and its evidence, with no new conclusion implied by the proposed control.

2. Name the controlled boundary. Ask the control owner to show where enforcement occurs. Does the rule sit at the CDN, gateway, service, database or client? Which hosts, routes, HTTP methods, identities and regions pass through it? Draw paths that bypass it, including direct service calls, workers and old deployments. OWASP's virtual-patching methodology separates analysis, implementation, testing and follow-up for this reason.

3. State the intended effect, not a vague claim of safety. For MasonLedger: “The public gateway denies requests matching rule ML-42 before they reach /reports/build.” That is testable. “SQL injection is prevented” is broader than the evidence. Record the rule version, deployment scope, effective time, owner and normal traffic it might accidentally block.

4. Validate both protection and useful behavior. In an authorized staging environment, the gateway owner checks that the agreed synthetic request is denied and that ordinary report filters still work. Review gateway routing and logs for the tested path. Where safe and approved, exercise alternate encodings or equivalent requests; one blocked input cannot establish complete attack coverage. Check that the internal worker does not silently use the same unsafe operation with a different trust boundary. Do not use a production exploit to prove an illustrative example.

5. Define failure signals and response. Record what would make the control ineffective: rule disabled, gateway bypassed, new host outside its scope, fail-open behavior, unexpected false positives or a deployment that changes routing. Assign someone to detect those changes and a response owner who can restrict the feature or accelerate the code fix. Record who notices a failed control and what they do next; otherwise a once-verified restriction can silently stop protecting the service.

6. Set an expiry and code-fix decision. Give the mitigation a review date, not an endless exception. State whether the team will repair the query builder, remove the feature or seek a separately authorized risk acceptance. The OWASP Vulnerability Management Guide treats exceptions, compensating controls, accountability and an audit trail as explicit parts of remediation management.

Copyable control record, filled for MasonLedger

Field Example entry
Finding and underlying status ML-204; verified query-construction weakness at R7; open, not fixed.
Harmful path User-controlled report filter → public report endpoint → query builder → account report data. No live exploit is asserted.
Compensating control Gateway rule ML-42, version 3, denies defined public request pattern before the report service.
Enforcement scope reports.mason.example public ingress, POST /reports/build; internal scheduled worker bypasses the gateway.
Evidence Staging gateway configuration and route map reviewed; synthetic request denied; normal report request succeeds.
Limits and unknowns Equivalent requests and worker inputs need review; no claim that all exploitation variants are blocked.
Failure signals Rule disabled, new direct host, unexpected route change or gateway health mode that stops enforcement.
Owners Platform team operates rule; report-service team owns code fix; security engineer reviews evidence and residual path.
Review date, expiry and next action Security engineer reviews on 1 November 2026, or earlier if the reporting release ships; the decision to rely on this mitigation must be renewed by 17:00 UTC that day or escalated. This is not an instruction to disable the protective rule automatically. Replace string assembly with a parameterized query and reassess at a new immutable revision.
Stakeholder label “Open — defined pattern checked in staging; production mitigation unverified; worker path unresolved.”

Separate control effectiveness, vulnerability status, technical severity, and business priority in the tracker. A compensating control may change environmental exposure, but it does not retroactively make the original code sound. FIRST's CVSS v4.0 specification distinguishes Base characteristics from environment-specific mitigations and leaves business decisions outside the score. If you publish a revised CVSS score, include its vector and explain which environmental assumptions changed; do not quietly overwrite the original assessment.

Operate the control through a failed legitimate request

Track three states separately: proposed, checked in staging and confirmed active in the selected deployment. The initial MasonLedger record reaches only the second. A denied staging request cannot establish production protection, and one pattern cannot establish every equivalent attack is blocked.

Continue the constructed exercise with deployment record G9 binding ML-42 version 3 to the public route. The supplied configuration and controlled observation confirm that the defined pattern is denied before the report service. The precise update is open — defined public request pattern blocked in G9; equivalent requests and worker path not established as covered. No request was actually executed for this article.

A legitimate customer filter containing punctuation now matches the rule. Operations reports a failed report-generation workflow. The team compares three choices:

Option Benefit Remaining problem
Relax the pattern immediately Restores the legitimate filter. May restore the known unsafe request shape; needs renewed coverage assessment.
Disable the affected public report-building feature Stops that entry without depending on pattern recognition. Interrupts legitimate public report creation; internal worker remains separate.
Accelerate the parameterized-query correction Addresses the unsafe code. Requires implementation, compatibility review and deployment; not an instant control.

The exercise decision is to pause the affected public feature, retain access to separately checked existing reports, and accelerate the code correction. The report owner accepts the interruption; platform owns the route restriction and its verified coverage; security records that ML-42 is no longer the basis for the public containment statement. The worker's reviewed input boundary remains separately documented and any unresolved worker exposure stays open.

A rollback is not simply “turn off ML-42.” Restoring the feature requires the corrected query path and its release verification, or another explicitly reviewed control. If the team chooses to narrow the pattern instead, record which ordinary inputs are restored and which malicious-equivalent classes remain unreviewed; do not carry forward the old effectiveness label unchanged.

OWASP's virtual-patching guidance calls for legitimate-workflow checks and follow-up. The control record makes that operational: it names the exact protected behavior, the disruption that invalidated the plan and the replacement basis for containment. The code finding stays open until its own correction is verified.

Make the control observable to its actual operator

The person who relies on the mitigation needs a way to know when its basis changes. For ML-42, retain the active rule version, route attachment and the reason the team currently believes the defined pattern is blocked. A generic gateway-health indicator does not establish that this particular rule is enabled on the report route.

Choose monitoring evidence that matches the control. The operator might review configuration changes affecting the route, record enforcement state or inspect bounded denial and legitimate-traffic signals. State what each signal proves. A rising denial count shows matching requests were blocked; it does not prove all malicious variants were recognized. A quiet count may simply mean no matching request arrived.

The operational runbook should say who responds to a rule detachment or false-positive report and which actions are available. In MasonLedger, a false-positive filter led to a feature pause and an accelerated code correction. That response changes the basis of containment, so the mitigation record should identify the new route restriction instead of leaving ML-42 marked as the effective protection.

Also record whether emergency configuration changes can bypass the normal review path. If an on-call operator disables the rule to restore reports, the service owner needs a notification and a renewed residual-risk decision. The intent is to connect a real operational event with the open vulnerability, not to require a meeting before every permitted response action.

When the permanent query fix is deployed, remove the temporary rule only after confirming that the code repair covers the original path and any still-relevant bypasses. Record the rule's retirement as a separate step. Otherwise a stale mitigation can continue blocking legitimate input after it no longer serves its intended purpose, while a prematurely removed one can expose code that has not actually changed.

For the next compensating control, copy the filled record and insist on one sentence beginning “This control covers…” plus one beginning “It does not cover…”. Then set an owner and review date. If the team needs to approve living with a remaining path, use the security exception process. When the code change is ready, follow the fix-verification guide before changing the finding to fixed.