# Hunting Bugs With A Code Graph

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-unboundcompute-security-agent-skills-hunting-bugs-with-a-code-graph`
- **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/hunting-bugs-with-a-code-graph
- **Website:** https://security.unboundcompute.com

## Install

```sh
agentstack add skill-unboundcompute-security-agent-skills-hunting-bugs-with-a-code-graph
```

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

## About

# Hunting bugs with a code graph

Grep finds strings; it misses the caller behind a rename, an alias, or an import
indirection, and it cannot answer "what can flow into this argument." Reasoning
over a codebase's *structure* - its call graph and dataflow - can. This skill is
the master loop for a source-level hunt. Two companions go deeper on single
moves: `adjudicating-taint-paths` (lead → decided finding) and
`auditing-guard-gaps` (the unguarded peer of a guarded function).

## When to use

- You have source and want *coverage*, not a one-off keyword search.
- You're cold on an unfamiliar codebase and need to find where input arrives.
- You want to work a bug *taxonomy* systematically and prove what you ruled out.

## Scope check (do this first)

Only run on code you're authorized to analyze: your own, an OSS project you
contribute to, a CTF, or an engagement where source review is in scope. If you
can't name why you're allowed to read this source, stop.

## The loop

1. **Index the target.** Build a structural index of the source tree. It is a
   *snapshot* - re-index whenever the code changes in a way that matters, or you
   will adjudicate against stale structure.

2. **Orient before hunting.** Start from the most-connected functions - the
   structural spine, where input arrives and trust boundaries sit - not from a
   file you happened to open. Map the top-level entry points and the module
   layout before drilling in.

3. **Enumerate the whole taxonomy - never one family.** List *every* bug class
   your catalog covers before examining any single one. Do not scope the hunt to
   the class you expect (injection, memory copy, whatever) because it's familiar.
   Coverage is over the whole taxonomy; a hunt that only ever looks at one family
   is not a hunt, it's a confirmation of a hunch.

4. **Rank is triage, not a filter.** A ranked lead list orders your *attention*.
   Inclusion is exhaustive; a low rank never justifies dropping a candidate from
   examination. Work down the list - don't truncate it.

5. **Leads are facts, not verdicts.** A lead means "the structure here matches a
   known-dangerous shape," never "this is a bug." Adjudicate each by tracing its
   source→sink provenance and confirming against live source. Only survivors are
   findings. (→ `adjudicating-taint-paths`.)

6. **Diff guarded against unguarded peers.** When one function validates before a
   sink and a sibling reaching the same sink does not, the sibling is the bug.
   (→ `auditing-guard-gaps`.)

7. **Record coverage honestly.** Emit every decided finding in the shared
   [finding schema](../../FINDING-SCHEMA.md), *including* killed leads. Then state
   what the taxonomy did **not** cover. Temporal/lifetime classes (use-after-free,
   double-free), integer-overflow-as-a-class, and uninitialized/NULL deref are
   commonly *not* modeled as catalog families - hunt those with
   `detecting-memory-safety-bugs` and `detecting-race-conditions`. An empty result
   over a partial taxonomy is not "the code is clean"; say which classes were out
   of scope of the catalog versus genuinely checked and clean.

## Worked example

> **Cold start on a Python web service.**
> 1. Orient: the spine surfaces `app.dispatch` and three request handlers as the
>    highest-traffic nodes - that's where untrusted input lands.
> 2. Enumerate taxonomy: catalog lists path-traversal, injection, SSRF, open-
>    redirect, deserialization, missing-authz (and flags memory/temporal classes
>    as *not modeled*).
> 3. Work each family; in path-traversal the lead is `export.filename → open()`.
> 4. Adjudicate: trace shows no normalization between source and sink →
>    **confirmed**, emitted per the schema (see the schema's worked record).
> 5. Guard audit: `delete_own_post` checks ownership; peer `admin_bulk_delete`
>    reaches the same delete sink with no ownership check → second finding.
> 6. Report: 2 confirmed, 4 families clean, memory/temporal classes flagged
>    out-of-catalog and handed to the dedicated skills.

## Rationalizations to reject

Shortcuts that cause misses and false positives - refuse them:

- *"This family is where the bug will be, I'll start there."* → You'll stop there.
  Enumerate the whole census first.
- *"Low rank, skip it."* → Rank orders attention, never filters. Read it.
- *"The lead looks real, log it."* → A lead is not a finding until you've read the
  source on every hop. Adjudicate or drop.
- *"No leads came back, the code is clean."* → Only true for classes the catalog
  actually models. Name the gaps.

## Executing this in practice

Run the loop with whatever structural tooling you have. At minimum you need
something that answers, from a real parse rather than text matching: who calls a
function, what a value can flow into, and the exact source of a declaration. A
code property graph gives you all three; a good static analyzer covers much of
it; careful manual tracing covers the rest on a small target. Whatever you use,
keep the snapshot caveat - re-index after the source changes.

## Related

- `adjudicating-taint-paths` - confirm or kill a source→sink lead.
- `auditing-guard-gaps` - find the unguarded peer of a guarded function.
- `detecting-memory-safety-bugs`, `detecting-race-conditions` - the classes a
  catalog usually doesn't model.
- [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-hunting-bugs-with-a-code-graph
- 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%.
