aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md93
1 files changed, 83 insertions, 10 deletions
diff --git a/README.md b/README.md
index 78eb42f..9354ee0 100644
--- a/README.md
+++ b/README.md
@@ -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