Skip to content

mailsec: full CLI and SDK coverage of the Email Security API - #344

Merged
maximelb merged 3 commits into
masterfrom
mailsec-cli
Aug 28, 2026
Merged

mailsec: full CLI and SDK coverage of the Email Security API#344
maximelb merged 3 commits into
masterfrom
mailsec-cli

Conversation

@maximelb

Copy link
Copy Markdown
Contributor

Adds limacharlie mailsec ... over all 22 /mailsec/{oid}/* gateway routes: coverage, the message index and drawer, the justified raw-EML download, similar-message pivots, per-message and campaign-wide actions, campaigns, sender profiles, the action audit trail, standalone EML analysis, the abuse-mailbox report queue, retro-hunts, custom-rule validation and backtest, the connection preflight, and the served onboarding guide.

Noun-verb groups (message get, report resolve, rule backtest) matching the cloudsec surface, with an --explain entry per command.

Three contracts the wrapper preserves rather than smooths over

Each is a place where being "helpful" would quietly change what the caller asked for:

  • Booleans are tri-state. Absent means unconstrained; it is not False. --user-reported / --no-user-reported resolves to None when neither is given. Collapsing the two would narrow every unfiltered read invisibly, because a smaller result set looks exactly like a correct one.
  • Cursors are opaque and passed back verbatim — they encode which index the walk is pinned to and are bound to the filter set that minted them.
  • confirm is absent, not empty. Campaign sweeps and hunt remediation preview by default; omitting --confirm must reach the server as an absence rather than an empty string that could read as a confirmation.

The EML download refuses an empty justification client-side — the justification is what makes the access auditable, and discovering that requirement as a server error after the fact is a worse way to learn it.

Verification

  • 21 SDK tests, including a route-coverage table asserting all 22 routes have a method with the right verb and URL. A route with no SDK method is a capability the CLI silently lacks until someone needs it.
  • The three CLI inventory guards (command count, expected commands, module map) updated to include mailsec — which is what those guards are for.
  • Verified against a stashed baseline: the suite's 111 pre-existing failures are unchanged and this introduces none.
  • Exercised against the live API on exp: report list (with paging), coverage, a filtered message list, and a rule backtest over the real 34-message window.

Adds `limacharlie mailsec ...` over all 22 `/mailsec/{oid}/*` gateway routes:
coverage, the message index and drawer, the justified raw-EML download,
similar-message pivots, per-message and campaign-wide actions, campaigns,
sender profiles, the action audit trail, standalone EML analysis, the
abuse-mailbox report queue, retro-hunts, custom-rule validation and backtest,
the connection preflight, and the served onboarding guide.

Structured as noun-verb groups (`message get`, `report resolve`, `rule
backtest`) matching the cloudsec surface, with an --explain entry per command.

Three contracts the wrapper had to preserve rather than smooth over, because
each is a place where being "helpful" would quietly change what the caller
asked for:

- BOOLEANS ARE TRI-STATE. Absent means unconstrained; it is NOT False. The
  --user-reported / --no-user-reported pair resolves to None when neither is
  given, so an unfiltered read stays unfiltered. Collapsing the two would
  narrow every such read invisibly, because a smaller result set looks exactly
  like a correct one.
- CURSORS ARE OPAQUE and are passed back verbatim. They encode which index the
  walk is pinned to and are bound to the filter set that minted them.
- CONFIRM IS ABSENT, NOT EMPTY. Campaign sweeps and hunt remediation preview
  by default; omitting --confirm must reach the server as an absence rather
  than an empty string that could read as a confirmation.

The EML download refuses an empty justification client-side. The justification
is what makes the access auditable, and discovering that requirement as a
server error after the fact is a worse way to learn it.

Tests: 21 SDK tests including a route-coverage table that asserts all 22 routes
have a method with the right verb and URL — a route with no SDK method is a
capability the CLI silently lacks until someone needs it.

The three CLI inventory guards (command count, expected commands, module map)
are updated to include mailsec, which is what those guards exist for. Verified
against a stashed baseline: the suite's 111 pre-existing failures are unchanged
and this adds none.

Exercised against the live API on exp: report list (with paging), coverage,
filtered message list, and a rule backtest over the real 34-message window.
lcbill
lcbill previously approved these changes Aug 28, 2026
`message eml --output FILE` collided with the global `--output <format>`
option. A user or an agent writing `--output yaml` would have silently created
a file named 'yaml' instead of formatting the response — a wrong outcome that
looks like nothing happened.

It is --out-file now, and the help says why so the next person does not
reintroduce it.
lcbill
lcbill previously approved these changes Aug 28, 2026
Found reviewing my own PR: cloudsec quotes user-controlled path segments with
`_quote(value, safe="")` and this SDK escaped none.

Most of these ids are server-minted UUIDs and are harmless either way. Two are
not: the sender key is an address or domain a person types, and the connection
record is a hive record name. An unescaped slash in either silently addresses a
DIFFERENT route rather than failing, which turns a typo into a request nobody
intended.

Verified against the live API that ordinary keys still resolve — `sender get
admin@elephantwhich.com` escapes the `@` and the server returns the profile.
@maximelb
maximelb merged commit 0256443 into master Aug 28, 2026
6 checks passed
@maximelb
maximelb deleted the mailsec-cli branch August 28, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants