---
name: Repo Forensics
slug: repo-forensics
category: Quality
description: Repo Forensics audits git repositories, AI skills, and MCP servers for supply-chain risk, prompt injection, credential theft, runtime drift, and known vulnerabilities. It is used to detect threats and evidence, not to fix vulnerabilities or pentest.
github: "https://github.com/alexgreensh/repo-forensics/tree/main/skills/repo-forensics"
language: Python
stars: 170
forks: 26
install: "npx degit https://github.com/alexgreensh/repo-forensics/tree/main/skills/repo-forensics ~/.claude/skills/repo-forensics"
installs_to: ~/.claude/skills/repo-forensics
source_path: skills/repo-forensics/SKILL.md
collection_size: 6
category_size: 1817
collection_url: "https://dirskills.com/collections/alexgreensh/repo-forensics"
added: 2026-09-07T05:22:23.701Z
last_synced: 2026-09-07T05:22:23.701Z
canonical_url: "https://dirskills.com/skills/repo-forensics"
---

# Repo Forensics

Repo Forensics audits git repositories, AI skills, and MCP servers for supply-chain risk, prompt injection, credential theft, runtime drift, and known vulnerabilities. It is used to detect threats and evidence, not to fix vulnerabilities or pentest.

**Install:**

```bash
npx degit https://github.com/alexgreensh/repo-forensics/tree/main/skills/repo-forensics ~/.claude/skills/repo-forensics
```

## README

<!-- repo-forensics v2 | built by Alex Greenshpun | https://linkedin.com/in/alexgreensh -->

# Repo Forensics v2

Deep security auditing for repositories, AI agent skills, and MCP servers.

## Highlights

- **Rules-as-data** (v2.10): ~545 behavioral detection patterns live in versioned
  JSON rule packs (`data/rulepacks/*.json`), not compiled into source. Each rule
  carries a stable id, severity, confidence score, explanation, and embedded
  self-tests. Pack-driven scanners: secrets, SAST, skill threats, MCP security,
  runtime dynamism, and shared patterns. Algorithmic scanners (entropy, AST, DAST,
  git forensics, integrity, manifest drift, binary, lifecycle, dependencies, infra,
  devcontainer, post-incident, dataflow, entrypoint) remain code-driven; they do not
  receive feed updates.
- **Signed daily rule-pack feed** (v2.10): New detection rules reach installed users
  without a code release. An Ed25519-signed bundle is fetched by the daily
  `refresh_threat_dbs.py` pipeline. Shipped packs always work offline; the feed
  only overlays when verified, schema-valid, and strictly newer than the last
  accepted version. The same signing now covers the IOC feed for symmetric trust.
- **Confidence tiers + verdict levels** (v2.10): Findings carry a `confidence` score.
  Four verdict tiers shape output and agent routing: BLOCK (>= 0.92), WARN (>= 0.60),
  INFO (>= 0.30), SUPPRESSED (< 0.30 or user-suppressed). Severity still drives exit
  codes (0/1/2/99) unchanged.
- **Separated trust signals**: JSON reports expose `core_verdict`,
  `coverage_status`, and `enrichment_status`. `core_verdict` is the deterministic
  install gate. Coverage and enrichment report what could not be checked and may
  add warnings or context, but they cannot remove findings, reduce severity, or
  lower the deterministic exit code.
- **Local scan attestations**: `--history` stores content-addressed attestations in
  a private local SQLite database. Storage and deferred retries stay off the
  verdict path; storage failure does not change scan output or exit behavior.
- **Offline benign-corpus FP gate** (v2.10): A committed corpus of tricky-but-clean
  content (emoji-rich markdown, legitimate postinstall scripts, `.env.example`, OAuth
  docs, clean SKILL.md) runs in pytest. Any rule change that raises new false positives
  on the corpus fails the test before it can ship.
- **Advisory adjudication** (v2.10): WARN-tier findings include an injection-safe
  adjudication block. Snippets are prefixed with `> SNIPPET: ` (not in code fences),
  metadata appears before content, the block is capped at 5 findings sorted by
  confidence descending. Verdict choices: confirm / downgrade / escalate. See
  "Adjudication Protocol" section for the full protocol. Confirm and refute
  responses are annotations only. Disagreement, invalid output, containment
  failure, or unavailable service remains unresolved and never gates the verdict.
- **Auto-scan hook** (v2): PostToolUse hook auto-triggers on `git clone`, `git pull`, `pip install`, `npm install/update`, `uv add/sync`, `bun install/add`, `pnpm install/add`, `gem install/update`, `brew install/upgrade`, etc. Zero-overhead for non-matching commands.
- **Pre-execution gate** (v2.6): PreToolUse hook blocks known-malicious packages and pipe-to-shell commands BEFORE execution. IOC-only, <10ms latency, no subprocess calls.
- **Session security scanner** (v2.6.3): SessionStart hook detects updated plugins/skills/MCP servers, refreshes threat databases daily, runs fast IOC check + full 27-scanner deep scan on changed items. Sub-1ms when nothing changed.
- **.pth file injection detection** (v2): Detects liteLLM-style Python startup injection attacks (exec/eval/base64/known IOC filenames)
- **Transitive dependency scanning** (v2): Deep-parses `package-lock.json`, `yarn.lock`, `poetry.lock`, `Pipfile.lock` for supply chain IOCs
- **DAST scanner** (`scan_dast.py`): Dynamic analysis of Claude Code hooks with 8 malicious payload types, sandboxed execution
- **File integrity monitor** (`scan_integrity.py`): SHA256 baselines for critical config files, drift detection with `--watch`
- **IOC auto-update** (`--update-iocs`): Pull latest indicators of compromise from remote feed
- **Installation verification** (`--verify-install`): Verify repo-forensics itself hasn't been tampered with
- **GitHub Actions** (`action.yml`): CI/CD integration for automated security gating
- **Runtime behavior prediction** (`scan_runtime_dynamism.py`): Detects code that changes behavior after install: dynamic imports, fetch-then-execute, self-modification, time bombs, dynamic tool descriptions
- **Manifest drift detection** (`scan_manifest_drift.py`): Compares declared vs actual dependencies, catches phantom deps, runtime installs, conditional import+install fallbacks
- **MCP rug pull detection**: Tool descriptions sourced from database, network, env vars, or conditional logic
- **Enhanced AST analysis**: 12 patterns including marshal.loads, types.CodeType, sys.addaudithook, bytes decode obfuscation, self-modification
- **Test suite**: 1,800+ pytest tests covering all scanners
- **OpenClaw/ClawHub scanning**: Auto-detects OpenClaw skills, validates frontmatter, tools.json, SOUL.md, .clawhubignore
- **Anti-forensics detection** (v2): Self-deleting installers, package.json overwrite, version mismatch (Axios supply chain pattern)
- **Compromised version detection** (v2): Flags known-bad versions of legitimate packages (Axios, liteLLM, vpmdhaj OpenSearch typosquats, Miasma/Red Hat Cloud Services)
- **Suspicious npm scope detection** (v2): Flags systematic MCP server forking campaigns (iflow-mcp)
- **Host IOC scanning** (v2): Known RAT binary paths, C2 domains, malicious file hashes
- **CVE-2026-33068 detection** (v2): Workspace trust bypass via bypassPermissions in Claude Code settings
- **Post-incident forensics** (v2.2): npm cache/log artifacts, RAT binary detection, C2 persistence, node_modules traces that survive dropper self-cleanup
- **Supply chain hardening** (v2.2): .npmrc scanning, missing lockfile detection, git/HTTP dep flagging, hostname bypass fix, unbounded Python range detection, install script severity elevation
- **Devcontainer security scanning** (v2.6.5): JSON-based analysis of devcontainer.json for host secret mounts, container escape vectors, localEnv interpolation, lifecycle command risks, and untrusted features
- **Framework env prefix leak detection** (v2.6.5): Catches secrets exposed to browser bundles via NEXT_PUBLIC_, REACT_APP_, VITE_, EXPO_PUBLIC_, GATSBY_, NX_PUBLIC_ prefixes
- **process.env exposure detection** (v2.6.5): Flags console.log(process.env), JSON.stringify(process.env), and crash report env dumps
- **Docker ARG secret detection** (v2.6.5): Catches secrets passed via ARG directives (permanently visible in docker history)
- **1Password/Vault token detection** (v2.6.5): OP_CONNECT_TOKEN, ops_ service account tokens, hvs. Vault tokens
- **Content-based archive detection** (v2.11.2): archives are identified by magic bytes (`PK`/`ustar`/gzip) and `is_zipfile`, not extension, so a zip renamed to dodge gating (e.g. a `.docx.txt`) or a polyglot/self-extracting zip is still opened and scanned. Scripts/executables smuggled inside an OOXML (Office) document are flagged HIGH on structure alone.
- **Bytecode poisoning detection** (v2.11.2): a benign `.py` source shipping a malicious compiled `.pyc` (Python loads the cache over source) is caught by diffing raw `.pyc` danger markers against the sibling source — **no unmarshalling, no execution, cross-version-safe**, so the verdict never runs attacker bytecode. Best-effort multi-interpreter decode enriches the report; obfuscated `getattr`+char-built-name gadgets are also detected.
- **Registry-hijack / dependency-confusion detection** (v2.11.2): npm/yarn/pip/bun registry or `index-url` redirected to a non-canonical host is flagged (MEDIUM — corporate mirrors are legitimate), escalating to HIGH only when the redirect co-occurs with reviewer-disarming assurance prose. Resolves `${VAR}` indirection; runs in the install-time hook too.
- **27 scanners** with 41 correlation rules

## How Detection Stays Fresh

**Short answer: no, these are not static rules you maintain by hand.**

Detection runs in layers, each with its own update cadence:

1. **Shipped rule packs** (offline-first, always available): ~545 behavioral patterns in
   `data/rulepacks/*.json` ship with every release. They work on an air-gapped machine
   with no network access. Pack-driven surfaces: secrets, SAST, skill threats, MCP
   security, runtime dynamism, and shared patterns.

2. **Signed daily rule-pack feed**: Every 24 hours, `refresh_threat_dbs.py` fetches
   `iocs/rulepacks.json` and verifies the Ed25519 signature before accepting it. A
   verified bundle with a strictly newer `pack_version` overlays the shipped packs in
   `~/.cache/repo-forensics/rulepacks/`. New behavioral detections land on every
   installed instance without requiring a release. Tampered, invalid, or replayed
   bundles are rejected and the shipped packs stay authoritative.

3. **IOC / KEV / OSV feeds** (existing, also now signed): IP/domain/package indicators
   (`iocs/latest.json`), CISA KEV catalog, and OSV vulnerability queries update
   continuously via the same daily pipeline. The IOC feed now carries an Ed25519
   signature for parity with the rule-pack channel.

4. **LLM adjudication**: For WARN-tier findings the host agent applies judgment
   to ambiguous cases, effectively providing a zero-latency "update" for novel
   patterns that haven't been formalized into rules yet.

5. **Code releases** (for algorithmic surfaces): Scanners whose detection is
   algorithmic rather than pattern-based (entropy math, Python AST walking, DAST
   sandbox execution, git forensics logic, integrity hashing, manifest diffing,
   binary detection, lifecycle hook parsing, dependency resolution, infra config
   analysis, devcontainer parsing, post-incident artifact hunting, dataflow taint,
   entrypoint analysis) update only with code releases. These surfaces are explicitly
   not pack-driven and do not receive feed updates between releases.

## When to Use

- **Auditing a new repo or dependency** before adding it to your project
- **Vetting AI skills/plugins** before installation (prompt injection, credential theft, backdoors)
- **Auditing MCP servers** for tool poisoning, SQL injection, config risks
- **Security review** when someone asks "is this code secure?"
- **Forensic investigation** of a suspected compromise
- **CI/CD gating** with machine-readable output and exit codes
- **Hook security testing** to verify Claude Code hooks handle malicious input safely

## Quick Start

Full audit (all 27 scanners):
```bash
./scripts/run_forensics.sh /path/to/repo
```

Focused AI skill scan (17 scanners, faster):
```bash
./scripts/run_forensics.sh /path/to/repo --skill-scan
```

With IOC update and integrity monitoring:
```bash
./scripts/run_forensics.sh /path/to/repo --update-iocs --watch
```

Verify your installation:
```bash
./scripts/run_forensics.sh /path/to/repo --verify-install
```

JSON output for automation:
```bash
./scripts/run_forensics.sh /path/to/repo --format json
```

## Severity System

| Level | Score | Meaning | Exit Code |
|-------|-------|---------|-----------|
| CRITICAL | 4 | Active threat, immediate action required | 2 |
| HIGH | 3 | Significant risk, investigate promptly | 1 |
| MEDIUM | 2 | Potential issue, review recommended | 1 |
| LOW | 1 | Informational, may be false positive | 0 |

## Scanners

| Scanner | What It Detects | Mode |
|---------|----------------|------|
| **runtime_dynamism** | Dynamic imports, fetch-then-execute, self-modification, time bombs, dynamic tool descriptions | skill + full |
| **manifest_drift** | Phantom dependencies, runtime package installs, conditional import+install, declared-but-unused deps | skill + full |
| **skill_threats** | Prompt injection, unicode smuggling, prerequisite attacks, ClickFix, MCP tool injection | skill + full |
| **agent_skills** | SKILL.md frontmatter abuse, tools.json FSP, agent config injection (SOUL.md/AGENTS.md/CLAUDE.md), .clawhubignore bypass, ClawHavoc IOCs. Covers Claude Code, OpenClaw, Codex, Cursor, MCP. | skill + full |
| **mcp_security** | SQL injection to prompt escalation, tool poisoning, rug pull enablers, config CVEs | skill + full |
| **dataflow** | Source-to-sink taint tracking (env vars to network calls), cross-file import taint | skill + full |
| **secrets** | 50+ patterns: API keys, tokens, private keys, database URIs, JWTs, framework env prefix leaks, 1Password/Vault tokens, .env variant files | skill + full |
| **sast** | Dangerous functions, injection, shell execution across 8 languages, process.env exposure, path traversal | skill + full |
| **lifecycle** | NPM hooks + Python setup.py/pyproject.toml cmdclass overrides + anti-forensics (self-deleting installers, package.json overwrite) | skill + full |
| **integrity** | SHA256 baselines for .claude/settings.json, CLAUDE.md, hook scripts. Drift detection with `--watch` | full |
| **dast** | Dynamic hook testing: 8 payload types (injection, traversal, amplification, env leak) in sandbox | full |
| **entropy** | Per-string Shannon entropy, base64 blocks, hex strings (combo detection) | full |
| **infra** | Docker (ENV/ARG secrets, .env COPY), K8s, GitHub Actions, Claude Code config (CVE-2025-59536, CVE-2026-21852, CVE-2026-33068) | full |
| **devcontainer** | JSON-based devcontainer.json analysis: host mounts, privileged mode, docker.sock, remoteEnv localEnv interpolation, lifecycle commands, untrusted features | skill + full |
| **dependencies** | NPM + Python typosquatting, l33t normalization, IOC packages (SANDWORM_MODE 2026), 190+ package IOCs, compromised version detection (Axios, liteLLM, vpmdhaj, Miasma), suspicious scope detection (iflow-mcp) | full |
| **ast_analysis** | Python AST: obfuscated exec chains, `__reduce__` backdoors, marshal/types bytecode, audit hook abuse, self-modification | full |
| **binary** | Executables hidden as images/text files | full |
| **git_forensics** | Time anomalies, GPG signature issues, identity inconsistencies | full |
| **oversize** | Files padded past the 10 MB scan cap (head+tail window scan) and whitespace-inflation padding that hides a payload after a long whitespace run | skill + full |
| **bytecode** | Python `.pyc` bytecode: dangerous-call primitives (os.system/subprocess/exec), embedded URLs / credential paths, orphan bytecode, and **bytecode poisoning** (benign source + malicious `.pyc`) detected by a raw-marker source diff with no unmarshalling or execution (cross-version-safe). Disassembly is unmarshalled in an isolated subprocess so hostile bytecode cannot crash the scan, and is enrichment only — never load-bearing for the verdict | skill + full |
| **archive** | Payloads hidden inside `.zip/.docx/.xlsx/.pptx/.jar/.whl/.tar.*` and other archives, including archives **renamed/forged to dodge extension gating** (detected by magic bytes + `is_zipfile`) and **scripts/executables smuggled inside an OOXML document** (HIGH structural flag). Members are read in memory (never written to disk) and run through the SAST / trifecta / secret / skill-threat detectors; bomb-, fan-out-, and tar-link-safe | skill + full |
| **dead_anchors** | External-anchor **claimability**: repojackable GitHub owner/repo, phantom/removed npm & PyPI packages named in prose install commands, unregistered/expired domains (RDAP), and dangling free-tier cloud subdomains (Vercel/Railway/GitHub Pages/… via DNS + provider fingerprint). Closes the **Skilljacking** gap AIR's research says "tripped nothing at all" — the reference is dead and *claimable by an attacker* while the file content never changed. Network-touching but never-hard-fail: emits **only** on a confirmed-claimable anchor; live-and-owned and couldn't-check are silent. `--offline` degrades every anchor to silent. | skill + full |
| **yara** | Curated YARA signature scanner for malware, webshells, cryptominers, and hacktools. 11 hand-authored rules across 4 families (`data/yara/{webshells,malware,cryptominers,hacktools}.yar`) with multi-string conjunctive conditions + filesize bounds so a match is a confirmed family indicator, not a single-token guess. Each rule carries a `meta:` block (id/severity/category/confidence/title) mirrored in `data/yara/manifest.json` with per-file sha256 integrity checks. `yara-python` is an **optional** dependency: when absent the scanner degrades to a missing-tool capability gap (one stderr line, exit-neutral, stdout `[]`), so the core product stays zero-non-stdlib-deps and offline. Webshells and reverse-shell stagers are CRITICAL/HIGH; hacktools start at MEDIUM (conservative initial severities). | skill + full |

### Dead-anchor coverage and known scope (Skilljacking / repojacking)

`dead_anchors` (motivated by AIR's *Skilljacking* research, plus the *Circus of
Skills* free-tier-suffix study, Snyk's *ToxicSkills* IOCs, and the *SkillSieve*
dataset — logged in `references/research_sources.md`) is precise, not total.
Known limits, surfaced honestly rather than implied as covered:

- **Rate-limit budget:** unauthenticated GitHub API is 60 req/hr/IP, so GH calls
  are hard-capped (~20/scan, ~40 if `GITHUB_TOKEN` is set — read, never
  required, never prompted) and a total per-scan probe ceiling (~50) plus a
  wall-clock deadline bound the whole pass. Over-budget anchors degrade to
  couldn't-check (silent, safe-by-design), never a false clear.
- **RDAP ccTLD gaps:** `rdap.org`'s bootstrap covers gTLDs well; some ccTLDs
  degrade to couldn't-check rather than a verdict.
- **Multi-part-TLD heuristic:** a small vendored compound-TLD list (not a full
  Public Suffix List, to stay zero-non-stdlib-dep), so some obscure ccTLD
  domain reductions are imprecise.
- **Fingerprint rot:** cloud-provider "deleted app" page strings change over
  time; the fingerprint list is pack-driven (`data/rulepacks/dead_anchors.json`,
  refreshed by the signed `refresh_threat_dbs.py` overlay) so it can be updated
  without a code change. Netlify/Render/Surge deleted-app pages are generic 404
  copy and are deliberately NOT fingerprinted (stay live-and-owned, never
  guessed).
- **Deferred:** verdict-decay / time-based recheck (a link live today can go
  claimable later with zero file change) needs new persistent per-anchor state
  and is a Phase-2 item, not built here.

### Bypass coverage and known scope (archive / oversize / bytecode)

These three scanners close the "hide the payload where the text reader never
looks" bypass class (CSA / Trail of Bits, June 2026). Their coverage is precise,
not total — what they do **not** yet reach is surfaced as a loud INFO finding
(`unsupported-archive-type`, `opaque-archive`, `archive-scan-incomplete`,
`unanalyzable-bytecode`) rather than implied as covered:

- **Archives:** the listed zip- and tar-family formats only. `.7z .xz .zst .rar
  .cab` and encrypted/password-protected members are reported as unsupported/
  opaque, not inspected. Nested archives are opened to depth 2. A base64- or
  otherwise-encoded payload **inside** an archive member is not decoded here
  (encoded-blob rescan is deferred follow-up work).
- **Bytecode:** Python `.pyc` only. Java `.class`, Node `.jsc`, and `.wasm`
  carry compiled logic the source scanners also miss, but are out of scope for
  this scanner.
- **Oversize:** files over 10 MB are scanned by head+tail window (first + last
  1 MB), so a payload buried in the exact middle of a multi-hundred-MB file may
  be sampled rather than fully read.

## Dynamic Analysis (DAST)

The `scan_dast.py` scanner executes hook scripts with malicious payloads in a sandboxed subprocess:

**8 payload types:**
1. Prompt injection in tool input
2. Path traversal in file arguments
3. Command injection via backticks/subshell
4. Oversized input (amplification test)
5. Unicode smug
