diff options
| -rw-r--r-- | README.md | 93 |
1 files changed, 83 insertions, 10 deletions
@@ -4,25 +4,84 @@ Abuse reporting for phishing mail. Parses a flagged message, extracts its indicators, resolves who to report each one to, and files the result to a MISP instance and to public abuse channels. -**Status: early.** The design is settled and the first part is being built. -Nothing here submits anything yet. +**Status: `init` and `parse` are built.** The rest of the pipeline is designed +but not written, see `docs/specs/2026-09-08-abusectl-design.md`. Nothing here +submits anything to anyone yet. ## What it does ``` +abusectl init # asks, writes ~/.config/abusectl/config.toml abusectl parse msg.eml # -> case directory, IOCs offline abusectl contacts <case> # + abuse contacts via RDAP network, read-only abusectl report <case> # + report bodies offline # review the bodies, by hand or in a mail client abusectl submit <case> # MISP, then the vendors network, writes +abusectl retry # whatever is due cron ``` -Each subcommand runs on its own and is useful on its own. `parse` triages a -message with no configuration at all; `parse`, `contacts` and `report` -together produce a document you can send by hand with no API key anywhere. +`init` and `parse` exist today. Each subcommand runs on its own and is useful +on its own: `parse` triages a message with no keys configured at all, and +`parse` plus `contacts` plus `report` will produce a document you can send by +hand before any API key exists. -State lives in a **case directory** rather than in memory, so a review can -take a week and survive a reboot. +State lives in a **case directory** rather than in memory, so a review can take +a week and survive a reboot. + +## Getting started + +```bash +abusectl init # asks, writes the config +abusectl parse message.eml # prints the case directory it created +``` + +`init` needs to know which `Received` hops your own mail infrastructure adds, +because everything below that boundary was written by whoever was talking to +your server and can be forged. Three ways to answer, at one prompt: + +- type the CIDRs, if you know them +- name a provider: `gmail`, `fastmail`, `proton`, `outlook`, `privateemail`, + `zoho`, whose published sending ranges ship with the tool +- point it at a message you know arrived legitimately with + `--from-sample good.eml` and pick your own hops out of the real chain + +`parse` refuses to run until that boundary is set. Guessing it wrong means +reporting an innocent third party, so it does not guess. + +For scripted or agent-driven setup, every question is also a flag and nothing +prompts: + +```bash +abusectl init --non-interactive --trusted-relays 192.0.2.0/24 198.51.100.0/24 +abusectl init --non-interactive --provider fastmail +``` + +Re-running `init` shows what is already configured and asks before replacing +it. Either way the old file is copied to `config.toml.bak-<timestamp>` first, +and any section this run does not set, such as `[misp]`, is carried across +untouched. + +## What a case looks like + +``` +~/.local/share/abusectl/2026-09-08-a3f1/ + source.eml the original, unredacted + manifest.json IOCs, and later contacts and per-destination status + bodies/ report bodies, once `report` exists +``` + +`manifest.json` after `parse`: + +```json +{ + "id": "ioc-1", "type": "ipv4", "value": "203.0.113.99", + "origin": "received-chain", "confidence": "boundary-hop" +} +``` + +Every indicator says where it came from. An IP from the trust boundary is +`boundary-hop`, the one address that can be stood behind; anything below it is +`untrusted-hop`, recorded because it may be useful but never presented as fact. ## Two properties that are not negotiable @@ -56,9 +115,23 @@ gets its own spec before it is built. ## Requirements -Python 3.11 or newer. `parse` and `report` need nothing else; `contacts` needs -an HTTP client and `submit` needs PyMISP. Development runs from a venv in the -checkout. +Python 3.12 or newer, and nothing else for what is built today: `init` and +`parse` are standard library only. `contacts` will need an HTTP client and +`submit` will need PyMISP, so a venv in the checkout is the development +arrangement; packaging comes once the tool does something worth installing. + +## Tests + +```bash +python3 -m unittest discover tests +``` + +100 tests, no framework, no network. Two of them are not ordinary unit tests +and are the ones worth knowing about. The `Received`-chain test is +mutation-checked: walking one hop too far makes it report an innocent party +named in a header the attacker wrote, and the test fails if that regresses. +And the parser's suite passes with `socket` disabled entirely, so +"nothing is fetched" is verified rather than documented. ## License |
