fix: check an invitation's sender also when it claims one of the user's own addresses

This commit is contained in:
Linus Rath
2026-11-03 00:43:16 +01:00
parent 6ef1397a53
commit 22a783efe0
2 changed files with 8 additions and 5 deletions
@@ -79,6 +79,10 @@ describe('api.calendar.respond', () => {
vi.mocked(client.getEmail).mockResolvedValueOnce({ ...invitation, from: [{ email: 'bob@example.org', name: 'Bob' }] } as never);
parsed.participants = { p1: { ...alice, email: 'bob@example.org' } } as never;
await expect(call(ASSISTANT, 'calendar.respond', 'inv', 'accepted')).rejects.toThrow(/organises/);
// From forged as the user's own address, for someone else's event, not verified.
vi.mocked(client.getEmail).mockResolvedValueOnce({ ...invitation, from: [{ email: 'bob@example.org', name: 'Bob' }] } as never);
parsed.participants = { p1: alice, p2: bob } as never;
await expect(call(ASSISTANT, 'calendar.respond', 'inv', 'accepted')).rejects.toThrow(/may not come from/);
// Someone else's event whose sender doesn't match its organiser.
vi.mocked(client.getEmail).mockResolvedValueOnce({ ...invitation, from: [{ email: 'mallory@evil.example', name: 'Alice' }] } as never);
parsed.participants = { p1: alice, p2: bob } as never;
+4 -5
View File
@@ -76,11 +76,10 @@ export async function doCalendarRespond(emailIdArg: unknown, statusArg: unknown)
if (itip !== 'request') throw new Error(`${method}: the message is not an invitation that asks for an answer`);
const emails = identityEmails();
const sender = email.from?.[0]?.email?.toLowerCase();
if (!(sender && emails.includes(sender))) {
const trust = getInvitationTrustAssessment(parsedEvent, email, itip, emails);
if (trust?.level === 'warning') throw new Error(`${method}: the invitation may not come from who it names; answer it in the reader`);
}
// Always checked, also when From is one of the user's own addresses (it
// can be forged): only a sender the server verified passes a mismatch.
const trust = getInvitationTrustAssessment(parsedEvent, email, itip, emails);
if (trust?.level === 'warning') throw new Error(`${method}: the invitation may not come from who it names; answer it in the reader`);
const existing = await ownCopy(client, parsedEvent.uid);
const { participant: myParticipant, isOrganizer } = attendeeOf(