Ask for the account password before minting an app password, and keep sessions the proxy checks from writing the account's own registry objects, so a session left open on someone else's machine cannot take a credential away from it. The password is compared with what the session holds; Stalwart is asked only when 2FA moved the session onto an app password. Serve attachments and proxied images with no-store on a device that is not the person's own. Give files from a winmail.dat only the types the server would show inline. Strip direction controls from sender and attachment names and from saved filenames. On signing out, send what is inside its undo window, then close every composer, so the next person to sign in does not find the last one's draft. Group sessions by the account Stalwart names and its server, so "sign out other sessions" also reaches a session opened as a bare or differently cased username.
69 lines
3.1 KiB
TypeScript
69 lines
3.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { displayName, formatAddress, initials, isValidEmail, parseAddressList, parseMailto } from "../address";
|
|
|
|
describe("address parsing", () => {
|
|
it("parses mixed lists", () => {
|
|
const list = parseAddressList('Ann Example <[email protected]>, [email protected]; "Smith, John" <[email protected]>');
|
|
expect(list).toEqual([
|
|
{ name: "Ann Example", email: "[email protected]" },
|
|
{ name: null, email: "[email protected]" },
|
|
{ name: "Smith, John", email: "[email protected]" },
|
|
]);
|
|
});
|
|
it("formats with quoting when needed", () => {
|
|
expect(formatAddress({ name: "Smith, John", email: "[email protected]" })).toBe('"Smith, John" <[email protected]>');
|
|
expect(formatAddress({ name: null, email: "[email protected]" })).toBe("[email protected]");
|
|
});
|
|
it("validates and initials", () => {
|
|
expect(isValidEmail("[email protected]")).toBe(true);
|
|
expect(isValidEmail("nope")).toBe(false);
|
|
expect(initials({ name: "Grace Hopper", email: "" })).toBe("GH");
|
|
expect(initials({ name: null, email: "[email protected]" })).toBe("LK");
|
|
});
|
|
});
|
|
|
|
describe("mailto URLs", () => {
|
|
it("takes recipients from the path, the to header, or both", () => {
|
|
expect(parseMailto("mailto:[email protected]")).toMatchObject({ to: [{ name: null, email: "[email protected]" }] });
|
|
expect(parseMailto("mailto:[email protected]").to).toEqual([{ name: null, email: "[email protected]" }]);
|
|
expect(parseMailto("mailto:[email protected][email protected]").to).toHaveLength(2);
|
|
expect(parseMailto("mailto:[email protected],[email protected]").to).toHaveLength(2);
|
|
});
|
|
|
|
it("reads cc, bcc, subject and body", () => {
|
|
const m = parseMailto("mailto:[email protected][email protected]&[email protected]&subject=Hello%20there&body=Line%20one");
|
|
expect(m.cc).toEqual([{ name: null, email: "[email protected]" }]);
|
|
expect(m.bcc).toEqual([{ name: null, email: "[email protected]" }]);
|
|
expect(m.subject).toBe("Hello there");
|
|
expect(m.body).toBe("Line one");
|
|
});
|
|
|
|
it("is case-insensitive about headers and decodes plus as space", () => {
|
|
const m = parseMailto("MAILTO:[email protected]?SUBJECT=Re:+lunch&Body=see+you");
|
|
expect(m.subject).toBe("Re: lunch");
|
|
expect(m.body).toBe("see you");
|
|
});
|
|
|
|
it("keeps display names and survives malformed escapes", () => {
|
|
expect(parseMailto('mailto:%22Smith%2C%20John%22%20%[email protected]%3E').to).toEqual([{ name: "Smith, John", email: "[email protected]" }]);
|
|
expect(parseMailto("mailto:[email protected]?subject=100%").subject).toBe("100%");
|
|
});
|
|
|
|
it("ignores headers it does not understand", () => {
|
|
const m = parseMailto("mailto:[email protected]?x-random=1&subject=Hi");
|
|
expect(m.subject).toBe("Hi");
|
|
expect(m.to).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe("names that reorder themselves", () => {
|
|
const spoof = { name: "[email protected]\u202E", email: "[email protected]" };
|
|
it("lose their direction controls when displayed", () => {
|
|
expect(displayName({ name: "\u202Egnp.exe\u202C Ann", email: "[email protected]" })).toBe("gnp.exe Ann");
|
|
expect(formatAddress(spoof)).toBe("[email protected] <[email protected]>");
|
|
});
|
|
it("fall back to the address when nothing else is left", () => {
|
|
expect(displayName({ name: "\u200F\u202E", email: "[email protected]" })).toBe("[email protected]");
|
|
});
|
|
});
|