# Adjudicating Taint Paths

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-unboundcompute-security-agent-skills-adjudicating-taint-paths`
- **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/adjudicating-taint-paths
- **Website:** https://security.unboundcompute.com

## Install

```sh
agentstack add skill-unboundcompute-security-agent-skills-adjudicating-taint-paths
```

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

## About

# Adjudicating taint paths: lead → decided finding

A lead is a *fact* about structure - "an input-shaped value can reach a
dangerous sink." It is never a verdict. Adjudication is the disciplined work of
deciding whether that structural possibility is a real, reachable bug on the
current source, and recording the decision so it isn't re-litigated next pass.

## When to use

- A scanner or candidate list flagged a sink and you must confirm or kill it.
- You spotted a sink by hand and want to know if attacker input reaches it.
- You need to *kill* a plausible-looking lead with evidence, not vibes.

## Scope check

Authorized source only (your own, OSS, CTF, in-scope engagement). If you can't
name the authorization, stop.

## The loop

1. **Name source and sink precisely.** Which exact argument of which sink is
   dangerous, and what is the *actual* untrusted entry - a request param, header,
   filename, env var, deserialized field? Vague framing ("user input reaches it
   somewhere") is how false positives survive.

2. **Trace the reverse cone into the sink.** What values can flow *into* this sink
   argument? This enumerates every origin. If none trace back to an untrusted
   source, the lead is dead - kill it, record why.

3. **Trace the forward cone from the source.** Where does the untrusted value go?
   If it never touches the sink, the lead is dead. Forward and reverse must agree;
   if they don't, you mis-specified an endpoint - fix it and redo.

4. **Get a witness path.** The strongest evidence is a concrete `source → … →
   sink` path. Good tooling returns either a witness or an *honest negative*
   ("no path"). A witness is a hypothesis to verify, not a proof.

5. **Confirm every hop against live source.** Read the actual body of each
   function on the path at the commit you're adjudicating. Verify the value is
   genuinely carried hop-to-hop and is not: reassigned to a constant/trusted
   value; validated, sanitized, or encoded by a guard on the path; narrowed to a
   safe type or bounded before the sink; or never actually passed by any caller.

6. **Decide and record - in the schema.** Survivor → `confirmed`: source, path,
   sink, evidence, impact. Killed → record the exact hop where taint breaks. Both
   go in the [finding schema](../../FINDING-SCHEMA.md); killed findings are kept.

## Evidence rules

- **Confidence is not truth.** A high-confidence edge is strong support; a
  conservative/over-approximated edge is included to avoid missing a path and is
  frequently spurious - a witness leaning on one demands extra source
  confirmation.
- **An absent edge is not proof of safety.** The tool may not model that path.
  Dynamic dispatch (attribute/vtable), function pointers, and
  `getattr`/`eval`/reflection are standard blind spots - "no path" through one of
  those is inconclusive, not clean. Confirm by reading source.
- **A sanitizer only helps if it covers the payload class.** An HTML encoder does
  nothing for a SQL context; a `realpath` check does nothing for a symlink race.
  Match the guard to the sink's *context*, not to its name.

## Worked example (a kill and a confirm)

> **Kill.** Lead: `GET /search?q=` → `cursor.execute(sql)`. Reverse cone shows
> `q` reaches `execute`, but reading the hop shows `execute(sql, (q,))` - `q` is
> a bound parameter, never concatenated into `sql`. **Killed**, `kill_reason` =
> "bound param at the sink; q never enters the SQL string."
>
> **Confirm.** Lead: `body.filename` → `open(path, 'w')`. Forward cone reaches
> the sink; reading each hop shows `name = body['filename']` (unchecked) →
> `path = base / name` → `open`. `/etc/x`-style input escapes `base`. **Confirmed**,
> `high`, impact = arbitrary file write → RCE via config/cron drop.

## Rationalizations to reject

- *"The witness path is enough."* → Not without reading source on every hop.
- *"There's a sanitizer, it's fine."* → Only if it covers this sink's context.
  Check what it actually enforces.
- *"No path found, so it's safe."* → Not if the path would run through a blind
  spot (reflection, function pointers, dynamic dispatch). Confirm by hand.
- *"I'll skip writing down why I killed it."* → Then you re-open it next pass.
  Record the kill reason.

## Executing this in practice

Run the loop with whatever answers three questions from a real parse: what
reaches a sink argument (reverse cone), where a source value flows (forward
cone), and the exact current source of any function on the path. A code property
graph answers all three; a taint-tracking analyzer covers most; on a small
target you trace by hand. Step 5 (source confirmation) is never optional - the
tool proposes, you confirm.

## Related

- `hunting-bugs-with-a-code-graph` - the master loop that surfaces leads.
- `auditing-guard-gaps` - when the "sanitizer" is present on one path but missing
  on a sibling.
- [FINDING-SCHEMA.md](../../FINDING-SCHEMA.md) - the shape every decision 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:** yes
- **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-adjudicating-taint-paths
- 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%.
