Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
abhigyanpatwari avatar

Gitnexus Taint Analysis

  • 185 installs
  • 45.1k repo stars
  • Updated August 4, 2026
  • abhigyanpatwari/gitnexus

gitnexus-taint-analysis is a Claude Code skill providing expert knowledge of GitNexus's opt-in taint subsystem for source-to-sink data-flow vulnerability findings.

About

This skill is expert reference knowledge for GitNexus's opt-in --pdg taint-analysis subsystem: control-flow graphs, reaching definitions, and intra- plus inter-procedural taint. It explains how source-to-sink data-flow findings are computed, minus sanitizers, and how per-function summaries are composed over the call graph. A developer uses it when adding a source, sink, or sanitizer, reviewing the taint code, or debugging why the explain tool did or did not report a flow. It documents the layered substrate, the pure-solver contract, and the sanitizer kind-set model.

  • Expert knowledge for GitNexus source-to-sink taint analysis (the --pdg layers)
  • Explains intra- and inter-procedural taint over reaching-definition facts
  • Covers adding sources, sinks, and sanitizers and debugging false positives

Gitnexus Taint Analysis by the numbers

  • 185 all-time installs (skills.sh)
  • Ranked #802 of 2,203 Security skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

gitnexus-taint-analysis capabilities & compatibility

Capabilities
taint analysis · data flow analysis · security audit · sink source modeling
Works with
github
Use cases
security audit · code review · debugging
From the docs

What gitnexus-taint-analysis says it does

Expert knowledge for the opt-in `--pdg` program-analysis subsystem: control-flow
SKILL.md
Forward reachability over RD facts from matched **sources** to matched **sinks**,
SKILL.md
Adding a source, sink, or sanitizer to the model.
SKILL.md
npx skills add https://github.com/abhigyanpatwari/gitnexus --skill gitnexus-taint-analysis

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs185
repo stars45.1k
Last updatedAugust 4, 2026
Repositoryabhigyanpatwari/gitnexus

What it does

Reason about source-to-sink data-flow security findings in GitNexus and extend its taint sources, sinks, and sanitizers.

Who is it for?

Reasoning about, extending, or reviewing GitNexus taint analysis (CFG, reaching-defs, intra- and inter-procedural taint) and its sources, sinks, and sanitizers.

Skip if: Default GitNexus analyze runs that were not indexed with the opt-in --pdg layers, where no taint data exists.

When should I use this skill?

Working on GitNexus's CFG/taint/PDG code, adding a source/sink/sanitizer, or debugging a false positive or negative in --pdg output.

What you get

Accurate source-to-sink taint findings that model sanitizers by sink kind and compose across functions.

By the numbers

  • 4 layered analysis stages (CFG, REACHING_DEF, intra taint, inter taint)
  • Sanitizers modeled as a set of neutralized sink kinds

Files

SKILL.mdMarkdownGitHub ↗

CFG & Taint Analysis with GitNexus

Expert knowledge for the opt-in --pdg program-analysis subsystem: control-flow graphs, reaching definitions, and intra- + inter-procedural taint. Read this before touching gitnexus/src/core/ingestion/cfg/** or gitnexus/src/core/ingestion/taint/**, or when explaining a finding.

When to Use

  • "How does the taint engine work / why is this flow (not) reported?"
  • Adding a source, sink, or sanitizer to the model.
  • Extending or reviewing the CFG / reaching-defs / taint / summary code.
  • Understanding the explain MCP tool's findings (intra- vs inter-procedural).
  • Debugging a false positive or false negative in --pdg output.

The layered substrate (build order)

Taint runs on the graph, not beside it. Each layer is opt-in behind --pdg and a default analyze run is byte-identical (the golden parity gate is the hard floor for every change here).

L1  CFG            per-function basic blocks + control-flow edges   (M1 #2081)
L2  REACHING_DEF   GEN/KILL def→use data dependence (pure solver)   (M2 #2082)
L3  Taint (intra)  source→sink over RD facts, minus sanitizers      (M3 #2083)
L4  Taint (inter)  per-function summaries composed over CALLS       (M4 #2084)
  • Worker-built, main-thread-solved. The parse worker builds each function's

CFG + harvests def/use + call-site facts onto ParsedFile.cfgSideChannel (plain, structured-clone-safe data — never AST nodes). The main thread runs the pure solvers. NEVER re-parse on the main thread (re-introduces the #1983 OOM).

  • In-phase emit (KTD1). L1–L4-harvest all run INSIDE the scope-resolution

pdg window (scope-resolution/pipeline/run.ts, gated input.pdg === true), because the disk-backed ParsedFile store is cleared when that phase ends — a standalone post-mro phase would read empty data. The cross-function fixpoint (L4) is the exception: it runs in its OWN registered phase (taintSummaries) AFTER scope-resolution, because it needs the COMPLETE call graph, and consumes small plain summary data threaded out via ScopeResolutionOutput.

  • Pure-solver contract. computeReachingDefs, computeTaintFlows,

harvestFunctionSummary, and solveInterprocTaint are pure and deterministic (no graph, no I/O, no logger; sorted outputs). Snapshot tests and content-derived edge ids depend on it.

Intra-procedural taint (L3)

Forward reachability over RD facts from matched sources to matched sinks, killed by sanitizers. Key design points worth internalizing:

  • Occurrence-tagged sites. A flat per-arg binding set cannot tell

exec(escape(x)) (safe) from exec(x) (finding); the harvest records nested call structure (SiteRecord.parent/via-tags) so sanitizer interposition is precise.

  • Kind-set sanitizer model. A taint carries a set of neutralized

SinkKinds; a sink fires unless its kind is in the set. So escape(req.body) suppresses res.send (xss) but STILL fires db.query (sql) — a kind-blind kill would be a suppressed live injection (the forbidden FN direction). path.basename(t) neutralizes path-traversal only, not command-injection.

  • Statement-level finding identity. NOT block-pair (block conflation drops

distinct findings; exec(req.body, req.query) is two findings).

  • Persisted as TAINTED edges (BasicBlock→BasicBlock); the path rides the

reason column via the shared versioned codec (taint/path-codec.ts).

Interprocedural taint (L4) — the functional/summary method

The production approach (Sharir-Pnueli 1981; the same shape as Meta's Pysa and Mariana Trench, and FB Infer) — NOT full IFDS tabulation. Each function is reduced to a compact summary, and summaries are composed over the already- resolved CALLS graph.

Summary shape (taint/summary-model.ts, whole-parameter granularity):

EdgeMeaningAnalogue
param→returna param flows to the return valueTITO — reserved (the floor already covers its recall; precision pass deferred)
param→callee-arga param flows into arg j of a call (carries the path's neutralized sink kinds)TITO into callee
param→sinka param reaches a modelled sinkpartial/triggered sink
source→returnthe function generates+returns a sourcegenerative — composed via the caller's callResults
source→callee-arga generated source flows into a callfixpoint SEED
callResultsa user-function call's result flows to a sink/return/callee-arg in the callercomposes with callee source→return

The fixpoint (taint/interproc-solver.ts): the unit is (function, parameter, source). Seed from source→callee-arg, propagate via param→callee-arg, fire a finding when a tainted param meets param→sink.

  • Cycle-safe by monotonicity. The tainted-set is monotone over a finite

lattice (fn × param × source), so the worklist converges — a recursive call just re-proposes an already-visited entry. SCC condensation would only refine processing order; correctness/termination don't require it.

  • Source-discriminated state (load-bearing). Key the state by the SOURCE

too. Keying only by (fn, param) collapses multi-source flows: a sink param tainted by source A is marked visited and a later flow from source B is dropped before firing — the recurring multi-source bug class. (Bit M3; bit M4 U9.)

  • Name-based call join. Match a summary's call-arg edge to a CALLS edge by

CALLEE NAME, not call-site line — line-base parity (CFG 1-based vs reference site) is fragile; the callee identity is exact and context-insensitivity taints the callee's param identically at every call site.

  • Persisted as TAINT_PATH edges (Function→Function), function-level hop chain

in reason via the same codec; confidence < the intra-procedural 1.0.

Context-insensitivity is the accepted trade-off at this tier: one summary per function, return/call-site merging accepted (security-conservative). Expect some FP from merging; the bigger FN sources are unmodeled features (below).

Known false-negative classes (documented, deferred)

The largest is closures/callbacks (arr.forEach(() => sink(y))) — taint into a callback is dropped without per-library models (true of CodeQL's JS libs too). Also deferred: field/property flows (obj.x = taint; sink(obj.y)), field-sensitive access paths, guard-style sanitizers, implicit/control-dependence flows, promise/async-await threading, and destructured/rest params before a tainted simple param (the summary port index is the binding ordinal, not the formal arg position — needs a formal-param index threaded from the worker BindingEntry). The interprocedural join is also context-insensitive: when one caller invokes two distinct same-named callees, a flow into one over-attributes to both (sound — over-report, never a missed flow). Absence of a finding is NOT proof of safety.

GitNexus-specific gotchas

  • Function↔CFG join. FunctionCfg.functionStartLine is 1-based; Function/

Method node startLine is 0-based — join at startLine - 1. Function nodes have no column, so same-line functions ({a:()=>x(), b:()=>y()}) are ambiguous → drop (the summary driver counts unresolved) rather than cross-wire.

  • No rel-property index (S1). Kuzu has no secondary index on relationship

properties, and unanchored [:TAINTED*]/[:TAINT_PATH*] queries explode. TAINT_PATH is therefore MATERIALIZED + anchored at analyze time, never traversed live; explain reads it source-anchored + LIMIT-guarded.

  • `explain` is the only discovery surface. TAINTED/TAINT_PATH are

deliberately OUT of VALID_RELATION_TYPES (impact's allow-list) and the web schema (pinned in security.test.ts). explain enumerates both layers (cross-function findings carry interprocedural: true).

  • One shared codec. Both the emit path and explain import

taint/path-codec.ts. Two hand-rolled copies of a wire format drift — never fork it. New metadata extends the format WITHIN the version when writer + reader ship together.

  • Cache versioning. A worker-harvest shape change bumps the parse-cache pdg

NAMESPACE (pdg:N), NOT SCHEMA_BUMP (which cold-invalidates every user). Persisted-graph/config changes ride RepoMeta.pdg's key-union mismatch → full writeback. Model content rides taintModelVersion.

Adding a source / sink / sanitizer

Edit the language model in taint/typescript-model.ts (registered via the explicit registerBuiltinTaintModels seam, keyed by SupportedLanguages). The spec is hashable data (no functions). A sanitizer's neutralizes lists the EXACT sink kinds it defends — never a blanket kill. Add a fixture + assert the finding (or its absence) in test/unit/taint/ (real-source harness: test/helpers/ts-cfg-harness.ts); the end-to-end proof is test/integration/cfg/.

Validation checklist for any --pdg change

1. tsc clean (schema additions are exhaustiveness-checked; watch the
   api.ts getNodeQuery runtime read-path if a node label is added).
2. Targeted vitest by directory (test/unit/taint, test/unit/cfg,
   test/integration/cfg) — verify by ISOLATION, not full-suite exit
   (known load-flakes). `node scripts/build.js` before worker/integration runs.
3. Flag-off golden byte-identical (pipeline-graph-golden.test.ts).
4. bench/cfg/measure.mjs --check (no fingerprint drift / budget regression).
5. detect_changes() before commit; impact({direction:'upstream'}) before
   editing shared symbols (KnowledgeGraph, RepoMeta, RelationshipType, codec).

Prior art (for deeper design questions)

Sharir & Pnueli 1981 (functional approach); Reps-Horwitz-Sagiv IFDS (POPL 1995); FlowDroid/StubDroid (access-path summaries); Pysa & Mariana Trench (TITO / propagations, parallel SCC fixpoint); CodeQL Models-as-Data (the richest port notation, incl. callback ports); Infer (content-keyed incremental summaries).

Related skills

FAQ

What does the taint engine detect?

Forward reachability from matched sources to matched sinks over reaching-definition facts, killed by sanitizers, reporting source-to-sink data-flow findings such as SQL or XSS injection.

How are sanitizers modeled?

As a kind-set: a taint carries a set of neutralized sink kinds, so escape(req.body) suppresses res.send (xss) but still fires db.query (sql).

Securityappsecaudit

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.