mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-02 00:06:23 +00:00
fix: keep a signed or encrypted reply-to name with a comma as one address
This commit is contained in:
@@ -102,6 +102,18 @@ describe('prepareSecureSend', () => {
|
|||||||
expect(new TextDecoder().decode(uploaded!)).not.toMatch(/bcc@example.org/i);
|
expect(new TextDecoder().decode(uploaded!)).not.toMatch(/bcc@example.org/i);
|
||||||
}, 60_000);
|
}, 60_000);
|
||||||
|
|
||||||
|
it('quotes a Reply-To name with a comma so it stays one address', async () => {
|
||||||
|
await importPgpKey(me.privateKey, me.passphrase);
|
||||||
|
let uploaded: Uint8Array | null = null;
|
||||||
|
const msg = { ...outgoing(), replyTo: [{ name: 'Support, ACME', email: 'help@acme.com' }] };
|
||||||
|
await prepareSecureSend(msg, { mode: 'pgp', sign: true, encrypt: false }, {
|
||||||
|
fetchBlob: async () => new Uint8Array([1]),
|
||||||
|
upload: async (bytes) => { uploaded = bytes; return 'x'; },
|
||||||
|
});
|
||||||
|
const text = new TextDecoder().decode(uploaded!);
|
||||||
|
expect(text).toMatch(/^Reply-To: "Support, ACME" <help@acme.com>$/m);
|
||||||
|
}, 60_000);
|
||||||
|
|
||||||
it('stops when a recipient has no key, or when the signing key is locked', async () => {
|
it('stops when a recipient has no key, or when the signing key is locked', async () => {
|
||||||
await importPgpKey(me.privateKey, me.passphrase);
|
await importPgpKey(me.privateKey, me.passphrase);
|
||||||
const deps = { fetchBlob: async () => new Uint8Array(), upload: async () => 'x' };
|
const deps = { fetchBlob: async () => new Uint8Array(), upload: async () => 'x' };
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { OutgoingEmail } from '../../api/email';
|
|||||||
import type { ContactCard } from '../../api/types';
|
import type { ContactCard } from '../../api/types';
|
||||||
import { useCryptoKeysStore, keysForEmail, isRecordCurrent, type CryptoKeyRecord, type PgpKeyRecord, type SmimeCertRecord } from '../../stores/crypto-keys-store';
|
import { useCryptoKeysStore, keysForEmail, isRecordCurrent, type CryptoKeyRecord, type PgpKeyRecord, type SmimeCertRecord } from '../../stores/crypto-keys-store';
|
||||||
import { messagePriorityHeaders } from '../message-priority';
|
import { messagePriorityHeaders } from '../message-priority';
|
||||||
import { base64Decode, formatHeader, encodeHeaderWords, type MimeAttachment, type MimeMessageInput } from './mime';
|
import { base64Decode, formatHeader, formatAddress, type MimeAttachment, type MimeMessageInput } from './mime';
|
||||||
import { buildSecureMessage, type SecureMode } from './secure-send';
|
import { buildSecureMessage, type SecureMode } from './secure-send';
|
||||||
import { ownKeyFor, unlockedPgpKey, unlockedSmimeKey } from './keyring';
|
import { ownKeyFor, unlockedPgpKey, unlockedSmimeKey } from './keyring';
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ export function secureReadiness(choice: SecureChoice, fromEmail: string, recipie
|
|||||||
function extraHeaders(outgoing: OutgoingEmail): string[] {
|
function extraHeaders(outgoing: OutgoingEmail): string[] {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
if (outgoing.replyTo?.length) {
|
if (outgoing.replyTo?.length) {
|
||||||
lines.push(formatHeader('Reply-To', outgoing.replyTo.map((a) => (a.name ? `${encodeHeaderWords(a.name)} <${a.email}>` : a.email)).join(', ')));
|
lines.push(formatHeader('Reply-To', outgoing.replyTo.map(formatAddress).join(', ')));
|
||||||
}
|
}
|
||||||
for (const [prop, value] of Object.entries(messagePriorityHeaders(outgoing.priority))) {
|
for (const [prop, value] of Object.entries(messagePriorityHeaders(outgoing.priority))) {
|
||||||
const name = /^header:([^:]+):/.exec(prop)?.[1];
|
const name = /^header:([^:]+):/.exec(prop)?.[1];
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ function unfold(value: string | undefined): string {
|
|||||||
return String(value ?? '').replace(/[\r\n\t]+/g, ' ').replace(/ {2,}/g, ' ').trim();
|
return String(value ?? '').replace(/[\r\n\t]+/g, ' ').replace(/ {2,}/g, ' ').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatAddress(addr: MimeAddress): string {
|
/** One address for a header: the name quoted (or RFC 2047-encoded) so commas in it stay inside. */
|
||||||
|
export function formatAddress(addr: MimeAddress): string {
|
||||||
if (addr.name) {
|
if (addr.name) {
|
||||||
const name = unfold(addr.name);
|
const name = unfold(addr.name);
|
||||||
// Raw 8-bit in a header is rejected by some MTAs; encode it.
|
// Raw 8-bit in a header is rejected by some MTAs; encode it.
|
||||||
|
|||||||
Reference in New Issue
Block a user