proton-mail-bridge-client 2.1.38 → 2.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -137,6 +137,7 @@ export declare function createServer(config: ProtonMailConfig, options?: {
137
137
  deliveryQueueService: import("./services/delivery-queue-service.js").DeliveryQueueService;
138
138
  snoozeService: import("./services/snooze-service.js").SnoozeService;
139
139
  auditService: AuditService;
140
+ accountManager: AccountManager;
140
141
  };
141
142
  export declare function main(): Promise<void>;
142
143
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -3763,15 +3763,10 @@ export function createServer(config, options = {}) {
3763
3763
  }
3764
3764
  // dryRun: preview without sending
3765
3765
  const dryRunSend = normalizeBoolean(args.dryRun, false);
3766
- // RESTRICT_OUTBOUND_TO_SELF check
3767
- if (config.runtime.restrictOutboundToSelf) {
3768
- const selfAddr = config.smtp.username.toLowerCase();
3769
- const allR = [...to, ...(cc ?? []), ...(bcc ?? [])];
3770
- const ext = allR.filter(r => r.toLowerCase() !== selfAddr);
3771
- if (ext.length > 0) {
3772
- throw new McpError(ErrorCode.InvalidParams, `RESTRICT_OUTBOUND_TO_SELF is enabled. Cannot send to: ${ext.join(", ")}`);
3773
- }
3774
- }
3766
+ // RESTRICT_OUTBOUND_TO_SELF check — shared helper so a self-send to
3767
+ // "you+tag@..." isn't wrongly rejected (see its comment in runtime-policy.ts).
3768
+ // `from`'s own account (if any) is the correct "self" for a multi-account send.
3769
+ ensureOutboundRecipientsAllowed(config.runtime, (from ? accountManager.byAddress(from)?.config.smtp.username : undefined) ?? config.smtp.username, [...to, ...(cc ?? []), ...(bcc ?? [])]);
3775
3770
  if (dryRunSend) {
3776
3771
  return createTextResult({ dryRun: true, wouldSendTo: { to, cc: cc ?? [], bcc: bcc ?? [] }, subject, note: "No email was sent." });
3777
3772
  }
@@ -3847,6 +3842,9 @@ export function createServer(config, options = {}) {
3847
3842
  });
3848
3843
  }
3849
3844
  case "cancel_send": {
3845
+ // Zero policy gate: canceling a queued send mutates local delivery-queue
3846
+ // state, but nothing here ever checked read-only mode.
3847
+ ensureMailboxWriteAllowed(config.runtime);
3850
3848
  // The queue id carries an account-slug prefix when the item was enqueued
3851
3849
  // through a non-primary account (see send_email/schedule_draft) — resolve
3852
3850
  // it back to that account's own deliveryQueueService, same as an
@@ -4002,14 +4000,9 @@ export function createServer(config, options = {}) {
4002
4000
  const replyHtmlBody = includeQuoteReply && signedReply.htmlBody !== undefined
4003
4001
  ? buildReplyHtml(detail, signedReply.htmlBody)
4004
4002
  : signedReply.htmlBody;
4005
- if (config.runtime.restrictOutboundToSelf) {
4006
- const selfAddr = config.smtp.username.toLowerCase();
4007
- const allR = [...to, ...cc, ...extraBcc];
4008
- const ext = allR.filter(r => r.toLowerCase() !== selfAddr);
4009
- if (ext.length > 0) {
4010
- throw new McpError(ErrorCode.InvalidParams, `RESTRICT_OUTBOUND_TO_SELF is enabled. Cannot send to: ${ext.join(", ")}`);
4011
- }
4012
- }
4003
+ // Shared helper, and the sending account's own address as "self" —
4004
+ // see send_email's comment.
4005
+ ensureOutboundRecipientsAllowed(config.runtime, sendBundleForReply.config.smtp.username, [...to, ...cc, ...extraBcc]);
4013
4006
  if (dryRunReply) {
4014
4007
  return createTextResult({ dryRun: true, wouldSendTo: { to, cc, bcc: extraBcc }, subject: prefixedSubject(detail.subject, "Re:"), note: "No email was sent." }, false, [emailSource({ ...detail, id: prefixedIdFor(readBundleReply, detail.id) })]);
4015
4008
  }
@@ -4087,14 +4080,9 @@ export function createServer(config, options = {}) {
4087
4080
  throw new McpError(ErrorCode.InvalidParams, "Unable to infer reply recipients.");
4088
4081
  }
4089
4082
  const dryRunRa = normalizeBoolean(args.dryRun, false);
4090
- if (config.runtime.restrictOutboundToSelf) {
4091
- const selfAddr = config.smtp.username.toLowerCase();
4092
- const allR = [...toRa, ...ccRa, ...extraBccRa];
4093
- const ext = allR.filter(r => r.toLowerCase() !== selfAddr);
4094
- if (ext.length > 0) {
4095
- throw new McpError(ErrorCode.InvalidParams, `RESTRICT_OUTBOUND_TO_SELF is enabled. Cannot send to: ${ext.join(", ")}`);
4096
- }
4097
- }
4083
+ // Shared helper, and the sending account's own address as "self" —
4084
+ // see send_email's comment.
4085
+ ensureOutboundRecipientsAllowed(config.runtime, sendBundleForRa.config.smtp.username, [...toRa, ...ccRa, ...extraBccRa]);
4098
4086
  if (dryRunRa) {
4099
4087
  return createTextResult({ dryRun: true, wouldSendTo: { to: toRa, cc: ccRa, bcc: extraBccRa }, subject: prefixedSubject(detailRa.subject, "Re:"), note: "No email was sent." });
4100
4088
  }
@@ -4189,14 +4177,9 @@ export function createServer(config, options = {}) {
4189
4177
  ensureValidEmails(cc, "cc");
4190
4178
  ensureValidEmails(bcc, "bcc");
4191
4179
  const dryRunFwd = normalizeBoolean(args.dryRun, false);
4192
- if (config.runtime.restrictOutboundToSelf) {
4193
- const selfAddr = config.smtp.username.toLowerCase();
4194
- const allR = [...to, ...cc, ...bcc];
4195
- const ext = allR.filter(r => r.toLowerCase() !== selfAddr);
4196
- if (ext.length > 0) {
4197
- throw new McpError(ErrorCode.InvalidParams, `RESTRICT_OUTBOUND_TO_SELF is enabled. Cannot send to: ${ext.join(", ")}`);
4198
- }
4199
- }
4180
+ // Shared helper, and the sending account's own address as "self" —
4181
+ // see send_email's comment.
4182
+ ensureOutboundRecipientsAllowed(config.runtime, sendBundleForFwd.config.smtp.username, [...to, ...cc, ...bcc]);
4200
4183
  if (dryRunFwd) {
4201
4184
  return createTextResult({ dryRun: true, wouldSendTo: { to, cc, bcc }, subject: prefixedSubject(detail.subject, "Fwd:"), note: "No email was sent." });
4202
4185
  }
@@ -4267,6 +4250,9 @@ export function createServer(config, options = {}) {
4267
4250
  }, false, [emailSource({ ...detail, id: prefixedIdFor(readBundleFwd, detail.id) })]);
4268
4251
  }
4269
4252
  case "create_draft": {
4253
+ // Local draft write — was only gated on the remote-sync side, so
4254
+ // PROTONMAIL_READ_ONLY never actually stopped a local draft being created.
4255
+ ensureMailboxWriteAllowed(config.runtime);
4270
4256
  const to = parseEmails(optionalString(args, "to"));
4271
4257
  const cc = parseEmails(optionalString(args, "cc"));
4272
4258
  const bcc = parseEmails(optionalString(args, "bcc"));
@@ -4338,6 +4324,8 @@ export function createServer(config, options = {}) {
4338
4324
  ]);
4339
4325
  }
4340
4326
  case "create_reply_draft": {
4327
+ // Local draft write — same gap as create_draft, see its comment.
4328
+ ensureMailboxWriteAllowed(config.runtime);
4341
4329
  // emailId carries the same "<slug>::" account prefix as everywhere else —
4342
4330
  // this previously always read via the PRIMARY account's imapService
4343
4331
  // regardless of that prefix, so replying to a non-primary account's email
@@ -4422,6 +4410,8 @@ export function createServer(config, options = {}) {
4422
4410
  ]);
4423
4411
  }
4424
4412
  case "create_forward_draft": {
4413
+ // Local draft write — same gap as create_draft, see its comment.
4414
+ ensureMailboxWriteAllowed(config.runtime);
4425
4415
  // Same emailId account resolution as create_reply_draft — see its comment.
4426
4416
  const { bundle: createForwardReadBundle, rest: createForwardEmailIdRest } = resolveAccountForEmailId(requireString(args, "emailId"));
4427
4417
  const detail = await createForwardReadBundle.imapService.getEmailById(createForwardEmailIdRest);
@@ -4568,6 +4558,8 @@ export function createServer(config, options = {}) {
4568
4558
  ]);
4569
4559
  }
4570
4560
  case "update_draft": {
4561
+ // Local draft write — same gap as create_draft, see its comment.
4562
+ ensureMailboxWriteAllowed(config.runtime);
4571
4563
  const { bundle: updateDraftBundle, rest: updateDraftIdRest } = resolveAccountForDraftId(requireString(args, "draftId"));
4572
4564
  const to = args.to === undefined ? undefined : parseEmails(optionalString(args, "to"));
4573
4565
  const cc = args.cc === undefined ? undefined : parseEmails(optionalString(args, "cc"));
@@ -4745,21 +4737,10 @@ export function createServer(config, options = {}) {
4745
4737
  if (draft.replyTo && !isValidEmail(draft.replyTo)) {
4746
4738
  throw new McpError(ErrorCode.InvalidParams, "replyTo must be a valid email address.");
4747
4739
  }
4748
- if (config.runtime.restrictOutboundToSelf) {
4749
- const allRecipients = [...draft.to, ...draft.cc, ...draft.bcc];
4750
- // Found live: with PROTONMAIL_IMAP_USERNAME set to a different
4751
- // address than the account's send identity, this compared
4752
- // recipients against the IMAP login instead — rejecting mail to
4753
- // the real self as "external" (and, the other way round, would
4754
- // let mail to that IMAP-only address through as if it were
4755
- // self). Every other RESTRICT_OUTBOUND_TO_SELF check in this
4756
- // file already uses smtp.username, since that's the identity
4757
- // mail actually sends as; this was the one inconsistent copy.
4758
- const selfAddr = config.smtp.username.toLowerCase();
4759
- const external = allRecipients.filter(r => r.toLowerCase() !== selfAddr);
4760
- if (external.length > 0)
4761
- throw new McpError(ErrorCode.InvalidParams, "PROTONMAIL_RESTRICT_OUTBOUND_TO_SELF is enabled. All recipients must be the authenticated user.");
4762
- }
4740
+ // Shared helper, and the sending account's own address as "self" —
4741
+ // see send_email's comment. isSelfAddress also fixes the IMAP-vs-SMTP
4742
+ // username mismatch the old inline check's comment warned about.
4743
+ ensureOutboundRecipientsAllowed(config.runtime, sendBundleForDraft.config.smtp.username, [...draft.to, ...draft.cc, ...draft.bcc]);
4763
4744
  if (normalizeBoolean(args.dryRun, false)) {
4764
4745
  return createTextResult({
4765
4746
  dryRun: true,
@@ -4953,14 +4934,9 @@ export function createServer(config, options = {}) {
4953
4934
  if (draft.replyTo && !isValidEmail(draft.replyTo)) {
4954
4935
  throw new McpError(ErrorCode.InvalidParams, "replyTo must be a valid email address.");
4955
4936
  }
4956
- if (config.runtime.restrictOutboundToSelf) {
4957
- const allRecipients = [...draft.to, ...draft.cc, ...draft.bcc];
4958
- // Same fix as send_draft's identical check — see its comment.
4959
- const selfAddr = config.smtp.username.toLowerCase();
4960
- const external = allRecipients.filter((r) => r.toLowerCase() !== selfAddr);
4961
- if (external.length > 0)
4962
- throw new McpError(ErrorCode.InvalidParams, "PROTONMAIL_RESTRICT_OUTBOUND_TO_SELF is enabled. All recipients must be the authenticated user.");
4963
- }
4937
+ // Shared helper, and the sending account's own address as "self" —
4938
+ // see send_draft's identical fix.
4939
+ ensureOutboundRecipientsAllowed(config.runtime, sendBundleForScheduleDraft.config.smtp.username, [...draft.to, ...draft.cc, ...draft.bcc]);
4964
4940
  const queued = await withAudit(auditService, name, args, async () => sendBundleForScheduleDraft.deliveryQueueService.enqueue({
4965
4941
  to: draft.to,
4966
4942
  cc: draft.cc,
@@ -4990,6 +4966,8 @@ export function createServer(config, options = {}) {
4990
4966
  });
4991
4967
  }
4992
4968
  case "delete_draft": {
4969
+ // Local draft write — same gap as create_draft, see its comment.
4970
+ ensureMailboxWriteAllowed(config.runtime);
4993
4971
  const { bundle: deleteDraftBundle, rest: deleteDraftIdRest } = resolveAccountForDraftId(requireString(args, "draftId"));
4994
4972
  const draft = await deleteDraftBundle.draftStore.getDraft(deleteDraftIdRest);
4995
4973
  ensureDestructiveConfirmed(config.runtime, normalizeBoolean(args?.confirmed, false), "Permanently delete draft: " + draft.id);
@@ -5894,6 +5872,8 @@ export function createServer(config, options = {}) {
5894
5872
  return createTextResult(filtered);
5895
5873
  }
5896
5874
  case "create_template": {
5875
+ // Local template write — same gap as create_draft, see its comment.
5876
+ ensureMailboxWriteAllowed(config.runtime);
5897
5877
  const result = await withAudit(auditService, name, args, async () => templateService.create({
5898
5878
  name: requireString(args, "name"),
5899
5879
  subject: requireString(args, "subject"),
@@ -5911,6 +5891,8 @@ export function createServer(config, options = {}) {
5911
5891
  return createTextResult(result);
5912
5892
  }
5913
5893
  case "delete_template": {
5894
+ // Local template write — same gap as create_draft, see its comment.
5895
+ ensureMailboxWriteAllowed(config.runtime);
5914
5896
  const templateId = requireString(args, "id");
5915
5897
  ensureDestructiveConfirmed(config.runtime, normalizeBoolean(args?.confirmed, false), "Permanently delete template: " + templateId);
5916
5898
  const result = await withAudit(auditService, name, args, async () => templateService.delete(templateId));
@@ -7154,12 +7136,17 @@ export function createServer(config, options = {}) {
7154
7136
  return createTextResult(result, false, result.saved.map((entry) => attachmentSource(result.emailId, entry.attachment)));
7155
7137
  }
7156
7138
  case "clear_cache":
7139
+ // Zero policy gate: this mutates local cache state, but nothing here
7140
+ // ever checked read-only mode.
7141
+ ensureMailboxWriteAllowed(config.runtime);
7157
7142
  analyticsService.clearCache();
7158
7143
  return createTextResult({
7159
7144
  clearedAt: new Date().toISOString(),
7160
7145
  ...imapService.clearCache(),
7161
7146
  });
7162
7147
  case "clear_index":
7148
+ // Zero policy gate — same gap as clear_cache, see its comment.
7149
+ ensureMailboxWriteAllowed(config.runtime);
7163
7150
  return createTextResult({
7164
7151
  clearedAt: new Date().toISOString(),
7165
7152
  ...(await localIndexService.clear()),
@@ -7227,6 +7214,7 @@ export function createServer(config, options = {}) {
7227
7214
  deliveryQueueService,
7228
7215
  snoozeService,
7229
7216
  auditService,
7217
+ accountManager,
7230
7218
  };
7231
7219
  }
7232
7220
  export async function main() {
@@ -7245,7 +7233,7 @@ export async function main() {
7245
7233
  // Explicitly chmod it too so the restriction actually takes effect on
7246
7234
  // upgrade, not only on a brand-new dataDir.
7247
7235
  await chmod(config.dataDir, 0o700).catch(() => { });
7248
- const { server, smtpService, imapService, backgroundSyncService, deliveryQueueService, snoozeService } = createServer(config, {
7236
+ const { server, smtpService, imapService, backgroundSyncService, deliveryQueueService, snoozeService, accountManager } = createServer(config, {
7249
7237
  startBackgroundSync: true,
7250
7238
  });
7251
7239
  logger.info("Starting ProtonMail MCP server", "MCPServer");
@@ -7262,7 +7250,11 @@ export async function main() {
7262
7250
  backgroundSyncService.stop();
7263
7251
  deliveryQueueService.stop();
7264
7252
  snoozeService.stop();
7265
- await Promise.allSettled([imapService.disconnect(), smtpService.close()]);
7253
+ await Promise.allSettled([
7254
+ imapService.disconnect(),
7255
+ smtpService.close(),
7256
+ ...accountManager.all().map((bundle) => bundle.localIndexService.close()),
7257
+ ]);
7266
7258
  process.exit(0);
7267
7259
  };
7268
7260
  process.on("SIGINT", () => {