From bfd655efdc7e0e33b808fbdf128e7e0966732e4c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 13 Apr 2026 21:56:27 +0000 Subject: [PATCH] fix: sanitize untrusted email fields in remaining interactive receiving commands Apply safeTerminalText (already on main) to the interactive output paths that were still printing raw untrusted fields: - get.ts: sanitize from, to, subject, and text body snippet - attachment.ts: sanitize filename - utils.ts: sanitize from, to, subject in table rows listen.ts was already sanitized on main. JSON output is unaffected. Resolves: BU-656 Co-authored-by: Bu Kinoshita --- src/commands/emails/receiving/attachment.ts | 3 ++- src/commands/emails/receiving/get.ts | 14 ++++++++------ src/commands/emails/receiving/utils.ts | 9 ++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/commands/emails/receiving/attachment.ts b/src/commands/emails/receiving/attachment.ts index ded3c62c..a5ddc9c5 100644 --- a/src/commands/emails/receiving/attachment.ts +++ b/src/commands/emails/receiving/attachment.ts @@ -3,6 +3,7 @@ import { runGet } from '../../../lib/actions'; import type { GlobalOpts } from '../../../lib/client'; import { buildHelpText } from '../../../lib/help-text'; import { pickId } from '../../../lib/prompts'; +import { safeTerminalText } from '../../../lib/safe-terminal-text'; import { receivedAttachmentPickerConfig, receivedEmailPickerConfig, @@ -47,7 +48,7 @@ export const getAttachmentCommand = new Command('attachment') id: attachmentId, }), onInteractive: (data) => { - console.log(`${data.filename ?? '(unnamed)'}`); + console.log(`${safeTerminalText(data.filename ?? '(unnamed)')}`); console.log(`ID: ${data.id}`); console.log(`Content-Type: ${data.content_type}`); console.log(`Size: ${data.size} bytes`); diff --git a/src/commands/emails/receiving/get.ts b/src/commands/emails/receiving/get.ts index 8283c6b1..022cdfe7 100644 --- a/src/commands/emails/receiving/get.ts +++ b/src/commands/emails/receiving/get.ts @@ -3,6 +3,7 @@ import { runGet } from '../../../lib/actions'; import type { GlobalOpts } from '../../../lib/client'; import { buildHelpText } from '../../../lib/help-text'; import { pickId } from '../../../lib/prompts'; +import { safeTerminalText } from '../../../lib/safe-terminal-text'; import { receivedEmailPickerConfig } from './utils'; export const getReceivingCommand = new Command('get') @@ -32,18 +33,19 @@ export const getReceivingCommand = new Command('get') loading: 'Fetching received email...', sdkCall: (resend) => resend.emails.receiving.get(id), onInteractive: (data) => { - console.log(`From: ${data.from}`); - console.log(`To: ${data.to.join(', ')}`); - console.log(`Subject: ${data.subject}`); + console.log(`From: ${safeTerminalText(data.from)}`); + console.log(`To: ${data.to.map(safeTerminalText).join(', ')}`); + console.log(`Subject: ${safeTerminalText(data.subject)}`); console.log(`Date: ${data.created_at}`); if (data.attachments.length > 0) { console.log(`Files: ${data.attachments.length} attachment(s)`); } if (data.text) { + const sanitized = safeTerminalText(data.text); const snippet = - data.text.length > 200 - ? `${data.text.slice(0, 197)}...` - : data.text; + sanitized.length > 200 + ? `${sanitized.slice(0, 197)}...` + : sanitized; console.log(`${snippet}`); } else if (data.html) { console.log( diff --git a/src/commands/emails/receiving/utils.ts b/src/commands/emails/receiving/utils.ts index 0ec99853..dcd73627 100644 --- a/src/commands/emails/receiving/utils.ts +++ b/src/commands/emails/receiving/utils.ts @@ -1,5 +1,6 @@ import type { ListReceivingEmail } from 'resend'; import type { PickerConfig } from '../../../lib/prompts'; +import { safeTerminalText } from '../../../lib/safe-terminal-text'; import { renderTable } from '../../../lib/table'; export const receivedEmailPickerConfig: PickerConfig<{ @@ -33,11 +34,13 @@ export function renderReceivingEmailsTable( emails: ListReceivingEmail[], ): string { const rows = emails.map((e) => { - const to = e.to.join(', '); + const from = safeTerminalText(e.from); + const to = e.to.map(safeTerminalText).join(', '); const toStr = to.length > 40 ? `${to.slice(0, 37)}...` : to; + const rawSubject = safeTerminalText(e.subject); const subject = - e.subject.length > 50 ? `${e.subject.slice(0, 47)}...` : e.subject; - return [e.from, toStr, subject, e.created_at, e.id]; + rawSubject.length > 50 ? `${rawSubject.slice(0, 47)}...` : rawSubject; + return [from, toStr, subject, e.created_at, e.id]; }); return renderTable( ['From', 'To', 'Subject', 'Created At', 'ID'],