{"id":51136016,"url":"https://github.com/JBGamond/git-moat","last_synced_at":"2026-07-14T01:00:47.927Z","repository":{"id":363542915,"uuid":"1263800312","full_name":"JBGamond/git-moat","owner":"JBGamond","description":"A security-aware git clone wrapper that detects and neutralises supply-chain attack vectors before you open a repository.","archived":false,"fork":false,"pushed_at":"2026-06-09T10:15:05.000Z","size":55,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T11:22:00.900Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JBGamond.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-09T09:24:14.000Z","updated_at":"2026-06-09T10:26:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/JBGamond/git-moat","commit_stats":null,"previous_names":["jbgamond/git-moat"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/JBGamond/git-moat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBGamond%2Fgit-moat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBGamond%2Fgit-moat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBGamond%2Fgit-moat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBGamond%2Fgit-moat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JBGamond","download_url":"https://codeload.github.com/JBGamond/git-moat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBGamond%2Fgit-moat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35441637,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-13T02:00:06.543Z","response_time":119,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-25T18:00:29.290Z","updated_at":"2026-07-14T01:00:47.921Z","avatar_url":"https://github.com/JBGamond.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"git-moat-logo.png\" alt=\"git-moat logo\" width=\"320\" /\u003e\n\u003c/p\u003e\n\n# git-moat\n\n\u003e A security-aware git wrapper that detects and neutralises supply-chain attack vectors **before you open, switch to, or pull into a repository or branch**.\n\nCloning a repository has always felt safe. [That changed](https://safedep.io/miasma-worm-ai-coding-agent-config-injection/) when the Miasma worm targeted developers by planting auto-executing payloads inside AI coding agent configs, VS Code tasks, and NPM scripts — triggered the moment a folder is opened, not when a package is installed. **git-moat** stands at the drawbridge: it clones, checks out, or pulls the branch, scans every known attack surface in a temporary worktree, and blocks or remediates threats before any tool can fire them.\n\n---\n\n## Installation\n\n### Pre-built binary (no Rust required)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/JBGamond/git-moat/main/scripts/install.sh | bash\n```\n\nInstalls to `~/.local/bin` by default. Override with `INSTALL_DIR`:\n\n```bash\nINSTALL_DIR=/usr/local/bin curl -fsSL https://raw.githubusercontent.com/JBGamond/git-moat/main/scripts/install.sh | bash\n```\n\nPin a specific release with `VERSION=v0.2.0`.\n\nPre-built binaries are available for:\n\n| Platform | Architecture |\n|---|---|\n| Linux | x86\\_64, aarch64 |\n\n### From source\n\n```bash\ngit clone https://github.com/JBGamond/git-moat\ncd git-moat\nmake install          # builds release binary, installs to ~/.local/bin + shell completions\n```\n\nRequires [Rust](https://rustup.rs) ≥ 1.70.\n\n### Shell completions\n\nInstalled automatically by `make install` or `scripts/install.sh` (when run from a cloned repo). To enable manually:\n\n```bash\n# zsh — copy to a directory in $fpath\ncp completions/_git-moat ~/.local/share/zsh/site-functions/_git-moat\nautoload -Uz compinit \u0026\u0026 compinit\n\n# bash — source or copy to completions dir\ncp completions/git-moat.bash ~/.local/share/bash-completion/completions/git-moat\nsource ~/.local/share/bash-completion/completions/git-moat\n```\n\n---\n\n## Usage\n\n### Clone\n\nDrop-in replacement for `git clone`. All standard git flags are forwarded transparently.\n\n```bash\ngit-moat clone https://github.com/org/repo\ngit-moat clone --depth 1 -b main https://github.com/org/repo ./local-dir\n```\n\n### Checkout\n\nScan a branch **before** switching to it. git-moat checks out the target branch into a temporary detached worktree, runs all threat rules against it, removes the worktree, then performs the real switch only if the branch is safe.\n\nIf the local branch is **behind its remote tracking branch**, git-moat scans the remote's latest commits and fast-forwards only if they are clean.\n\nIf the target branch is **already active**, the worktree step is skipped and git-moat simply checks whether a pull is needed.\n\n```bash\ngit-moat checkout feature/new-api\n```\n\n- **Critical or High threats found** → checkout is blocked, threats are printed, exit 1.\n- **Medium threats found** → checkout proceeds with a warning.\n- **No threats** → switches normally.\n\nTab-completion for branch names works in both bash and zsh.\n\n### Pull\n\nSecure equivalent of `git pull`. Fetches the remote tracking branch, scans the incoming commits in a temporary worktree, then fast-forwards only if clean.\n\n```bash\ngit-moat pull\n```\n\nAlways acts on the current branch. No arguments needed.\n\n### Fetch\n\nPassthrough to `git fetch`. Fetch only downloads refs and never modifies the working tree, so no scan is required.\n\n```bash\ngit-moat fetch\ngit-moat fetch --all --prune\ngit-moat fetch origin main\n```\n\nTab-completion offers remote names and common flags.\n\n### Example output — clone\n\n```\ngit-moat — secure clone v0.1.0\nCloning https://github.com/org/repo ...\n\nScanning for threats in ./repo\n  -\u003e [1/8] Scanning for direct payloads and droppers (.github/setup.js)...\n  -\u003e [2/8] Checking agent session start hooks (.claude \u0026 .gemini)...\n  -\u003e [3/8] Checking VS Code task folderOpen auto-runs...\n  -\u003e [4/8] Validating Cursor rules (.mdc alwaysApply configurations)...\n  -\u003e [5/8] Inspecting package.json scripts and lifecycles...\n  -\u003e [6/8] Checking binding.gyp silent build expansions...\n  -\u003e [7/8] Inspecting git history, signatures, and backdated logs...\n  -\u003e [8/8] Scanning build/dependency manifests for auto-executing hook vectors...\n\n[CRITICAL] Miasma Dropper\n  File: .github/setup.js\n  Found '.github/setup.js' matching Miasma Worm IOCs: Caesar-shift eval harness\n  (shift-value-agnostic), oversized file (\u003e500 KB).\n  Action: DELETED\n\n[CRITICAL] Claude Session Hook Injection\n  File: .claude/settings.json\n  Found Claude settings with a SessionStart hook executing shell command(s):\n  [\"node .github/setup.js\"]\n  Action: DELETED\n\n2 threat(s) found and neutralised. Repository is safe to open.\n```\n\n### Example output — checkout\n\n```\n====================================================\ngit-moat Scanning branch: feature/backdoored\ngit-moat Repository:      /home/user/my-project\n====================================================\n\nScanning branch in temporary worktree — working tree untouched...\n\n⚠️  SECURITY ALERT: CHECKOUT BLOCKED ⚠️\nBranch contains Critical/High threats. Checkout was aborted.\nRemove the threat vectors from the branch before switching.\n\n1. [CRITICAL] Claude Session Hook Injection (.claude/settings.json)\n   Description: Found Claude settings with a SessionStart hook executing\n   shell command(s): [\"node .github/setup.js\"]\n   Remediation: i Logged only (scan-only or commit-log anomaly).\n```\n\n---\n\n## What it detects\n\ngit-moat scans eight attack surfaces documented in the [Miasma worm campaign](https://safedep.io/miasma-worm-ai-coding-agent-config-injection/) and extended to cover the broader supply-chain landscape:\n\n| Rule | File(s) | Vector |\n|---|---|---|\n| **Miasma Dropper** | `.github/setup.js` | Caesar-shift eval harness (shift-value-agnostic), AES-128-GCM loader, Bun runtime download, oversized JS file |\n| **AI Agent Hooks** | `.claude/settings.json`, `.gemini/settings.json` | `SessionStart` command hooks; cross-correlation of identical commands across both agents (exact Miasma fingerprint) |\n| **VS Code Auto-run** | `.vscode/tasks.json` | `folderOpen` tasks fire without user interaction |\n| **Cursor Prompt Injection** | `.cursor/rules/*.mdc` | `alwaysApply: true` + execution keywords (social-engineers the agent) |\n| **NPM Script Hijack** | `package.json` | `preinstall`, `postinstall`, `prepare`, `test` and related hooks |\n| **node-gyp Expansion** | `binding.gyp` | `\u003c!(...)` command expansions run silently at config time |\n| **Git History Anomaly** | git log | Unsigned `github-actions` commits, `[skip ci]` + dependency wording, backdated timestamps |\n| **Build Hook Auto-run** | `composer.json`, `Gemfile`, `*.gemspec`, `Makefile`, `Podfile`, `setup.py`, `pyproject.toml`, `pom.xml`, `build.gradle`, `build.rs` | Auto-executing hooks across PHP, Ruby, Make, iOS, Python, Java, and Rust build systems |\n\n### Threat levels\n\n| Level | Meaning |\n|---|---|\n| **CRITICAL** | Matches a known Miasma/Shai-Hulud IOC or direct dropper pattern — remove immediately |\n| **HIGH** | Auto-execution confirmed, payload unrecognised — high confidence of malice |\n| **MEDIUM** | Auto-execution capability present — verify before opening |\n\n### Remediation\n\ngit-moat removes threats in place rather than deleting the entire clone:\n\n- **Dropper files** (`.github/setup.js`, AI agent configs, VS Code tasks) are **deleted**\n- **`package.json`** has the malicious scripts stripped and the file rewritten cleanly\n- **Git history anomalies** are **logged** (no on-disk remediation possible)\n- All other flagged files are **deleted**\n\n---\n\n## How it works\n\ngit-moat is built around a hexagonal architecture with a strict domain / ports / adapters separation:\n\n```\nsrc/\n├── domain/\n│   ├── rules/          # One file per threat-detection rule (ThreatRule trait)\n│   ├── service.rs      # Use-case orchestrator: clone/checkout/pull → scan → remediate\n│   └── threat.rs       # Pure domain types (Threat, ThreatLevel, ScanReport)\n├── ports/              # Trait interfaces (GitClient, ThreatAnalyzer, Sanitizer)\n└── adapters/           # Concrete implementations (real git, filesystem, rules engine)\n```\n\nAdding a new detection rule is a single file implementing `ThreatRule::check(\u0026self, dir: \u0026Path) -\u003e Vec\u003cThreat\u003e` — no other code needs to change.\n\n---\n\n## Background\n\nOn 3 June 2026 the **Miasma worm** hit 123+ GitHub repositories across dozens of accounts (including Microsoft Azure and major npm packages). A single unsigned commit planted six files — one dropper, five triggers — wiring the same 4.3 MB payload to fire automatically in Claude Code, Gemini CLI, Cursor, VS Code, and `npm test`. The attack detonates when a developer *opens* the cloned folder, not when they install packages.\n\nFull write-up: [Miasma Worm Targets AI Coding Agents via GitHub Repos](https://safedep.io/miasma-worm-ai-coding-agent-config-injection/) — SafeDep, June 2026.\n\n---\n\n## Contributing\n\nThreat landscapes evolve — new rules are welcome. The bar for a new rule:\n\n1. Create `src/domain/rules/\u003cname\u003e.rs` implementing `ThreatRule`\n2. Register it in `src/domain/rules/mod.rs` and `src/adapters/threat_analyzer.rs`\n3. Add an integration test in `tests/integration.rs`\n\n```bash\ncargo test      # all tests must pass\ncargo clippy    # no warnings\n```\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJBGamond%2Fgit-moat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FJBGamond%2Fgit-moat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJBGamond%2Fgit-moat/lists"}