Metadata-Version: 2.4
Name: decon-cli
Version: 0.1.0
Summary: decon — clone an untrusted repo and get it running inside a throwaway sandbox: no host secrets, network off by default, setup commands gated.
Project-URL: Homepage, https://github.com/staticplay/decon
Project-URL: Issues, https://github.com/staticplay/decon/issues
Author-email: Chris Hall <chrishall11993@gmail.com>
License: MIT
License-File: LICENSE
Keywords: ai-agents,bubblewrap,clone,docker,git,prompt-injection,sandbox,security,supply-chain
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# decon

**Clone a repo you don't trust and get it running — inside a throwaway sandbox, with no host secrets and network off by default.**

A repo can look clean and still be dangerous. The malicious payload need not live in the source: setup instructions (README, error handlers, `postinstall` hooks, a "run this to fix it" command) can fetch the real payload *at runtime* — from a DNS TXT record, a pastebin, a "license check" endpoint — and open a reverse shell. Static review and scanners miss it because there is nothing malicious in the tree. This is a live [indirect prompt-injection risk for AI coding agents](https://blog.mozilla.org/en/mozilla/ai/) with shell, file, and network access: the agent reads the "fix" as an instruction and runs it, dialing your machine home to an attacker.

`decon` draws the boundary where it belongs — **the sandbox, not anyone's judgement** (yours or an agent's):

- **The clone runs on the host in a stripped git environment** — hooks disabled, submodules off, credential helpers and `url.*.insteadOf` rewrites ignored, `ext::`/local-file protocols blocked, no terminal prompt. No repo-controlled code executes during fetch.
- **All work runs in a throwaway sandbox** with **no host secrets** (no SSH keys, tokens, `~/.env`, cloud creds), a non-inherited environment, and **network egress OFF by default**. Egress is an explicit, informed opt-in — because that is the reverse-shell channel.
- **A pre-flight scan** surfaces the exact trigger surfaces (`curl | bash`, DNS-TXT config pulls, `base64 -d | bash`, `/dev/tcp` reverse shells, committed binaries, setup files) so you eyeball them before the sandbox opens. It reports signal, never a "safe" verdict.

## Install

```sh
pipx install decon-cli    # recommended
# or
pip install decon-cli
```

Requires `git` and a sandbox backend:

- **docker** — strongest isolation, used by default when the daemon is reachable.
- **bubblewrap** (`bwrap`) — daemonless fallback.

## Use

```sh
# clone, scan, drop into a shell with NO network and NO secrets
decon https://github.com/owner/repo

# run the (dangerous) setup step deliberately, with network but still no secrets
decon https://github.com/owner/repo --allow-network -- pip install -e .

# a specific ref, and keep the clone afterwards
decon https://github.com/owner/repo --ref v1.2.3 --keep
```

Everything after `--` runs as the command inside the sandbox instead of an interactive shell.

### Options

| flag | meaning |
|---|---|
| `--allow-network` | give the **work** phase network egress (still no host secrets). Off by default. |
| `--ref REF` | branch, tag, or commit to check out |
| `--backend {auto,docker,bwrap}` | sandbox backend (default `auto`) |
| `--image IMG` | docker image for the work phase (default `python:3.12-slim`) |
| `--shell SH` | shell to launch (default `bash`, falls back to `sh`) |
| `--no-scan` | skip the pre-flight trigger-surface scan |
| `--yes`, `-y` | don't pause at the "eyeball the findings" gate |
| `--no-harden` | drop the extra docker hardening flags |
| `--keep` | don't delete the throwaway clone on exit |
| `--print-cmd` | print the sandbox command that would run, then exit |

## What it is *not*

It does not prove a repo is safe — it *can't*, because the payload may arrive at runtime. It removes the reward: no secrets to steal, no egress to phone home over, nothing persistent left behind. Treat every clone as hostile and this makes "hostile" cheap to handle.

## Threat model & guarantees

| vector | mitigation |
|---|---|
| hook / submodule execution on clone | host clone with `core.hooksPath=/dev/null`, `--no-recurse-submodules`, hooks dir wiped |
| token/credential theft via gitconfig | `GIT_CONFIG_GLOBAL/SYSTEM=/dev/null`, credential helper disabled, empty env |
| `ext::` / local-file protocol tricks | `protocol.ext/file.allow=never`, `GIT_ALLOW_PROTOCOL=http:https` |
| runtime-fetched payload / reverse shell | network egress denied by default |
| secret exfiltration | no SSH keys / tokens / `.env` / cloud creds mounted; host env not inherited |
| persistence | ephemeral container + throwaway clone, deleted on exit |

## License

MIT
