@rndmjoker/proton-mcp 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +27 -6
  2. package/dist/mail/compose.d.ts +29 -1
  3. package/dist/mail/compose.js +41 -4
  4. package/dist/mail/compose.js.map +1 -1
  5. package/dist/mail/markup.d.ts +58 -2
  6. package/dist/mail/markup.js +129 -35
  7. package/dist/mail/markup.js.map +1 -1
  8. package/dist/server.js +6 -0
  9. package/dist/server.js.map +1 -1
  10. package/dist/tools/confirm.d.ts +33 -3
  11. package/dist/tools/confirm.js +90 -47
  12. package/dist/tools/confirm.js.map +1 -1
  13. package/dist/tools/drafts.js +32 -0
  14. package/dist/tools/drafts.js.map +1 -1
  15. package/dist/tools/failures.d.ts +12 -0
  16. package/dist/tools/failures.js +24 -0
  17. package/dist/tools/failures.js.map +1 -1
  18. package/dist/tools/guidance.js +50 -10
  19. package/dist/tools/guidance.js.map +1 -1
  20. package/dist/tools/interface.js +7 -3
  21. package/dist/tools/interface.js.map +1 -1
  22. package/dist/tools/pending-view.d.ts +46 -0
  23. package/dist/tools/pending-view.js +63 -0
  24. package/dist/tools/pending-view.js.map +1 -0
  25. package/dist/tools/preview.d.ts +65 -0
  26. package/dist/tools/preview.js +103 -0
  27. package/dist/tools/preview.js.map +1 -0
  28. package/dist/tools/send.js +38 -2
  29. package/dist/tools/send.js.map +1 -1
  30. package/dist/tools/updates.d.ts +53 -0
  31. package/dist/tools/updates.js +134 -0
  32. package/dist/tools/updates.js.map +1 -0
  33. package/dist/web/layout.js +34 -0
  34. package/dist/web/layout.js.map +1 -1
  35. package/dist/web/pending.d.ts +72 -0
  36. package/dist/web/pending.js +196 -0
  37. package/dist/web/pending.js.map +1 -0
  38. package/dist/web/security.js +10 -0
  39. package/dist/web/security.js.map +1 -1
  40. package/dist/web/server.d.ts +9 -0
  41. package/dist/web/server.js +39 -0
  42. package/dist/web/server.js.map +1 -1
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -35,9 +35,12 @@ This server therefore runs on your machine as well, started by the AI client as
35
35
  | `send_reply` | Replies and sends, keeping the conversation intact |
36
36
  | `send_forward` | Forwards and sends, attachments and all |
37
37
  | `send_draft` | Sends a draft that is already written |
38
+ | `check_for_updates` | Asks npm whether a newer version exists. The only thing here that contacts anything but your own machine, and only when called |
38
39
 
39
40
  Every tool that composes a message takes either `text` or `html`, never both: Proton drops the plain text half of a message that carries markup, so the half you confirmed would be the half that never arrived.
40
41
 
42
+ They also take a `markupLevel`. At **`standard`**, the default, a message may use colour, background, font, alignment, spacing, borders and size, plus links, images, tables and lists; anything else is refused with a reason rather than quietly removed. At **`extended`** every CSS property is permitted, including ones that can put content out of sight. Nothing is refused there for being a style: what was used is reported instead, the confirmation carries a warning telling you to open the preview, and the preview names every property, its value and the element it sat on. A proper button needs `extended`, because a link cannot fill its own padding without `display: inline-block`.
43
+
41
44
  The server also tells an assistant how it works, so the rules are not something a model has to infer from failures. A short text travels with every connection naming the four things that go wrong quietly, and two longer guides can be read on demand: `proton-mcp://guide/writing` for composing messages and `proton-mcp://guide/bridge` for the measured behaviour of the Bridge itself.
42
45
 
43
46
  Underneath: a held IMAP connection that recovers from a Bridge restart, stable identifiers based on the Message-ID, HTML to text conversion, filtering of the public key Proton attaches to every sent message, and a character budget so a single message cannot exhaust a context window.
@@ -52,6 +55,8 @@ There is also a **local web interface**, on `127.0.0.1` only, running for as lon
52
55
  | Credentials | Where the Bridge password is kept, and signing out, which clears it from every store at once |
53
56
  | Activity | What is running and for how long, plus the last 50 finished calls. Tool names and timings only, never arguments |
54
57
 
58
+ One more appears only while a message is waiting for you to confirm it, linked from that question: the message rendered as the recipient will see it, every address in full, every alt text, every attachment. It is the one place this interface shows mail, and it carries no confirm button by design.
59
+
55
60
  Before signing in there are two more: the sign-in form, where the address and Bridge password go into the browser rather than into a client configuration or a conversation, with four places to keep the password and the cost of each stated; and the unlock page, which after a restart asks for the master password alone, since the Bridge password is already on disk.
56
61
 
57
62
  **None of it uses JavaScript.** The Content-Security-Policy forbids it, so navigation is links and anything interactive is a form control. It follows the system light or dark setting, which needs no script either.
@@ -100,6 +105,16 @@ Fetch https://raw.githubusercontent.com/RndmJoker/proton-mcp/main/prompt.md and
100
105
 
101
106
  It checks the prerequisites, registers the server with your client and tells you what to do next. **It never asks for your Bridge password** and cannot: that goes into the local web interface, in your browser, and nowhere else.
102
107
 
108
+ You can also just ask your assistant whether there is an update: the server has a `check_for_updates` tool that asks npm and reports what to do. It is the one thing in here that contacts anything other than your own machine, it runs only when called, it sends nothing about you, and it installs nothing.
109
+
110
+ Or hand over the guide, the same way as for the installation:
111
+
112
+ ```
113
+ Fetch https://raw.githubusercontent.com/RndmJoker/proton-mcp/main/update.md and follow it
114
+ ```
115
+
116
+ It works out how you installed it, since the three ways look identical from the outside and are updated differently, and it reminds you that the client has to be restarted. Nothing has to be set up again: credentials, the pinned certificate and the ports all survive.
117
+
103
118
  ### From npm
104
119
 
105
120
  ```bash
@@ -258,14 +273,17 @@ Every tool that changes something is bounded on purpose:
258
273
 
259
274
  **A draft is the safe default.** The assistant prepares, you decide.
260
275
 
261
- **Messages can be plain text or formatted.** A formatted one may use headings, paragraphs, emphasis, lists, tables, links and images, with colour, font, alignment, spacing and borders. Anything outside that set is refused with a reason rather than quietly removed, because a message that was silently altered is no longer the message anyone agreed to.
276
+ **Messages can be plain text or formatted.** A formatted one may use headings, paragraphs, emphasis, lists, tables, links and images, with colour, font, alignment, spacing and borders. **Images work the ordinary way**: an `<img>` at an `https` address, at any size, inside a link, animated if you like. Only a `data:` address is refused, because it would carry a whole file inside the markup where nothing lists it.
277
+
278
+ Anything outside the permitted set is refused with a reason rather than quietly removed, because a message that was silently altered is no longer the message anyone agreed to. **The refusal names the way forward**, which is usually `markupLevel: "extended"`.
262
279
 
263
- Formatting changes what a confirmation has to show, and that is the interesting part. In plain text a link's visible text and its target are the same string, so showing you the body shows you everything. In markup they are two strings, which is the shape of every phishing mail ever written. So the confirmation for a formatted message adds two blocks that are never shortened:
280
+ Formatting changes what a confirmation has to show, and that is the interesting part. In plain text a link's visible text and its target are the same string, so showing you the body shows you everything. In markup they are two strings, which is the shape of every phishing mail ever written. Neither fits in a dialog, so both live on the preview page, never shortened:
264
281
 
265
282
  - **Every address in the message**, links and images alike, each with the text it is shown as.
266
- - **Text a recipient can read that the body preview does not show.** Measured: an image's alt text never appears in the converted body, and mail clients block remote images by default, so the alt text is frequently what the recipient actually reads. That was a way past the confirmation, and it is closed.
283
+ - **Text a recipient can read that the body does not show.** Measured: an image's alt text never appears in the converted body, and mail clients block remote images by default, so the alt text is frequently what the recipient actually reads. That was a way past the confirmation, and it is closed.
284
+ - **Every property beyond ordinary formatting**, when `extended` was used, with the ones that really put something out of sight marked as such.
267
285
 
268
- The set of permitted markup leaves out `style`, `script`, `noscript`, `textarea` and `head` by not listing them. Those are the elements whose content the preview and a mail client disagree about, and a list of what is allowed refuses them without anyone having had to think of them first. Attachments from files on this machine are deliberately not supported: a path named by a model, read by the server and carried out by the next confirmation is not a feature, it is a way out for anything on your disk. Forwarding loses nothing all the same, because the original message travels along whole.
286
+ What no level permits is `style` blocks, `script`, event handlers and `data:` addresses. The line runs between describing a document and running inside it or reaching out of it: a `<style>` in the quote of a reply restyles the answer written above it, and 66 percent of real formatted mail carries one. Attachments from files on this machine are deliberately not supported either: a path named by a model, read by the server and carried out by the next confirmation is not a feature, it is a way out for anything on your disk. Forwarding loses nothing all the same, because the original message travels along whole, embedded images included.
269
287
 
270
288
  One thing worth knowing about drafts: Proton rewrites the thread headers of anything it stores. A reply is built with `In-Reply-To` and `References`, and what comes back has neither, only Proton's own internal thread id. Threading a stored draft is therefore Proton's business rather than this server's.
271
289
 
@@ -275,7 +293,10 @@ Writes need a moment to settle. A move takes roughly fifteen seconds to reconcil
275
293
 
276
294
  Mail is text written by strangers, handed to a model that can call tools. A message can politely ask to be forwarded somewhere, and other projects answer that with a line in a system prompt. **A system prompt is not a security boundary.** This one lives in the server:
277
295
 
278
- - **Nothing is sent without a person saying yes.** The first call never sends. It returns a question through your client showing the final recipients separated into To, Cc and Bcc, the subject, and the first lines. Only the second call, carrying your answer, sends.
296
+ - **Nothing is sent without a person saying yes.** The first call never sends. It returns a question through your client naming the sender, every recipient separated into To, Cc and Bcc, and the subject. Only the second call, carrying your answer, sends.
297
+ - **The question is short, and the message is somewhere with room.** It carries no part of the message itself; it links to a page in the local interface where you can read it as the recipient will see it, with every address in full. That split exists because the question used to grow with the message: 74 lines for a newsletter-shaped one, at which point the confirm button sat below the bottom of the dialog and nothing could be sent at all.
298
+ - **Recipients are never summarised**, however many there are. Seeing three of eight would mean agreeing to a send to five strangers.
299
+ - **The answer goes through your client, never through the browser.** That page has no confirm button and will not get one: the address of the interface reaches your assistant, so a control there could be operated by the thing it is supervising.
279
300
  - **There is no setting that turns the question off.** Not an environment variable, not an argument, not a mode.
280
301
  - **A client that cannot ask cannot send.** If your client does not support form elicitation, sending is refused and you are pointed at drafts instead. That is deliberately different from the sign-in prompt, which falls back to text: the worst outcome there is an inconvenience, and here it is a message that cannot be recalled. What counts as "can ask" is the capability your client declares, read from the request when the protocol revision carries it there and from the handshake otherwise.
281
302
  - **Your yes covers one specific message.** A fingerprint of the sender, every recipient, the subject and the body is sealed with an HMAC whose key exists only in the running process, and it travels with the question. On the way back the fingerprint is recomputed from what is being asked for now. If a single address was added in between, nothing is sent. A confirmation is also bound to the tool that asked, so one cannot be reused for another.
@@ -310,7 +331,7 @@ It binds to `127.0.0.1` and there is no option to change that. It is not remote
310
331
  - **An Origin check and a per-action CSRF token** on everything that changes something, so a token for one action cannot be replayed against another.
311
332
  - **A strict Content-Security-Policy.** Nothing may be loaded from anywhere and nothing can reach the network. The interface also serves no files from disk at all, which removes path traversal as a category rather than guarding against it.
312
333
 
313
- It shows no message content, by design. It is for configuration and state, not a mail client.
334
+ It shows no message content, with exactly one exception: a message waiting for you to confirm it. That page renders it as the recipient will see it, and lists every address, alt text and attachment. The message is held in memory only, dropped as soon as you answer either way, and gone after fifteen minutes. Somebody else's markup, which a reply carries as its quote, is rendered in a frame with no permissions at all, and images from the internet are not loaded: a tracking pixel must not tell its author that you looked.
314
335
 
315
336
  ### What you actually expose
316
337
 
@@ -11,7 +11,7 @@
11
11
  * carries it outside. Which paths may be reached is its own decision, not a
12
12
  * side effect of being able to write a draft.
13
13
  */
14
- import { type MarkupUrl } from './markup.js';
14
+ import { type MarkupUrl, type MarkupLevel, type StyleNote } from './markup.js';
15
15
  /** An address, optionally with a display name. */
16
16
  export interface Recipient {
17
17
  name?: string;
@@ -30,6 +30,15 @@ export interface Draft {
30
30
  references?: string[];
31
31
  /** Our own Message-ID. Minted here so there is a handle straight away. */
32
32
  messageId: string;
33
+ /**
34
+ * How much markup this message may carry. Absent means `standard`.
35
+ *
36
+ * Part of the draft rather than a parameter beside it, because it decides
37
+ * what the message is allowed to contain and therefore belongs to the same
38
+ * thing the digest covers. A message agreed to at one level must not be sent
39
+ * at another.
40
+ */
41
+ markupLevel?: MarkupLevel;
33
42
  /** A whole message carried along, used when forwarding. */
34
43
  attachedMessage?: {
35
44
  filename: string;
@@ -211,6 +220,16 @@ export declare const UTF8_NOTE: string;
211
220
  * alternative is an invitation to try anyway.
212
221
  */
213
222
  export declare const MARKUP_NOTE: string;
223
+ /**
224
+ * What a caller is told about the two levels.
225
+ *
226
+ * Written to be read by something that will otherwise reach for the wider one
227
+ * out of convenience. The cost is stated in terms of what the user sees,
228
+ * because that is the part a model can reason about: at `extended` the person
229
+ * being asked gets a warning telling them to go and look, every time, whether
230
+ * or not anything was actually hidden.
231
+ */
232
+ export declare const MARKUP_LEVEL_NOTE: string;
214
233
  export declare const PLAIN_TEXT_NOTE: string;
215
234
  /**
216
235
  * The message as a person will read it.
@@ -262,3 +281,12 @@ export declare function describeUrls(draft: Draft): MarkupUrl[];
262
281
  export declare function hiddenTextOf(draft: Draft): string[];
263
282
  /** A fingerprint of a file, so a confirmation can be bound to its contents. */
264
283
  export declare function fingerprintPart(content: Buffer): string;
284
+ /**
285
+ * The properties this message uses that are worth mentioning before it is sent.
286
+ *
287
+ * Empty at markup level `standard`, because nothing outside the ordinary set
288
+ * gets that far. At `extended` it is what the confirmation warns about and the
289
+ * preview lists, and it is the reason the level is worth having: the answer to
290
+ * "what did it use" is a list rather than a promise.
291
+ */
292
+ export declare function styleNotesOf(draft: Draft): StyleNote[];
@@ -14,7 +14,7 @@
14
14
  import MailComposer from 'nodemailer/lib/mail-composer/index.js';
15
15
  import { createHash, randomUUID } from 'node:crypto';
16
16
  import { BridgeError } from '../bridge/errors.js';
17
- import { assertSendableMarkup, readMarkup } from './markup.js';
17
+ import { assertSendableMarkup, readMarkup, } from './markup.js';
18
18
  import { prepareQuotedMarkup } from './quote.js';
19
19
  import { htmlToText } from '../mime/parse.js';
20
20
  /**
@@ -99,7 +99,7 @@ export async function buildMessage(draft, options = {}) {
99
99
  else {
100
100
  // Checked here rather than only at the tool, so that no path into this
101
101
  // function can produce a message whose markup was never read.
102
- const reading = assertSendableMarkup(draft.html);
102
+ const reading = assertSendableMarkup(draft.html, draft.markupLevel ?? 'standard');
103
103
  // Every content id the markup points at has to have arrived with it. A
104
104
  // reference to a part that is not there shows the recipient a broken image
105
105
  // where the sender saw a picture, and neither of them finds out why.
@@ -315,6 +315,26 @@ export const MARKUP_NOTE = 'A message may instead be written as markup, in the h
315
315
  'is refused rather than quietly removed, and the answer names what and why. Note that the ' +
316
316
  'confirmation shows every address in the message in full, including the ones behind links and ' +
317
317
  'images, and every alt text, because those are what a recipient reads.';
318
+ /**
319
+ * What a caller is told about the two levels.
320
+ *
321
+ * Written to be read by something that will otherwise reach for the wider one
322
+ * out of convenience. The cost is stated in terms of what the user sees,
323
+ * because that is the part a model can reason about: at `extended` the person
324
+ * being asked gets a warning telling them to go and look, every time, whether
325
+ * or not anything was actually hidden.
326
+ */
327
+ export const MARKUP_LEVEL_NOTE = 'The markupLevel field decides how much is permitted. "standard" is the default and covers ' +
328
+ 'ordinary formatted mail: colour, background, font, alignment, spacing, borders and size. ' +
329
+ '"extended" permits every CSS property, including ones that can put content out of sight such ' +
330
+ 'as display, visibility, opacity and position. **Prefer "standard".** At "extended" the ' +
331
+ 'confirmation carries a prominent warning telling the person to open the preview before ' +
332
+ 'answering, and the preview lists every property used, its value and the element it sat on. ' +
333
+ 'That warning appears whether or not anything was really hidden, so reaching for "extended" ' +
334
+ 'without needing it makes every send noisier and trains people to click past a warning that ' +
335
+ 'matters. Use it when the user asked for something that needs it, or when there is genuinely ' +
336
+ 'no other way, and say which of the two it was. One common case: a link cannot fill its own ' +
337
+ 'padding without display:inline-block, so a proper button needs "extended".';
318
338
  export const PLAIN_TEXT_NOTE = 'The body is plain text and markup is not interpreted. HTML written here is delivered as ' +
319
339
  'visible characters, so the recipient would read the tags rather than see formatting. ' +
320
340
  'Give a message its shape with line breaks, blank lines, indentation and plain lists, and ' +
@@ -382,14 +402,31 @@ export function describeAttachments(draft) {
382
402
  }
383
403
  /** The addresses in a message's markup, with the text they are shown as. */
384
404
  export function describeUrls(draft) {
385
- return draft.html === undefined ? [] : assertSendableMarkup(draft.html).urls;
405
+ return draft.html === undefined
406
+ ? []
407
+ : assertSendableMarkup(draft.html, draft.markupLevel ?? 'standard').urls;
386
408
  }
387
409
  /** Text a recipient can read that the readable body leaves out. */
388
410
  export function hiddenTextOf(draft) {
389
- return draft.html === undefined ? [] : assertSendableMarkup(draft.html).hiddenText;
411
+ return draft.html === undefined
412
+ ? []
413
+ : assertSendableMarkup(draft.html, draft.markupLevel ?? 'standard').hiddenText;
390
414
  }
391
415
  /** A fingerprint of a file, so a confirmation can be bound to its contents. */
392
416
  export function fingerprintPart(content) {
393
417
  return createHash('sha256').update(content).digest('hex').slice(0, 16);
394
418
  }
419
+ /**
420
+ * The properties this message uses that are worth mentioning before it is sent.
421
+ *
422
+ * Empty at markup level `standard`, because nothing outside the ordinary set
423
+ * gets that far. At `extended` it is what the confirmation warns about and the
424
+ * preview lists, and it is the reason the level is worth having: the answer to
425
+ * "what did it use" is a list rather than a promise.
426
+ */
427
+ export function styleNotesOf(draft) {
428
+ if (draft.html === undefined)
429
+ return [];
430
+ return assertSendableMarkup(draft.html, draft.markupLevel ?? 'standard').styleNotes;
431
+ }
395
432
  //# sourceMappingURL=compose.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/mail/compose.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/mail/compose.ts"],"names":[],"mappings":"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"}
@@ -51,6 +51,24 @@
51
51
  export declare const PERMITTED_TAGS: Set<string>;
52
52
  /** Style properties a message may set. */
53
53
  export declare const PERMITTED_STYLES: Set<string>;
54
+ /**
55
+ * Properties that can take content out of sight without taking it out of the
56
+ * message.
57
+ *
58
+ * These used to be refused outright. That was too blunt in both directions.
59
+ * `display: inline-block` is what makes a link fill its own padding, which is
60
+ * what makes a button clickable, and it hides nothing; `float` is ordinary
61
+ * layout that mail has used for twenty years. Refusing the property rather than
62
+ * the value cost every message a usable button and bought nothing, because a
63
+ * confirmation that reads markup without applying it showed the hidden text
64
+ * anyway.
65
+ *
66
+ * What changed is that the preview now renders. Text hidden by CSS really does
67
+ * disappear from it, so the answer is no longer to refuse the property but to
68
+ * say plainly that it was used, where, and how often, and to let the person
69
+ * decide.
70
+ */
71
+ export declare const RISKY_STYLES: Set<string>;
54
72
  /** One reason a message was refused. */
55
73
  export interface MarkupProblem {
56
74
  /** What was found, in the caller's own markup. */
@@ -58,6 +76,36 @@ export interface MarkupProblem {
58
76
  /** Why it cannot be sent, and what to do instead. */
59
77
  reason: string;
60
78
  }
79
+ /**
80
+ * How much markup a message may carry.
81
+ *
82
+ * Chosen per call by whoever is composing, with `standard` as the default that
83
+ * applies when nobody says otherwise. The point of naming them rather than
84
+ * listing properties is that a caller has to decide something it can explain,
85
+ * and that the answer travels into the confirmation where a person sees it.
86
+ */
87
+ export type MarkupLevel =
88
+ /**
89
+ * Colour, font, spacing, borders, size, alignment. Everything an ordinary
90
+ * formatted message uses, and nothing that can put text out of sight.
91
+ * Anything else is refused with a reason.
92
+ */
93
+ 'standard'
94
+ /**
95
+ * Every CSS property, including the ones that can hide content. Nothing is
96
+ * refused for being a style; what is used is reported instead, and the
97
+ * confirmation says so in as many words.
98
+ */
99
+ | 'extended';
100
+ /** One use of a property worth telling the person about before they agree. */
101
+ export interface StyleNote {
102
+ property: string;
103
+ value: string;
104
+ /** The element it sat on, so the preview can say where. */
105
+ element: string;
106
+ /** True when this value really does take something out of sight. */
107
+ hides: boolean;
108
+ }
61
109
  /** An address the message points at, which a person has to see before sending. */
62
110
  export interface MarkupUrl {
63
111
  kind: 'link' | 'image';
@@ -72,6 +120,14 @@ export interface MarkupReading {
72
120
  hiddenText: string[];
73
121
  /** Content ids an image refers to, which have to be provided as parts. */
74
122
  contentIds: string[];
123
+ /**
124
+ * Properties used that are worth telling the person about.
125
+ *
126
+ * Empty for a message that stays inside the ordinary set, which is almost
127
+ * every message. Anything in here means the confirmation says so and the
128
+ * preview lists it.
129
+ */
130
+ styleNotes: StyleNote[];
75
131
  }
76
132
  /**
77
133
  * Reads a message's markup: what is wrong with it, and what has to be shown.
@@ -79,7 +135,7 @@ export interface MarkupReading {
79
135
  * One pass, because the two questions are the same walk over the document and
80
136
  * answering them separately is how they drift apart.
81
137
  */
82
- export declare function readMarkup(html: string): MarkupReading;
138
+ export declare function readMarkup(html: string, level?: MarkupLevel): MarkupReading;
83
139
  /**
84
140
  * Refuses markup this server will not send.
85
141
  *
@@ -88,4 +144,4 @@ export declare function readMarkup(html: string): MarkupReading;
88
144
  * nobody would find out: not the caller, whose formatting quietly vanishes, and
89
145
  * not the person confirming, who has nothing to compare against.
90
146
  */
91
- export declare function assertSendableMarkup(html: string): MarkupReading;
147
+ export declare function assertSendableMarkup(html: string, level?: MarkupLevel): MarkupReading;
@@ -58,18 +58,34 @@ export const PERMITTED_TAGS = new Set([
58
58
  'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'caption',
59
59
  'a', 'img',
60
60
  ]);
61
- /** Attributes each element may carry, on top of the ones allowed everywhere. */
61
+ /**
62
+ * Attributes each element may carry, on top of the ones allowed everywhere.
63
+ *
64
+ * `role` and `aria-*` are here because leaving them out was a mistake rather
65
+ * than a policy. `role="presentation"` on a layout table is the standard way to
66
+ * write mail: without it a screen reader announces "table, three rows, two
67
+ * columns" for something that only centres a button. The attribute carries no
68
+ * behaviour and can hide nothing; refusing it made messages worse for the
69
+ * people who most need them to be well made.
70
+ *
71
+ * `class` and `id` are permitted for the opposite reason: they do nothing here,
72
+ * since a message may carry no `<style>` block, and some clients hang their own
73
+ * rules on them. There is nothing to protect by refusing them.
74
+ *
75
+ * `bgcolor` and `background` on table elements are old mail HTML that Outlook
76
+ * still needs, and both are colours rather than behaviour.
77
+ */
62
78
  const PERMITTED_ATTRIBUTES = {
63
- '*': ['style', 'title', 'dir', 'lang'],
79
+ '*': ['style', 'title', 'dir', 'lang', 'role', 'class', 'id'],
64
80
  a: ['href', 'target', 'rel'],
65
- img: ['src', 'alt', 'width', 'height'],
81
+ img: ['src', 'alt', 'width', 'height', 'border'],
66
82
  // height sits beside width rather than being an omission with a reason. A
67
83
  // table cell of a given height hides nothing: its content still renders, and
68
84
  // the properties that do hide are refused as styles further down.
69
- table: ['width', 'height', 'border', 'cellpadding', 'cellspacing', 'align'],
70
- td: ['colspan', 'rowspan', 'align', 'valign', 'width', 'height'],
71
- th: ['colspan', 'rowspan', 'align', 'valign', 'width', 'height'],
72
- tr: ['align', 'valign', 'height'],
85
+ table: ['width', 'height', 'border', 'cellpadding', 'cellspacing', 'align', 'bgcolor', 'background'],
86
+ td: ['colspan', 'rowspan', 'align', 'valign', 'width', 'height', 'bgcolor', 'background'],
87
+ th: ['colspan', 'rowspan', 'align', 'valign', 'width', 'height', 'bgcolor', 'background'],
88
+ tr: ['align', 'valign', 'height', 'bgcolor'],
73
89
  ol: ['start', 'type'],
74
90
  blockquote: ['cite'],
75
91
  };
@@ -85,52 +101,119 @@ export const PERMITTED_STYLES = new Set([
85
101
  'width', 'height', 'max-width', 'vertical-align', 'white-space', 'list-style',
86
102
  ]);
87
103
  /**
88
- * Properties that take a message out of sight without taking it out of the
89
- * message. Refused whatever else is written.
104
+ * Properties that can take content out of sight without taking it out of the
105
+ * message.
106
+ *
107
+ * These used to be refused outright. That was too blunt in both directions.
108
+ * `display: inline-block` is what makes a link fill its own padding, which is
109
+ * what makes a button clickable, and it hides nothing; `float` is ordinary
110
+ * layout that mail has used for twenty years. Refusing the property rather than
111
+ * the value cost every message a usable button and bought nothing, because a
112
+ * confirmation that reads markup without applying it showed the hidden text
113
+ * anyway.
114
+ *
115
+ * What changed is that the preview now renders. Text hidden by CSS really does
116
+ * disappear from it, so the answer is no longer to refuse the property but to
117
+ * say plainly that it was used, where, and how often, and to let the person
118
+ * decide.
90
119
  */
91
- const HIDING_STYLES = new Set([
120
+ export const RISKY_STYLES = new Set([
92
121
  'display', 'visibility', 'opacity', 'position', 'z-index', 'overflow',
93
- 'clip', 'clip-path', 'transform', 'text-indent', 'float', 'content',
122
+ 'clip', 'clip-path', 'transform', 'text-indent', 'content',
94
123
  'max-height', 'min-height', 'mix-blend-mode', 'filter',
95
124
  ]);
96
- /** Properties whose value must be a positive size. Zero is a way of hiding. */
125
+ /** Values of those properties that actually take something out of sight. */
126
+ function hidesContent(name, value) {
127
+ const v = value.trim().toLowerCase();
128
+ switch (name) {
129
+ case 'display':
130
+ return v === 'none';
131
+ case 'visibility':
132
+ return v === 'hidden' || v === 'collapse';
133
+ case 'opacity':
134
+ return Number.parseFloat(v) < 0.1;
135
+ case 'max-height':
136
+ case 'min-height':
137
+ return /^0(\D|$)/.test(v);
138
+ case 'text-indent':
139
+ return v.startsWith('-');
140
+ case 'position':
141
+ return v === 'absolute' || v === 'fixed';
142
+ case 'overflow':
143
+ return v === 'hidden';
144
+ case 'filter':
145
+ return /opacity\s*\(\s*0?(\.0*[0-9])?\s*\)/.test(v);
146
+ default:
147
+ // clip, clip-path, transform, z-index, content, mix-blend-mode: whether
148
+ // they hide anything depends on values this server has no business
149
+ // interpreting. Treated as worth mentioning rather than as proven.
150
+ return false;
151
+ }
152
+ }
153
+ /** Properties whose value being zero or less takes content out of sight. */
97
154
  const MUST_BE_POSITIVE = new Set(['font-size', 'line-height', 'width', 'height', 'max-width']);
98
155
  /** A scheme in an href or src. Anything else is refused. */
99
156
  const PERMITTED_SCHEMES = ['http:', 'https:', 'mailto:', 'cid:'];
100
- function styleProblems(tag, style) {
157
+ /**
158
+ * Reads one style attribute.
159
+ *
160
+ * At `standard` a property outside the ordinary set is refused with a reason,
161
+ * which is what it always did. At `extended` nothing is refused for being a
162
+ * style; every property outside that set is written down instead, together with
163
+ * whether its value really takes something out of sight.
164
+ *
165
+ * The distinction between "unusual" and "hides something" is the whole point.
166
+ * `display: inline-block` is a button; `display: none` is a disappearance. One
167
+ * property, two very different things to tell somebody.
168
+ */
169
+ function readStyle(tag, style, level) {
101
170
  const problems = [];
171
+ const notes = [];
102
172
  for (const declaration of style.split(';')) {
103
173
  const [rawName, ...rest] = declaration.split(':');
104
174
  const name = (rawName ?? '').trim().toLowerCase();
105
175
  const value = rest.join(':').trim();
106
176
  if (!name)
107
177
  continue;
108
- if (HIDING_STYLES.has(name)) {
109
- problems.push({
110
- found: `style "${name}" on <${tag}>`,
111
- reason: `"${name}" can put text out of sight while leaving it in the message, so a recipient ` +
112
- 'and the person who confirmed the send would not read the same thing. Style the message ' +
113
- 'with colour, font and spacing instead.',
178
+ const ordinary = PERMITTED_STYLES.has(name) && !(MUST_BE_POSITIVE.has(name) && /^0(\D|$)|^-/.test(value));
179
+ if (ordinary)
180
+ continue;
181
+ if (level === 'extended') {
182
+ notes.push({
183
+ property: name,
184
+ value,
185
+ element: tag,
186
+ hides: hidesContent(name, value) ||
187
+ (MUST_BE_POSITIVE.has(name) && /^0(\D|$)|^-/.test(value)),
114
188
  });
115
189
  continue;
116
190
  }
117
- if (!PERMITTED_STYLES.has(name)) {
191
+ if (RISKY_STYLES.has(name)) {
118
192
  problems.push({
119
193
  found: `style "${name}" on <${tag}>`,
120
- reason: `"${name}" is not among the properties this server sends. Permitted are colour, ` +
121
- 'background, font, alignment, spacing, borders and size.',
194
+ reason: `"${name}" can put text out of sight while leaving it in the message. It is available ` +
195
+ 'with markup level "extended", which shows the person confirming exactly what was used ' +
196
+ 'and where. Use it only when there is no other way.',
122
197
  });
123
198
  continue;
124
199
  }
125
- if (MUST_BE_POSITIVE.has(name) && /^0(\D|$)|^-/.test(value)) {
200
+ if (!PERMITTED_STYLES.has(name)) {
126
201
  problems.push({
127
- found: `style "${name}: ${value}" on <${tag}>`,
128
- reason: `A "${name}" of zero or less makes text unreadable without removing it, which is the ` +
129
- 'same as hiding it. Give it a real size or leave it out.',
202
+ found: `style "${name}" on <${tag}>`,
203
+ reason: `"${name}" is not among the properties sent at markup level "standard". Permitted are ` +
204
+ 'colour, background, font, alignment, spacing, borders and size. Everything else is ' +
205
+ 'available at level "extended", which tells the person confirming what was used.',
130
206
  });
207
+ continue;
131
208
  }
209
+ problems.push({
210
+ found: `style "${name}: ${value}" on <${tag}>`,
211
+ reason: `A "${name}" of zero or less makes text unreadable without removing it, which is the ` +
212
+ 'same as hiding it. Give it a real size, or use markup level "extended", which says ' +
213
+ 'plainly that it was done.',
214
+ });
132
215
  }
133
- return problems;
216
+ return { problems, notes };
134
217
  }
135
218
  function urlProblem(tag, attribute, value) {
136
219
  const trimmed = value.trim();
@@ -160,11 +243,12 @@ function urlProblem(tag, attribute, value) {
160
243
  * One pass, because the two questions are the same walk over the document and
161
244
  * answering them separately is how they drift apart.
162
245
  */
163
- export function readMarkup(html) {
246
+ export function readMarkup(html, level = 'standard') {
164
247
  const problems = [];
165
248
  const urls = [];
166
249
  const hiddenText = [];
167
250
  const contentIds = [];
251
+ const styleNotes = [];
168
252
  // A link's text arrives as separate events between its tags, so it is
169
253
  // collected while the link is open.
170
254
  let openLink;
@@ -182,18 +266,28 @@ export function readMarkup(html) {
182
266
  const allowed = [...(PERMITTED_ATTRIBUTES['*'] ?? []), ...(PERMITTED_ATTRIBUTES[tag] ?? [])];
183
267
  for (const [rawAttribute, value] of Object.entries(attributes)) {
184
268
  const attribute = rawAttribute.toLowerCase();
185
- if (!allowed.includes(attribute)) {
269
+ // aria-* as a family rather than one by one. They describe a document
270
+ // to assistive technology and carry no behaviour, and listing them
271
+ // individually would mean this file goes stale every time the
272
+ // specification grows one.
273
+ if (!allowed.includes(attribute) && !attribute.startsWith('aria-')) {
186
274
  problems.push({
187
275
  found: `${attribute}="${String(value).slice(0, 30)}" on <${tag}>`,
188
276
  reason: attribute.startsWith('on')
189
277
  ? 'An event handler has no place in a message. No mail client runs it, and ' +
190
278
  'what it contains would never be seen by anyone confirming the send.'
191
- : `<${tag}> may not carry "${attribute}" here.`,
279
+ : `<${tag}> may not carry "${attribute}" here. Permitted are style, title, ` +
280
+ 'dir, lang, role, class, id and aria-*, plus what each element needs of its ' +
281
+ 'own. Markup level "extended" widens which style properties may be used, not ' +
282
+ 'which attributes exist.',
192
283
  });
193
284
  continue;
194
285
  }
195
- if (attribute === 'style')
196
- problems.push(...styleProblems(tag, String(value)));
286
+ if (attribute === 'style') {
287
+ const read = readStyle(tag, String(value), level);
288
+ problems.push(...read.problems);
289
+ styleNotes.push(...read.notes);
290
+ }
197
291
  if (attribute === 'href' || attribute === 'src') {
198
292
  const problem = urlProblem(tag, attribute, String(value));
199
293
  if (problem)
@@ -239,7 +333,7 @@ export function readMarkup(html) {
239
333
  parser.end();
240
334
  if (openLink)
241
335
  urls.push({ kind: 'link', url: openLink.url, label: openLink.text.trim() });
242
- return { problems, urls, hiddenText, contentIds };
336
+ return { problems, urls, hiddenText, contentIds, styleNotes };
243
337
  }
244
338
  /**
245
339
  * Refuses markup this server will not send.
@@ -249,8 +343,8 @@ export function readMarkup(html) {
249
343
  * nobody would find out: not the caller, whose formatting quietly vanishes, and
250
344
  * not the person confirming, who has nothing to compare against.
251
345
  */
252
- export function assertSendableMarkup(html) {
253
- const reading = readMarkup(html);
346
+ export function assertSendableMarkup(html, level = 'standard') {
347
+ const reading = readMarkup(html, level);
254
348
  if (reading.problems.length === 0)
255
349
  return reading;
256
350
  const listed = reading.problems