> For the complete documentation index, see [llms.txt](https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/solutions-and-use-cases/vulnerability-jail.md).

# Vulnerability Jail

Attach a real consequence to overdue remediation — a team carrying work past its SLO can still open pull requests, but can't merge them — and make fixing in time achievable enough that the consequence

Most vulnerability programs run on escalation: the deadline passes, someone chases the team, and eventually a leader is asked to approve an extension. That works while the volume is small. Past a certain backlog it stops working — extension requests queue up behind busy approvers, and "overdue" quietly becomes a state a repository can live in indefinitely.

**Vulnerability jail** replaces the chase with a consequence that lands on its own. If a team is carrying remediation past its SLO, that team can still open pull requests — they just can't merge them into the default branch until the overdue work is dealt with. Nobody has to send a reminder, and nobody has to be the person who says no.

The point isn't punishment. It's that engineers are pulled at from every direction — product deadlines, customer escalations, platform migrations, security — and a deadline with no consequence loses every one of those arguments. A deadline that blocks the merge queue is one they can take to their manager and reprioritize against.

{% hint style="info" %}
**For:** AppSec leaders who already run SLOs and need overdue work to actually resolve. **Prerequisites:** source control connected; SLOs configured; ownership mapped so findings route to the right team; branch protection enabled on default branches. **Roles:** an **Administrator** to configure SLOs, guardrails, and workflows.
{% endhint %}

## What makes this work

Four pieces, and the order matters — the enforcement is the *last* one you turn on.

|                 |                                                                                                                                                                                                  |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **The clock**   | [SLOs](/mrecEO40m5D6bt7Pq5pE/administer-and-monitor/program-policy/service-level-objectives.md) give every finding a deadline, so "overdue" is a fact rather than an opinion.                    |
| **The warning** | [Workflows](/mrecEO40m5D6bt7Pq5pE/operate/workflows.md) tell the owning team a deadline is approaching, well before anything blocks.                                                             |
| **The way out** | [Auto-fix](/mrecEO40m5D6bt7Pq5pE/fix/sca-auto-fix.md) makes fixing inside the window achievable, so the gate is something teams clear rather than something they're stuck behind.                |
| **The gate**    | An [SLO guardrail](/mrecEO40m5D6bt7Pq5pE/prevent/pr-guardrails/guardrail-types/slo-guardrails.md) set to **Block** fails the status check on any PR into a repository carrying overdue findings. |

## Before you automate anything

A gate that fires on bad data destroys the credibility of the whole program in a week. Three things have to be true before you enforce, and each is worth measuring first.

{% hint style="warning" %}
**Your findings have to be trustworthy.** A team blocked by a false positive will — correctly — stop believing anything the gate says. Use [Heeler Risk](/mrecEO40m5D6bt7Pq5pE/fix/prioritize-heeler-risk.md) and [reachability and exposure](/mrecEO40m5D6bt7Pq5pE/solutions-and-use-cases/determine-exploitability-in-production.md) so what's on the clock is what matters, and make it easy for engineers to push back. Disagreement is a signal you want, not a problem to suppress.

**Ownership has to be right.** The gate lands on whoever owns the repository, so a routing error jails the wrong team. Get [remediation assignment and routing](/mrecEO40m5D6bt7Pq5pE/administer-and-monitor/program-policy/remediation-assignment-and-routing.md) accurate first, and make sure reassignment is quick when it's wrong.

**You have to be responsive.** A blocked team needs someone who can unblock them the same day. If you can't staff that, don't turn on Block.
{% endhint %}

{% hint style="info" %}
**Get leadership alignment before rollout, not after.** This changes the merge path for every engineer in scope, so it needs to be announced by engineering leadership rather than discovered by a developer whose PR won't merge. Agree the policy with your CTO and CISO, have it communicated by engineering, and say plainly what will happen and when. That turns the gate into an organizational decision that security implements, rather than something security did to engineering.
{% endhint %}

## Build it

{% stepper %}
{% step %}

### Put every finding on a clock

Set [Service Level Objectives](/mrecEO40m5D6bt7Pq5pE/administer-and-monitor/program-policy/service-level-objectives.md) under **Administration → Program**. Both SCA and SAST default to the **Heeler Risk** strategy — Urgent 14 / Plan 60 / Defer 120 days.

Set the windows you can actually defend. Tightening them later is one change on the SLO settings page, and it re-times every open finding immediately — so start with windows your teams can hit and tighten as throughput improves, rather than opening with a deadline that puts half the estate overdue on day one.

{% hint style="warning" %}
**The SLO guardrail reads the SCA card's strategy.** Whichever strategy the **SCA** card uses — Heeler Risk or Severity — is the set of bands the guardrail will let you enforce on. Decide it before you build the guardrail.
{% endhint %}
{% endstep %}

{% step %}

### Route every finding to its owner

Configure [remediation assignment and routing](/mrecEO40m5D6bt7Pq5pE/administer-and-monitor/program-policy/remediation-assignment-and-routing.md) so a new finding lands on the owning team automatically — from CODEOWNERS, a `dependency_owners.json`, or team mappings.

This is the load-bearing step. The gate is applied per repository, so repository ownership *is* the jail's addressing scheme. If ownership is stale, the gate is unfair, and unfair is fatal.
{% endstep %}

{% step %}

### Warn well before the deadline

Build a [workflow](/mrecEO40m5D6bt7Pq5pE/operate/workflows/building-a-workflow.md) on the **SLO Expiring** trigger with an **SLO Due Within (Days)** condition, and have it message the owning team's Slack channel and open a ticket. Chain up to three actions in one workflow.

Run this for a while *on its own*, before any gate exists. Two things come out of it: teams get used to the deadline being real, and you find out where your notifications and ownership are wrong while the cost of being wrong is still just a misdirected Slack message.
{% endstep %}

{% step %}

### Make fixing in time achievable

This is the step that decides whether the program is fair. A gate is only reasonable if clearing it is realistic inside the window — otherwise you've built a queue of blocked teams.

* **Unattended fixing.** A workflow on **New Remediation Available** with the **Fix with Heeler Agent** action drives [unattended auto-fixing](/mrecEO40m5D6bt7Pq5pE/fix/automate-remediation.md) — Heeler picks the upgrade, validates it through your CI, and opens a merge-ready PR without anyone asking.
* **On demand.** Engineers with the **Team contributor** or **Organization contributor** role can trigger [Fix Now](/mrecEO40m5D6bt7Pq5pE/fix/sca-auto-fix.md) on a finding themselves.
* **In bulk.** The [Remediations](/mrecEO40m5D6bt7Pq5pE/findings/open-source-sca/remediations.md) view groups findings into per-package fix units, so one upgrade clears several findings and a backlog can be worked in batches rather than one CVE at a time.

The tighter your SLO windows, the more of this you need. A 3-to-10-day window is not a manual-remediation program.
{% endstep %}

{% step %}

### Build the gate

Create an [SLO guardrail](/mrecEO40m5D6bt7Pq5pE/prevent/pr-guardrails/guardrail-types/slo-guardrails.md) — check type **SLO Violations**. Configure it to match the policy you announced:

* **Bands** — start with your top band only (**Urgent**, or **Critical** on a severity strategy). Add lower bands once the top one runs clean.
* **Days overdue threshold** — the grace on top of the deadline. Opening at `0` jails every repository with any overdue debt the moment you enable it. A threshold in the range of a sprint gives teams a visible runway between "overdue" and "blocked".
* **Scope** — **Repository** by org/group to pilot with one team, or **Service Runtime Context** to gate production, internet-facing services first.
* **Action** — **Observe** for now.

Then split it by branch, which is the part that makes this a jail rather than a work stoppage.

#### Block the default branch, warn everywhere else

The **Branch** scope matches on the branch a pull request **targets**. Build the policy as **two** guardrails with the same rule and different branch scopes — a guardrail carries one action, so the gate and the warning can't be the same policy:

| Guardrail   | Branch scope                  | Action    | Effect                                                                            |
| ----------- | ----------------------------- | --------- | --------------------------------------------------------------------------------- |
| The gate    | **Default Branch**            | **Block** | A PR into the default branch won't merge while the team carries overdue findings. |
| The warning | **All Except Default Branch** | **Warn**  | Every other PR reports the same overdue findings and merges normally.             |

This is precisely the shape of the constraint: a team in jail keeps working. They open pull requests, stack branches, review each other's code, and merge into release and integration branches — and every one of those PRs carries the list of what's holding their path to the default branch. What they can't do is land it where it ships.

That split matters for adoption as much as for fairness. A gate on every branch stops a team dead and generates an escalation the same afternoon. A gate on the default branch alone leaves the team's day-to-day intact, applies pressure exactly where the work becomes real, and — because the warning has been on every preceding PR — never lands as a surprise.

{% hint style="warning" %}
**Block needs branch protection.** Heeler fails the status check; your SCM decides whether that stops a merge. Turn on the matching branch protection rule, or the gate is advisory only.
{% endhint %}

{% hint style="info" %}
**"Default branch" means the branch Heeler tracks** — a repository's scan branch override where one is set, and the SCM default otherwise. In a monorepo, that single branch is also what gives this pattern its leverage: one gate covers everything that ships.
{% endhint %}
{% endstep %}

{% step %}

### Build the release valve

There must be a legitimate way out that isn't "fix it right now", or teams will find an illegitimate one.

* **Extend a deadline deliberately.** An [SLO override](/mrecEO40m5D6bt7Pq5pE/operate/exceptions-and-overrides.md) on a finding takes a required reason (**No Fix Available** · **Fix Complexity** · **Not a Priority** · **Other**), a required written rationale, and a new due date — at most a year past the original. The guardrail reads the new date on its next run, so the block lifts. Heeler keeps the original deadline alongside the new one, so the **SLO Overrides** register shows how far every exception moved the date and **who granted it**.
* **Grant grace automatically.** A workflow with the **Update SLO Override** action can extend a due date without anyone filing a request — the obvious case being findings with no fix available yet, which no amount of pressure will resolve. You set the number of days (or let Heeler compute a fresh window from your SLO policy), the reason recorded, and whether the extension counts from the original due date or from today.

That second one is how you handle the unfair cases structurally rather than case by case — the finding that lands on a team days before the deadline, or the one with nothing to upgrade to.
{% endstep %}

{% step %}

### Turn it on, gradually

Promote the guardrail **Observe → Warn → Block**, watching the **Pull Requests** tab between each step.

Because this guardrail reads existing debt rather than the diff, its day-one blast radius is the size of your backlog, not your change volume — so the Observe reading is the number that tells you whether you're ready. Many programs run the first cycles as a **manual** decision: the guardrail scoped to the specific repositories that have had every warning and still haven't moved. That builds the expectation before it becomes automatic, and it's much easier to widen a narrow scope than to recover from a week where everyone was blocked at once.
{% endstep %}
{% endstepper %}

## Keeping it fair

The gate holds only as long as teams believe it's applied honestly. What to watch, and what to do:

| Problem                                               | What to do about it                                                                                                                                                    |
| ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **The wrong team is blocked.**                        | Fix the routing, and treat every occurrence as a bug in ownership rather than an exception to wave through.                                                            |
| **A team is blocked with no fix available.**          | Cover it with an automatic **Update SLO Override** workflow so it never reaches the gate.                                                                              |
| **A finding lands just before its deadline.**         | Same mechanism — a due-soon extension rule gives a newly assigned team a real window.                                                                                  |
| **Extensions are being rubber-stamped.**              | Review the **SLO Overrides** register by **Set By** and by reason. An extension whose rationale is empty of a plan is the one worth asking about.                      |
| **A finding disappears and reappears between scans.** | Heeler scans continuously and closes findings automatically when a scan no longer sees them — regardless of who fixed it. A finding that has actually gone stays gone. |

{% hint style="success" %}
**A developer can clear the gate in their own PR.** If the pull request's own scan no longer finds the overdue `package@version`, Heeler drops that violation — so bumping the overdue dependency on the branch that's being blocked unblocks it on the next check run. Worth telling teams explicitly: for a straightforward upgrade, the way out is one commit on the branch they're already on.
{% endhint %}

## The outcome

Overdue remediation carries a consequence that arrives on its own schedule, in a place engineers already look, without a security engineer sending a message or a VP signing anything. Teams reprioritize because the cost of not doing so is concrete and immediate — and because the fix is usually a validated pull request Heeler has already opened.

The escalation path doesn't disappear; it just stops being the mechanism. Extensions still exist, they're still attributed, and they're still reviewable — but they're now the exception rather than the throughput limit of the whole program.

## Validate success

* **Overdue counts trend down** and SLO attainment trends up on the [dashboards](/mrecEO40m5D6bt7Pq5pE/operate/dashboards.md).
* **Very few repositories are actually blocked** in steady state. A jail that's always full means your windows, your grace threshold, or your fix throughput is wrong — not that your teams are.
* **Time-to-fix drops for findings approaching their deadline**, which is the behavior change you're actually buying.
* **Extensions are rare and well-reasoned**, and the **SLO Overrides** register shows who granted each one.
* **No team is blocked by a finding they had no way to fix** — no-fix-available and late-assignment cases are absorbed by workflow-granted grace.

## Where to go next

* [SLO Guardrails](/mrecEO40m5D6bt7Pq5pE/prevent/pr-guardrails/guardrail-types/slo-guardrails.md) — the gate itself, in full detail.
* [Run an SLO-Driven AppSec Program](/mrecEO40m5D6bt7Pq5pE/solutions-and-use-cases/run-an-slo-driven-appsec-program.md) — the wider operating model this enforces.
* [SCA Auto-Fix](/mrecEO40m5D6bt7Pq5pE/fix/sca-auto-fix.md) · [Automate Remediation](/mrecEO40m5D6bt7Pq5pE/fix/automate-remediation.md) — making the deadline achievable.
* [Exceptions and Overrides](/mrecEO40m5D6bt7Pq5pE/operate/exceptions-and-overrides.md) — granting and reviewing extended deadlines.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/solutions-and-use-cases/vulnerability-jail.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
