SKILL.md
SKILL.mdBrowse 8 files
4,693 tokens
19,852 bytes
Token encoding: o200k_base
Snapshot 24fd22b
1---2name: oss-forensics3description: "GitHub supply-chain forensics: recovery, IOCs, reporting."4version: 1.0.05author: Teknium (teknium1), Hermes Agent6license: MIT7platforms: [linux, macos, windows]8category: security9triggers:10 - "investigate this repository"11 - "investigate [owner/repo]"12 - "check for supply chain compromise"13 - "recover deleted commits"14 - "forensic analysis of [owner/repo]"15 - "was this repo compromised"16 - "supply chain attack"17 - "suspicious commit"18 - "force push detected"19 - "IOC extraction"20toolsets:21 - terminal22 - web23 - file24 - delegation25metadata:26 hermes:27 tags: [Security, Forensics, GitHub, Supply-Chain]28 related_skills: []29---30 31# OSS Security Forensics Skill32 33A 7-phase multi-agent investigation framework for researching open-source supply chain attacks.34Adapted from RAPTOR's forensics system. Covers GitHub Archive, Wayback Machine, GitHub API,35local git analysis, IOC extraction, evidence-backed hypothesis formation and validation,36and final forensic report generation.37 38---39 40## ⚠️ Anti-Hallucination Guardrails41 42Read these before every investigation step. Violating them invalidates the report.43 441. **Evidence-First Rule**: Every claim in any report, hypothesis, or summary MUST cite at least one evidence ID (`EV-XXXX`). Assertions without citations are forbidden.452. **STAY IN YOUR LANE**: Each sub-agent (investigator) has a single data source. Do NOT mix sources. The GH Archive investigator does not query the GitHub API, and vice versa. Role boundaries are hard.463. **Fact vs. Hypothesis Separation**: Mark all unverified inferences with `[HYPOTHESIS]`. Only statements verified against original sources may be stated as facts.474. **No Evidence Fabrication**: The hypothesis validator MUST mechanically check that every cited evidence ID actually exists in the evidence store before accepting a hypothesis.485. **Proof-Required Disproval**: A hypothesis cannot be dismissed without a specific, evidence-backed counter-argument. "No evidence found" is not sufficient to disprove—it only makes a hypothesis inconclusive.496. **SHA/URL Double-Verification**: Any commit SHA, URL, or external identifier cited as evidence must be independently confirmed from at least two sources before being marked as verified.507. **Suspicious Code Rule**: Never run code found inside the investigated repository locally. Analyze statically only, or use `execute_code` in a sandboxed environment.518. **Secret Redaction**: Any API keys, tokens, or credentials discovered during investigation must be redacted in the final report. Log them internally only.52 53---54 55## Example Scenarios56 57- **Scenario A: Dependency Confusion**: A malicious package `internal-lib-v2` is uploaded to NPM with a higher version than the internal one. The investigator must track when this package was first seen and if any PushEvents in the target repo updated `package.json` to this version.58- **Scenario B: Maintainer Takeover**: A long-term contributor's account is used to push a backdoored `.github/workflows/build.yml`. The investigator looks for PushEvents from this user after a long period of inactivity or from a new IP/location (if detectable via BigQuery).59- **Scenario C: Force-Push Hide**: A developer accidentally commits a production secret, then force-pushes to "fix" it. The investigator uses `git fsck` and GH Archive to recover the original commit SHA and verify what was leaked.60 61---62 63> **Path convention**: Throughout this skill, `SKILL_DIR` refers to the root of this skill's64> installation directory (the folder containing this `SKILL.md`). When the skill is loaded,65> resolve `SKILL_DIR` to the actual path — e.g. `~/.hermes/skills/security/oss-forensics/`66> or the `optional-skills/` equivalent. All script and template references are relative to it.67 68## Phase 0: Initialization69 701. Create investigation working directory:71 ```bash72 mkdir investigation_$(echo "REPO_NAME" | tr '/' '_')73 cd investigation_$(echo "REPO_NAME" | tr '/' '_')74 ```752. Initialize the evidence store:76 ```bash77 python SKILL_DIR/scripts/evidence-store.py --store evidence.json list78 ```793. Copy the forensic report template:80 ```bash81 cp SKILL_DIR/templates/forensic-report.md ./investigation-report.md82 ```834. Create an `iocs.md` file to track Indicators of Compromise as they are discovered.845. Record the investigation start time, target repository, and stated investigation goal.85 86---87 88## Phase 1: Prompt Parsing and IOC Extraction89 90**Goal**: Extract all structured investigative targets from the user's request.91 92**Actions**:93- Parse the user prompt and extract:94 - Target repository (`owner/repo`)95 - Target actors (GitHub handles, email addresses)96 - Time window of interest (commit date ranges, PR timestamps)97 - Provided Indicators of Compromise: commit SHAs, file paths, package names, IP addresses, domains, API keys/tokens, malicious URLs98 - Any linked vendor security reports or blog posts99 100**Tools**: Reasoning only, or `execute_code` for regex extraction from large text blocks.101 102**Output**: Populate `iocs.md` with extracted IOCs. Each IOC must have:103- Type (from: COMMIT_SHA, FILE_PATH, API_KEY, SECRET, IP_ADDRESS, DOMAIN, PACKAGE_NAME, ACTOR_USERNAME, MALICIOUS_URL, OTHER)104- Value105- Source (user-provided, inferred)106 107**Reference**: See [evidence-types.md](./references/evidence-types.md) for IOC taxonomy.108 109---110 111## Phase 2: Parallel Evidence Collection112 113Spawn up to 5 specialist investigator sub-agents using `delegate_task` (batch mode, max 3 concurrent). Each investigator has a **single data source** and must not mix sources.114 115> **Orchestrator note**: Pass the IOC list from Phase 1 and the investigation time window in the `context` field of each delegated task.116 117---118 119### Investigator 1: Local Git Investigator120 121**ROLE BOUNDARY**: You query the LOCAL GIT REPOSITORY ONLY. Do not call any external APIs.122 123**Actions**:124```bash125# Clone repository126git clone https://github.com/OWNER/REPO.git target_repo && cd target_repo127 128# Full commit log with stats129git log --all --full-history --stat --format="%H|%ae|%an|%ai|%s" > ../git_log.txt130 131# Detect force-push evidence (orphaned/dangling commits)132git fsck --lost-found --unreachable 2>&1 | grep commit > ../dangling_commits.txt133 134# Check reflog for rewritten history135git reflog --all > ../reflog.txt136 137# List ALL branches including deleted remote refs138git branch -a -v > ../branches.txt139 140# Find suspicious large binary additions141git log --all --diff-filter=A --name-only --format="%H %ai" -- "*.so" "*.dll" "*.exe" "*.bin" > ../binary_additions.txt142 143# Check for GPG signature anomalies144git log --show-signature --format="%H %ai %aN" > ../signature_check.txt 2>&1145```146 147**Evidence to collect** (add via `python SKILL_DIR/scripts/evidence-store.py add`):148- Each dangling commit SHA → type: `git`149- Force-push evidence (reflog showing history rewrite) → type: `git`150- Unsigned commits from verified contributors → type: `git`151- Suspicious binary file additions → type: `git`152 153**Reference**: See [recovery-techniques.md](./references/recovery-techniques.md) for accessing force-pushed commits.154 155---156 157### Investigator 2: GitHub API Investigator158 159**ROLE BOUNDARY**: You query the GITHUB REST API ONLY. Do not run git commands locally.160 161**Actions**:162```bash163# Commits (paginated)164curl -s "https://api.github.com/repos/OWNER/REPO/commits?per_page=100" > api_commits.json165 166# Pull Requests including closed/deleted167curl -s "https://api.github.com/repos/OWNER/REPO/pulls?state=all&per_page=100" > api_prs.json168 169# Issues170curl -s "https://api.github.com/repos/OWNER/REPO/issues?state=all&per_page=100" > api_issues.json171 172# Contributors and collaborator changes173curl -s "https://api.github.com/repos/OWNER/REPO/contributors" > api_contributors.json174 175# Repository events (last 300)176curl -s "https://api.github.com/repos/OWNER/REPO/events?per_page=100" > api_events.json177 178# Check specific suspicious commit SHA details179curl -s "https://api.github.com/repos/OWNER/REPO/git/commits/SHA" > commit_detail.json180 181# Releases182curl -s "https://api.github.com/repos/OWNER/REPO/releases?per_page=100" > api_releases.json183 184# Check if a specific commit exists (force-pushed commits may 404 on commits/ but succeed on git/commits/)185curl -s "https://api.github.com/repos/OWNER/REPO/commits/SHA" | jq .sha186```187 188**Cross-reference targets** (flag discrepancies as evidence):189- PR exists in archive but missing from API → evidence of deletion190- Contributor in archive events but not in contributors list → evidence of permission revocation191- Commit in archive PushEvents but not in API commit list → evidence of force-push/deletion192 193**Reference**: See [evidence-types.md](./references/evidence-types.md) for GH event types.194 195---196 197### Investigator 3: Wayback Machine Investigator198 199**ROLE BOUNDARY**: You query the WAYBACK MACHINE CDX API ONLY. Do not use the GitHub API.200 201**Goal**: Recover deleted GitHub pages (READMEs, issues, PRs, releases, wiki pages).202 203**Actions**:204```bash205# Search for archived snapshots of the repo main page206curl -s "https://web.archive.org/cdx/search/cdx?url=github.com/OWNER/REPO&output=json&limit=100&from=YYYYMMDD&to=YYYYMMDD" > wayback_main.json207 208# Search for a specific deleted issue209curl -s "https://web.archive.org/cdx/search/cdx?url=github.com/OWNER/REPO/issues/NUM&output=json&limit=50" > wayback_issue_NUM.json210 211# Search for a specific deleted PR212curl -s "https://web.archive.org/cdx/search/cdx?url=github.com/OWNER/REPO/pull/NUM&output=json&limit=50" > wayback_pr_NUM.json213 214# Fetch the best snapshot of a page215# Use the Wayback Machine URL: https://web.archive.org/web/TIMESTAMP/ORIGINAL_URL216# Example: https://web.archive.org/web/20240101000000*/github.com/OWNER/REPO217 218# Advanced: Search for deleted releases/tags219curl -s "https://web.archive.org/cdx/search/cdx?url=github.com/OWNER/REPO/releases/tag/*&output=json" > wayback_tags.json220 221# Advanced: Search for historical wiki changes222curl -s "https://web.archive.org/cdx/search/cdx?url=github.com/OWNER/REPO/wiki/*&output=json" > wayback_wiki.json223```224 225**Evidence to collect**:226- Archived snapshots of deleted issues/PRs with their content227- Historical README versions showing changes228- Evidence of content present in archive but missing from current GitHub state229 230**Reference**: See [github-archive-guide.md](./references/github-archive-guide.md) for CDX API parameters.231 232---233 234### Investigator 4: GH Archive / BigQuery Investigator235 236**ROLE BOUNDARY**: You query GITHUB ARCHIVE via BIGQUERY ONLY. This is a tamper-proof record of all public GitHub events.237 238> **Prerequisites**: Requires Google Cloud credentials with BigQuery access (`gcloud auth application-default login`). If unavailable, skip this investigator and note it in the report.239 240**Cost Optimization Rules** (MANDATORY):2411. ALWAYS run a `--dry_run` before every query to estimate cost.2422. Use `_TABLE_SUFFIX` to filter by date range and minimize scanned data.2433. Only SELECT the columns you need.2444. Add a LIMIT unless aggregating.245 246```bash247# Template: safe BigQuery query for PushEvents to OWNER/REPO248bq query --use_legacy_sql=false --dry_run "249SELECT created_at, actor.login, payload.commits, payload.before, payload.head,250 payload.size, payload.distinct_size251FROM \`githubarchive.month.*\`252WHERE _TABLE_SUFFIX BETWEEN 'YYYYMM' AND 'YYYYMM'253 AND type = 'PushEvent'254 AND repo.name = 'OWNER/REPO'255LIMIT 1000256"257# If cost is acceptable, re-run without --dry_run258 259# Detect force-pushes: zero-distinct_size PushEvents mean commits were force-erased260# payload.distinct_size = 0 AND payload.size > 0 → force push indicator261 262# Check for deleted branch events263bq query --use_legacy_sql=false "264SELECT created_at, actor.login, payload.ref, payload.ref_type265FROM \`githubarchive.month.*\`266WHERE _TABLE_SUFFIX BETWEEN 'YYYYMM' AND 'YYYYMM'267 AND type = 'DeleteEvent'268 AND repo.name = 'OWNER/REPO'269LIMIT 200270"271```272 273**Evidence to collect**:274- Force-push events (payload.size > 0, payload.distinct_size = 0)275- DeleteEvents for branches/tags276- WorkflowRunEvents for suspicious CI/CD automation277- PushEvents that precede a "gap" in the git log (evidence of rewrite)278 279**Reference**: See [github-archive-guide.md](./references/github-archive-guide.md) for all 12 event types and query patterns.280 281---282 283### Investigator 5: IOC Enrichment Investigator284 285**ROLE BOUNDARY**: You enrich EXISTING IOCs from Phase 1 using passive public sources ONLY. Do not execute any code from the target repository.286 287**Actions**:288- For each commit SHA: attempt recovery via direct GitHub URL (`github.com/OWNER/REPO/commit/SHA.patch`)289- For each domain/IP: check passive DNS, WHOIS records (via `web_extract` on public WHOIS services)290- For each package name: check npm/PyPI for matching malicious package reports291- For each actor username: check GitHub profile, contribution history, account age292- Recover force-pushed commits using 3 methods (see [recovery-techniques.md](./references/recovery-techniques.md))293 294---295 296## Phase 3: Evidence Consolidation297 298After all investigators complete:299 3001. Run `python SKILL_DIR/scripts/evidence-store.py --store evidence.json list` to see all collected evidence.3012. For each piece of evidence, verify the `content_sha256` hash matches the original source.3023. Group evidence by:303 - **Timeline**: Sort all timestamped evidence chronologically304 - **Actor**: Group by GitHub handle or email305 - **IOC**: Link evidence to the IOC it relates to3064. Identify **discrepancies**: items present in one source but absent in another (key deletion indicators).3075. Flag evidence as `[VERIFIED]` (confirmed from 2+ independent sources) or `[UNVERIFIED]` (single source only).308 309---310 311## Phase 4: Hypothesis Formation312 313A hypothesis must:314- State a specific claim (e.g., "Actor X force-pushed to BRANCH on DATE to erase commit SHA")315- Cite at least 2 evidence IDs that support it (`EV-XXXX`, `EV-YYYY`)316- Identify what evidence would disprove it317- Be labeled `[HYPOTHESIS]` until validated318 319**Common hypothesis templates** (see [investigation-templates.md](./references/investigation-templates.md)):320- Maintainer Compromise: legitimate account used post-takeover to inject malicious code321- Dependency Confusion: package name squatting to intercept installs322- CI/CD Injection: malicious workflow changes to run code during builds323- Typosquatting: near-identical package name targeting misspellers324- Credential Leak: token/key accidentally committed then force-pushed to erase325 326For each hypothesis, spawn a `delegate_task` sub-agent to attempt to find disconfirming evidence before confirming.327 328---329 330## Phase 5: Hypothesis Validation331 332The validator sub-agent MUST mechanically check:333 3341. For each hypothesis, extract all cited evidence IDs.3352. Verify each ID exists in `evidence.json` (hard failure if any ID is missing → hypothesis rejected as potentially fabricated).3363. Verify each `[VERIFIED]` piece of evidence was confirmed from 2+ sources.3374. Check logical consistency: does the timeline depicted by the evidence support the hypothesis?3385. Check for alternative explanations: could the same evidence pattern arise from a benign cause?339 340**Output**:341- `VALIDATED`: All evidence cited, verified, logically consistent, no plausible alternative explanation.342- `INCONCLUSIVE`: Evidence supports hypothesis but alternative explanations exist or evidence is insufficient.343- `REJECTED`: Missing evidence IDs, unverified evidence cited as fact, logical inconsistency detected.344 345Rejected hypotheses feed back into Phase 4 for refinement (max 3 iterations).346 347---348 349## Phase 6: Final Report Generation350 351Populate `investigation-report.md` using the template in [forensic-report.md](./templates/forensic-report.md).352 353**Mandatory sections**:354- Executive Summary: one-paragraph verdict (Compromised / Clean / Inconclusive) with confidence level355- Timeline: chronological reconstruction of all significant events with evidence citations356- Validated Hypotheses: each with status and supporting evidence IDs357- Evidence Registry: table of all `EV-XXXX` entries with source, type, and verification status358- IOC List: all extracted and enriched Indicators of Compromise359- Chain of Custody: how evidence was collected, from what sources, at what timestamps360- Recommendations: immediate mitigations if compromise detected; monitoring recommendations361 362**Report rules**:363- Every factual claim must have at least one `[EV-XXXX]` citation364- Executive Summary must state confidence level (High / Medium / Low)365- All secrets/credentials must be redacted to `[REDACTED]`366 367---368 369## Phase 7: Completion370 3711. Run final evidence count: `python SKILL_DIR/scripts/evidence-store.py --store evidence.json list`3722. Archive the full investigation directory.3733. If compromise is confirmed:374 - List immediate mitigations (rotate credentials, pin dependency hashes, notify affected users)375 - Identify affected versions/packages376 - Note disclosure obligations (if a public package: coordinate with the package registry)3774. Present the final `investigation-report.md` to the user.378 379---380 381## Ethical Use Guidelines382 383This skill is designed for **defensive security investigation** — protecting open-source software from supply chain attacks. It must not be used for:384 385- **Harassment or stalking** of contributors or maintainers386- **Doxing** — correlating GitHub activity to real identities for malicious purposes387- **Competitive intelligence** — investigating proprietary or internal repositories without authorization388- **False accusations** — publishing investigation results without validated evidence (see anti-hallucination guardrails)389 390Investigations should be conducted with the principle of **minimal intrusion**: collect only the evidence necessary to validate or refute the hypothesis. When publishing results, follow responsible disclosure practices and coordinate with affected maintainers before public disclosure.391 392If the investigation reveals a genuine compromise, follow the coordinated vulnerability disclosure process:3931. Notify the repository maintainers privately first3942. Allow reasonable time for remediation (typically 90 days)3953. Coordinate with package registries (npm, PyPI, etc.) if published packages are affected3964. File a CVE if appropriate397 398---399 400## API Rate Limiting401 402GitHub REST API enforces rate limits that will interrupt large investigations if not managed.403 404**Authenticated requests**: 5,000/hour (requires `GITHUB_TOKEN` env var or `gh` CLI auth)405**Unauthenticated requests**: 60/hour (unusable for investigations)406 407**Best practices**:408- Always authenticate: `export GITHUB_TOKEN=ghp_...` or use `gh` CLI (auto-authenticates)409- Use conditional requests (`If-None-Match` / `If-Modified-Since` headers) to avoid consuming quota on unchanged data410- For paginated endpoints, fetch all pages in sequence — don't parallelize against the same endpoint411- Check `X-RateLimit-Remaining` header; if below 100, pause for `X-RateLimit-Reset` timestamp412- BigQuery has its own quotas (10 TiB/day free tier) — always dry-run first413- Wayback Machine CDX API: no formal rate limit, but be courteous (1-2 req/sec max)414 415If rate-limited mid-investigation, record the partial results in the evidence store and note the limitation in the report.416 417---418 419## Reference Materials420 421- [github-archive-guide.md](./references/github-archive-guide.md) — BigQuery queries, CDX API, 12 event types422- [evidence-types.md](./references/evidence-types.md) — IOC taxonomy, evidence source types, observation types423- [recovery-techniques.md](./references/recovery-techniques.md) — Recovering deleted commits, PRs, issues424- [investigation-templates.md](./references/investigation-templates.md) — Pre-built hypothesis templates per attack type425- [evidence-store.py](./scripts/evidence-store.py) — CLI tool for managing the evidence JSON store426- [forensic-report.md](./templates/forensic-report.md) — Structured report template427 Discovery context
Discovered by repository scan. No exact path reference found in the snapshot’s root AGENTS.md.