aboutsummaryrefslogtreecommitdiffstats
path: root/docs/BACKLOG.md
blob: 780cbf572649f861e8857f3bf99d44d85df5d86e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# Backlog

Open items, newest last. One numbering sequence; a closed item keeps its
number and gains a status rather than being renumbered.

| # | Item | Size | Status |
|---|------|------|--------|
| 1 | Skip boilerplate namespace URLs | XS | open |
| 2 | An IDN indicator resolves to no contact | S | open |
| 3 | Expose kept cases so qtmaildir can tag spam | ? | open, unsized |
| 4 | `Report-Type: phishing` is unverified against x-arf | XS | open |

## 1. Skip boilerplate namespace URLs

**Observed.** A sweep of 82 real spam messages reported 8 URL IOCs pointing
at `www.w3.org`, from four distinct values:

```
http://www.w3.org/1999/xhtml
http://www.w3.org/TR/html4/loose.dtd
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
https://www.w3.org/1999/xhtml
```

**Cause.** `_scan_urls()` scans the body text for anything URL-shaped, and an
XHTML doctype or namespace declaration is URL-shaped. The spammer did not put
those there; the HTML boilerplate did.

**Approach.** A hostname skip-list, checked after the URL is extracted. Four
hosts cover everything the corpus produced: `www.w3.org`, `schemas.microsoft.com`,
`purl.org`, `ns.adobe.com`. Keep it a list of HOSTS, not a regex over the URL:
a hostile URL can put any string in a path or query, so matching on anything
but the host lets an attacker suppress their own indicator.

**Constraints.** Not a leak and not a correctness defect: it is noise in a
report a human reads, and the cost is filing `w3.org` as phishing
infrastructure with an abuse desk. Weigh against the argument for leaving it
alone, which is real: a parser that reports exactly what the message contained
is easier to defend than one that decides what to omit, and every entry on a
skip-list is a thing an attacker knows will not be reported. If it is built,
the skipped URLs should still be visible somewhere during review rather than
silently dropped, on the same reasoning that makes `suspect_path_segments`
flag rather than redact.

## 2. An IDN indicator resolves to no contact

**Observed.** `contacts.is_queryable()` refuses any non-ASCII value, so a
phishing domain in an internationalised name is recorded as `unusable` with the
reason "not ASCII, and we do not guess at an IDN encoding", and no registry is
asked. The `ponytail:` comment on that function names this as the ceiling it
accepted.

**Cause.** Deliberate, not a defect. Converting a name to punycode means
choosing an encoding for attacker-supplied text, and a wrong choice sends a
different name than the one in the message to a registry, which is a disclosure
made about the wrong party. Refusing keeps the value in front of the user
instead.

**Approach.** `value.encode("idna")` is the obvious move and is not enough on
its own: it normalises, so the name queried may differ from the name written in
the message, and the manifest must record BOTH, the way `query_domain` already
records `queried` separately when the label walk shortens a host. The refusal
reason already distinguishes non-ASCII from illegal, so the user-facing half
exists.

**Constraints.** Not a leak: the failure direction is asking nobody, which is
safe. Weigh against a real argument for leaving it: a homograph name is exactly
where the attacker wants the tool to normalise on their behalf, and a
consultant chasing one indicator by hand is a smaller cost than a query made
about a name the user never saw. Wait for a real IDN indicator in a sweep before
building it.

## 3. Expose kept cases so qtmaildir can tag spam

**Source.** The author's idea note, not a defect found in the code. Unlike
items 1 and 2 the cause here has NOT been verified against the code, because
there is nothing built yet to verify: this is a feature request, and it is
recorded unsized on purpose.

**Observed.** Case directories are permanent by design, so over time they
become a local corpus of messages the user has already judged to be phishing.
Nothing reads them back. The idea is that qtmaildir could ask this tool
whether an incoming message resembles one, and tag it as spam when it does.

**Approach.** Undecided, and the shape matters more than the code. The
umbrella design already fixes the coupling between the two repositories: the
manifest format and a command name in qtmaildir's config, with no submodule.
A read-only subcommand answering a question about one message fits that
contract; a daemon, a socket or a shared database does not, and the umbrella
design rules out a database of this tool's own.

**Constraints, and the real tension.** Deciding a message is spam by
resemblance is a classifier, and this tool has so far been deliberately
mechanical: it reports what a message declared, and refuses rather than
guesses when the trust boundary is unset. A resemblance score is the first
thing here that would be an opinion rather than an observation, and a wrong
one either hides real mail or teaches the user to distrust the tag.

There is also a quieter question about what a match is allowed to be based on.
The obvious signals are the ones already in a manifest, a sending IP, a
domain, a URL shape, an attachment hash. Those are safe. Matching on the
message body would mean holding attacker-supplied text against new mail, and
`source.eml` is unredacted, so anything built here must not become a route by
which a stored recipient identifier reaches a comparison that is later
reported or logged. Property 1 governs what may be published, and a tag is not
a report, but the path from one to the other is short.

**Before building.** Ask the author what "fits certain requisites" means to
him concretely, since that phrase is doing all the work in the note, and
whether he wants a judgement or only the facts, for instance a subcommand that
answers "this IP appears in three kept cases" and leaves the tagging decision
to qtmaildir. The second is much more in keeping with the rest of the tool.

## 4. `Report-Type: phishing` is unverified against x-arf

**Observed.** `report.feedback_fields()` emits `Report-Type: phishing` in the
machine-readable part. Every other field there was verified against RFC 5965
itself; this one was not, because no primary source for x-arf's own field
semantics could be reached while building it. The abusix README documents only
the v3 to v4 deprecation and does not define the field.

**Cause.** Not a defect found in the code. The value follows the worked
example in `docs/specs/2026-09-09-report.md`, so it is internally consistent,
and the hybrid envelope means a strict RFC 5965 parser ignores the field
either way (the RFC requires implementors ignore fields they do not support).
The exposure is limited to x-arf tooling reading a field name or value that
does not exist in the version it implements.

**Approach.** Find a primary source for x-arf v4 field names, confirm or
correct the value, and record what it was checked against. If x-arf turns out
to name the field differently, the fix is one string and one test.

**Constraints.** Low urgency: nothing here is a leak, and the failure mode is
a field an x-arf parser skips rather than acts on wrongly. Worth doing before
the first real report is filed, so a desk running x-arf tooling gets what it
expects.