# Extracting Nday From A Patch

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-unboundcompute-security-agent-skills-extracting-nday-from-a-patch`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [UnboundCompute](https://agentstack.voostack.com/s/unboundcompute)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [UnboundCompute](https://github.com/UnboundCompute)
- **Source:** https://github.com/UnboundCompute/security-agent-skills/tree/main/skills/extracting-nday-from-a-patch
- **Website:** https://security.unboundcompute.com

## Install

```sh
agentstack add skill-unboundcompute-security-agent-skills-extracting-nday-from-a-patch
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Extracting n-day from a patch: the fix is the map

A security fix is a confession. It tells you exactly where the bug was, what the
missing check should have been, and, by omission, which other paths the author
did not think to cover. Reading a patch backward, from fix to bug, is one of the
highest-yield techniques in vulnerability research: the hard part (locating the
defect) is already done for you, and the incomplete fixes are waiting.

## When to use

- A fix commit or security release landed and you want to know what it addressed.
- An advisory is deliberately vague but links a diff, a PR, or a tag.
- You maintain or depend on a fork and need to know if a fix upstream applies.
- You confirmed one bug via its patch and want the instances the patch missed.

## Scope check

You may read and reason about public patches and advisories freely. You may only
*test or exploit* against code and systems you are authorized for (your own, OSS
you run, CTF, in-scope engagement). Analysis is not authorization to attack.

## The loop

1. **Read the fix, infer the bug.** Look at what the patch *adds* or *tightens*,
   and name the vulnerability it implies. A new length or bounds check implies an
   out-of-bounds read or write. A new authorization call implies missing access
   control. A new escape or parameterization implies injection. A new
   normalization implies traversal or confusion. The added guard tells you the
   invariant that was being violated.

2. **Locate the pre-patch sink and its precondition.** In the *vulnerable*
   revision, find the exact operation that was unsafe and the condition under
   which it misbehaves (the input shape, size, or state the new guard now
   rejects). This is the bug, stated concretely.

3. **Reconstruct the reachable path.** Confirm an untrusted input could reach that
   sink in the vulnerable version. Because the fix exists, the bug is real by
   construction; your job is to establish the trigger, not to re-prove the flaw.

4. **Test the fix for completeness.** This is the payoff. Ask whether the new
   guard sits on *every* path to the sink or only the reported one. Common gaps:
   the check was added to one caller but a second caller still reaches the sink
   raw; the check runs after an early operation that is already unsafe; the guard
   covers one input shape but a sibling shape (a different encoding, a second
   parameter) slips past. An incomplete fix is a live bug in a patched version.

5. **Hunt variants the patch never touched.** Extract the defect's shape and sweep
   the rest of the tree, sibling modules, vendored copies, and downstream forks
   for the same mistake in code the fix did not visit. A fix is scoped to one
   report; the same author's same habit usually recurs.

6. **Adjudicate and record.** Each incomplete-fix path and each variant is a lead.
   Confirm or kill it on live source and record it in the schema, with `commit`
   set to the revision you adjudicated against and the fix commit noted as the
   seed.

## Reading a fix well

- **A guard added means a guard was missing.** Name what the guard enforces, then
  ask where else that invariant must hold and does not.
- **Refactors hide fixes.** A "cleanup" or "rename" commit next to a release
  sometimes carries the real security change. Diff behavior, not just lines.
- **Test files leak the bug.** A new test case in the patch often spells out the
  exact malicious input, saving you the reconstruction.
- **The advisory undersells it.** "Minor hardening" frequently means a real,
  reachable vulnerability the vendor chose to describe quietly.

## Worked example (a confirm and a kill)

> **Patch.** A release adds `if (nmemb > SIZE_MAX / size) return NULL;` before an
> allocation that is then filled by a copy loop.
>
> **Infer.** Pre-patch integer overflow in the size computation leads to an
> undersized buffer and an out-of-bounds write in the fill loop.
>
> **Completeness (confirm).** Two other functions compute `nmemb * size` the same
> way and allocate without the new check; one is reachable from a parser fed by
> file input. The fix missed it. **Confirmed**, incomplete fix, `high`.
>
> **Variant (kill).** A third site also multiplies, but the operands are a fixed
> compile-time constant and a bounded enum; no attacker influence and no overflow.
> **Killed**, `kill_reason` = "both operands bounded and non-attacker-controlled."

## Rationalizations to reject

- *"The patch fixed it, so the version is safe."* → The patch fixed the *reported*
  path. Completeness analysis is the whole point; assume it missed one.
- *"The advisory says low severity."* → Vendor severity is a business decision.
  Reconstruct the bug and judge impact yourself.
- *"No diff, just a version bump."* → Then diff the two versions yourself; the
  security change is in there somewhere.
- *"It's the same code, so it's the same bug."* → A structural twin can be safe
  because a caller bounds the input. Adjudicate every variant.

## Executing this in practice

You need a way to read two revisions side by side, to establish reachability to a
sink in the vulnerable version, and to sweep the tree for the defect's shape. A
diff tool plus a structural query (a code property graph or structural search)
covers it; on a small change you can trace by hand. The inference in step 1 and
the completeness judgment in step 4 are yours; the tools only surface the sites.

## Related

- `hunting-bug-variants` - the sweep in steps 4 and 5, generalized.
- `adjudicating-dependency-cve-reachability` - when the patched code is a
  dependency and you need to know if the pre-fix bug reaches your app.
- `adjudicating-taint-paths` - the confirm/kill loop for each reconstructed path.
- [FINDING-SCHEMA.md](../../FINDING-SCHEMA.md) - the shape every finding takes.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [UnboundCompute](https://github.com/UnboundCompute)
- **Source:** [UnboundCompute/security-agent-skills](https://github.com/UnboundCompute/security-agent-skills)
- **License:** MIT
- **Homepage:** https://security.unboundcompute.com

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-unboundcompute-security-agent-skills-extracting-nday-from-a-patch
- Seller: https://agentstack.voostack.com/s/unboundcompute
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
