Ask before opening a shared item in a message #391

Closed
opened 2026-09-16 19:23:44 +00:00 by jcoffey-dev · 0 comments
Owner

Summary

This is from the security review. The PWA's share target is POST <base>/share: the service worker stashes the form, and the app used to open it straight into a composer. That address takes a plain form POST, so any website could make the app open a composer pre-filled with its own text and files. Nothing would be sent without the reader pressing Send, but that's still something to ask about first.

Now:

  • A confirmation comes first. offerShare (views/ShareOffer.tsx) shows:
    • the shared title;
    • the text and link, cut to 160 characters (shareSummary);
    • up to five file names;
    • a note that nothing is sent until Send is chosen, and to discard the share if the reader didn't just make it.
  • Start a message opens the composer exactly as before, with nothing addressed. Discard drops the share. Either way, the stash was already deleted when the share was collected.
  • Dialog change: ConfirmHost puts a message that isn't a plain string in a <div> instead of a <p>, since the summary has its own blocks.

This adds one tap to a share made from the operating system's share sheet. The page can't tell such a share apart from one a website posted.

Related issues

None.

Translations

Three new strings, translated in all nine catalogs:

  • "Start a new message with what was shared?"
  • "Something was shared with ihasmail. Nothing is sent until you choose Send. If you didn't just share this, discard it."
  • "Start a message"

Existing terms reused:

  • the "Discard" button label (already in every catalog);
  • each catalog's own word for Send;
  • in Russian and Ukrainian, the body names the "Не сохранять" / "Не зберігати" button.

Conventions followed: formal register in de, fr and nl; full-width in ja and zh-Hans; a space before ? in fr.

Counts: catalog keys went from 1,625 to 1,628, and each language from 1,609 to 1,612 translated. The same 16 strings as on main still fall back to English. These translations are AI-written and not reviewed by native speakers, like the rest of the catalogs.

Testing

  • shareTarget.test.ts: shareSummary combines title, text and link, lists file names, and shortens long text.
  • New share-offer.test.tsx, rendering the real ConfirmHost: the dialog shows the title, text, link and file names before anything opens; Start a message opens the share; Discard opens nothing.
  • npm test -w web (1,353 tests), npm run typecheck, npm run build -w web and npm run i18n:check are clean.
  • Checked in Chrome against the built app served by npm run dev:mock. From a signed-in tab, I submitted a multipart form to /share with a title, text, link and a file, the way a website could:
    • The confirmation appeared, showing all four, and no composer opened behind it.
    • Discard closed it with no composer, and the stash was gone.
    • A second share, then Start a message, opened a composer with the subject, body and attachment, and nothing addressed.

Merged 2026-09-16 as coffey-labs/ihasmail@5fe89d6e15

Rebuilt from: git history, session transcript.

## Summary This is from the security review. The PWA's share target is `POST <base>/share`: the service worker stashes the form, and the app used to open it straight into a composer. That address takes a plain form POST, so **any website** could make the app open a composer pre-filled with its own text and files. Nothing would be sent without the reader pressing Send, but that's still something to ask about first. **Now:** - **A confirmation comes first.** `offerShare` (`views/ShareOffer.tsx`) shows: - the shared title; - the text and link, cut to 160 characters (`shareSummary`); - up to five file names; - a note that nothing is sent until Send is chosen, and to discard the share if the reader didn't just make it. - **Start a message** opens the composer exactly as before, with nothing addressed. **Discard** drops the share. Either way, the stash was already deleted when the share was collected. - **Dialog change:** `ConfirmHost` puts a message that isn't a plain string in a `<div>` instead of a `<p>`, since the summary has its own blocks. This adds one tap to a share made from the operating system's share sheet. The page can't tell such a share apart from one a website posted. ## Related issues None. ## Translations **Three new strings, translated in all nine catalogs:** - "Start a new message with what was shared?" - "Something was shared with ihasmail. Nothing is sent until you choose Send. If you didn't just share this, discard it." - "Start a message" **Existing terms reused:** - the "Discard" button label (already in every catalog); - each catalog's own word for Send; - in Russian and Ukrainian, the body names the "Не сохранять" / "Не зберігати" button. **Conventions followed:** formal register in de, fr and nl; full-width `?` in ja and zh-Hans; a space before `?` in fr. **Counts:** catalog keys went from 1,625 to 1,628, and each language from 1,609 to 1,612 translated. The same 16 strings as on `main` still fall back to English. These translations are AI-written and not reviewed by native speakers, like the rest of the catalogs. ## Testing - `shareTarget.test.ts`: `shareSummary` combines title, text and link, lists file names, and shortens long text. - New `share-offer.test.tsx`, rendering the real `ConfirmHost`: the dialog shows the title, text, link and file names before anything opens; **Start a message** opens the share; **Discard** opens nothing. - `npm test -w web` (1,353 tests), `npm run typecheck`, `npm run build -w web` and `npm run i18n:check` are clean. - **Checked in Chrome** against the built app served by `npm run dev:mock`. From a signed-in tab, I submitted a multipart form to `/share` with a title, text, link and a file, the way a website could: - The confirmation appeared, showing all four, and no composer opened behind it. - **Discard** closed it with no composer, and the stash was gone. - A second share, then **Start a message**, opened a composer with the subject, body and attachment, and nothing addressed. **Merged** 2026-09-16 as coffey-labs/ihasmail@5fe89d6e1559 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.