aboutsummaryrefslogtreecommitdiffstats
path: root/docs/specs/2026-09-09-report.md
AgeCommit message (Collapse)AuthorFilesLines
27 hoursdocs: settle the vendor and MISP destinationsDanilo M.1-0/+280
The report spec names three destination kinds and the module writes only one. Verified through the public API: generate() on a manifest with an IP contact returns a single email row, and nothing in the tree mentions a misp or api row. The plan the module was built from never carried that task, so report is complete against its plan and incomplete against its spec. Found when the submit brainstorm began, since submit is specified to fill bodies into rows that do not exist. The addendum settles which rows exist: configured AND holding an indicator the destination accepts, each row carrying only the types it can act on. The accepted types were read from each vendor's own documentation rather than from memory, which is the provider table's lesson. That reading is also what removed VirusTotal: its only submission endpoints are POST /urls and file upload, so it accepts exactly what URLhaus accepts and nothing else, and it takes no verdict with a submission. It goes to backlog item 6 with the findings, including the reason its more interesting read side collides with the fourth property. Also records that generate() rebuilds destinations wholesale, which is safe only while the freeze rule holds, and names that as a constraint on the submit spec rather than a description of this one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0176FYdVfpzUq8S9jecqQqL6
31 hoursfix: emit only registered ARF fields, verified against IANADanilo M.1-3/+10
Backlog item 4 asked for a primary source for the machine-readable part. It exists, and it is IANA's MARF registry rather than x-arf's own docs. Checked against that registry and RFC 5965 itself on 2026-09-10. Report-Type is not a registered field. The registry lists 25 names and it is absent; it came from this spec's worked example rather than a decision. Removed, because Feedback-Type already says it. Keeping it would have been harmless, since RFC 5965 section 6 makes ignoring an unknown field a MUST for the receiver, but that same section requires an extension field be registered, and a desk should be able to look up every field in a document this tool sends. Feedback-Type moves from abuse to fraud. RFC 5965 registers fraud as "indicates some kind of fraud or phishing activity" and abuse as "unsolicited email or some other kind of email abuse". This tool reports phishing, and some desks route fraud separately from bulk spam. Reported-Uri becomes Reported-URI, the spelling in the ABNF and the registration. The RFC's own worked example uses the other one, which is where this came from. Names are case-insensitive so nothing was broken. A new test holds the registry's 25 names and asserts every emitted field is one of them, with Source named as the single deliberate exception, so a new unregistered field cannot arrive unnoticed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LByBnw83xr9YP85nskzkyE
2 daysfix: strip the envelope recipient in every shape the grammar allowsDanilo M.1-0/+31
The for-clause rule anchored on the clause terminator, which is strictly stronger than RFC 5321 4.4: For sits inside Opt-info, so With, ID, Via or a CFWS comment may legitimately follow it, and its ABNF is 1*( Path / Mailbox ) where Mailbox carries no angle brackets. Only the tidiest shape was stripped. Four ordinary ones published the victim's address in the one header a report reproduces verbatim, including "for <a@b> (envelope-from <c@d>);", which is routine Exim and Sendmail output. Anchored on the address instead. The envelope SENDER survives the cut, since that is what the report is about, and the leftover whitespace, orphan separator and emptied comment are tidied because the line is published to a third party verbatim. Table-driven over all five shapes; reverting the regex fails six of nine tests in the class. Two accepted disclosures are now named in the spec's "Where the headers come from" rather than living in a test comment: our own relay's hostname, which the by and authserv-id clauses carry by construction, and attacker-controlled free text in Subject and the From display name, which the whitelist does not and should not filter. The second is asserted as a documented limit so it reads as deliberate, and the manifest assertion now bars the obfuscated you%40example.org spelling alongside the plain one. case.create() seeds an empty headers block so an unparsed case has the same shape as a parsed one. Additive, so FORMAT_VERSION is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xj1ayFRSUQ2u7cwb3S4axE
2 daysdocs: settle the report specDanilo M.1-0/+412
Settles the three questions the umbrella design left open for report: the X-ARF schema, which fields the reporting identity fills, and the plain-text alternative. The message is not attached. RFC 5965's text/rfc822-headers carries a whitelist of headers instead, and parse stores that whitelist in the manifest so report never opens source.eml, which is the first property applied one level down. Also settles destination grouping per abuse address, an explicit unreportable list, and a freeze rule: any sent destination freezes the whole case, marked by a write-once field, with body integrity checked by content hash rather than mtime. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xj1ayFRSUQ2u7cwb3S4axE