diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-08 15:09:11 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-08 15:09:11 +0200 |
| commit | 4ea2cffd2c901b652b1236a3d0eb3ce39cc62c1d (patch) | |
| tree | b111cf44117adccf2f2cbb0e7ba7516d44d32b25 | |
| parent | 73a407f96076a971078b47be8586d114f437172b (diff) | |
| download | abusectl-4ea2cffd2c901b652b1236a3d0eb3ce39cc62c1d.tar.gz abusectl-4ea2cffd2c901b652b1236a3d0eb3ce39cc62c1d.zip | |
docs: README covers init and parse
Marks what is built rather than describing the whole pipeline as though it
existed, documents the three ways to answer the trust-boundary question,
and shows what a case directory holds and what an origin and confidence
mean on an indicator.
Every command in it was run before committing, including the two
non-interactive forms.
The tests section names the two checks that are not ordinary unit tests,
because they are the ones a reader would otherwise not know to keep: the
Received-chain mutation check, and the parser suite running with sockets
disabled.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KphFXTc2QajxXsHWyvGJ4R
| -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 |
