proton-mail-bridge-client 2.0.4 → 2.0.5

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
@@ -26,6 +26,8 @@ export declare function getReplyRecipients(detail: EmailDetail, ownerEmail: stri
26
26
  to: string[];
27
27
  cc: string[];
28
28
  };
29
+ export declare function getBulkMaxBatchSize(args: Record<string, unknown>): number;
30
+ export declare function ensureBulkBatchSize(uidsLength: number, max: number): void;
29
31
  export declare function buildConfigFromEnv(): ProtonMailConfig;
30
32
  export declare function createServer(config: ProtonMailConfig, options?: {
31
33
  startBackgroundSync?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AASA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAYnE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAA2H,iBAAiB,EAA+B,MAAM,mCAAmC,CAAC;AAC5N,OAAO,EAAkB,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,KAAK,EAUV,WAAW,EAEX,gBAAgB,EAEjB,MAAM,kBAAkB,CAAC;AA2qD1B,eAAO,MAAM,eAAe,aAyB1B,CAAC;AAgGH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAwBZ;AAmED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK3G;AAKD,wBAAgB,0BAA0B,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAI1G;AASD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAa9E;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAS7F;AA4KD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,OAAO,GAChB;IAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IAAC,EAAE,EAAE,MAAM,EAAE,CAAA;CAAE,CAyBhC;AAwzBD,wBAAgB,kBAAkB,IAAI,gBAAgB,CAkGrD;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE;IACP,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqhGP;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAmE1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AASA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAYnE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAA2H,iBAAiB,EAA+B,MAAM,mCAAmC,CAAC;AAC5N,OAAO,EAAkB,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,KAAK,EAUV,WAAW,EAEX,gBAAgB,EAEjB,MAAM,kBAAkB,CAAC;AA6qD1B,eAAO,MAAM,eAAe,aAyB1B,CAAC;AAgGH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAwBZ;AAmED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK3G;AAKD,wBAAgB,0BAA0B,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAI1G;AASD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAa9E;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAS7F;AA4KD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,OAAO,GAChB;IAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IAAC,EAAE,EAAE,MAAM,EAAE,CAAA;CAAE,CAyBhC;AAoiBD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEzE;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAOzE;AAyQD,wBAAgB,kBAAkB,IAAI,gBAAgB,CAkGrD;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE;IACP,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgjGP;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAmE1C"}
package/dist/index.js CHANGED
@@ -1131,6 +1131,7 @@ const TOOLS = [
1131
1131
  description: "Preview the impact without mutating the mailbox.",
1132
1132
  default: false,
1133
1133
  },
1134
+ maxBatchSize: { type: "number", description: "Maximum number of messages to process. Defaults to 500. Use to prevent runaway operations." },
1134
1135
  },
1135
1136
  required: ["emailIds", "action"],
1136
1137
  },
@@ -1171,6 +1172,7 @@ const TOOLS = [
1171
1172
  description: "Refresh the local mailbox index from IMAP before resolving the thread.",
1172
1173
  default: false,
1173
1174
  },
1175
+ maxBatchSize: { type: "number", description: "Maximum number of messages to process. Defaults to 500. Use to prevent runaway operations." },
1174
1176
  },
1175
1177
  required: ["threadId", "action"],
1176
1178
  },
@@ -2498,10 +2500,10 @@ async function previewEmailAction(imapService, emailId, action, targetFolder) {
2498
2500
  async function verifySentCopy(imapService, messageId) {
2499
2501
  return imapService.sentCopyVerify(messageId, "Sent", 8_000);
2500
2502
  }
2501
- function getBulkMaxBatchSize(args) {
2503
+ export function getBulkMaxBatchSize(args) {
2502
2504
  return typeof args.maxBatchSize === "number" ? Math.min(args.maxBatchSize, 2000) : 500;
2503
2505
  }
2504
- function ensureBulkBatchSize(uidsLength, max) {
2506
+ export function ensureBulkBatchSize(uidsLength, max) {
2505
2507
  if (uidsLength > max) {
2506
2508
  throw new McpError(ErrorCode.InvalidParams, "Batch size " + uidsLength + " exceeds limit " + max + ". Use match criteria to narrow the scope, or increase maxBatchSize.");
2507
2509
  }
@@ -3850,6 +3852,15 @@ export function createServer(config, options = {}) {
3850
3852
  // after an ambiguous response) sailed past the status check above
3851
3853
  // and enqueued a second, independent delivery-queue record. Both
3852
3854
  // fire independently later, delivering the same email twice.
3855
+ //
3856
+ // Found live: this list()-then-check was itself a non-atomic
3857
+ // TOCTOU race — two concurrent schedule_draft calls could both
3858
+ // observe no pending record here and both enqueue. The real
3859
+ // enforcement now lives inside deliveryQueueService.enqueue()
3860
+ // itself, under its lock, which is what actually prevents the
3861
+ // duplicate. This external check is kept only as an optional
3862
+ // early-exit optimization to avoid a wasted round-trip to the lock
3863
+ // in the common non-racing case.
3853
3864
  const alreadyPendingScheduled = (await deliveryQueueService.list()).find((record) => record.sourceDraftId === draft.id && record.status === "pending");
3854
3865
  if (alreadyPendingScheduled) {
3855
3866
  throw new McpError(ErrorCode.InvalidParams, `This draft already has a pending scheduled send (id ${alreadyPendingScheduled.id}, sendAt ${alreadyPendingScheduled.sendAt}). Scheduling it again would deliver it twice. Cancel the existing one with cancel_send first if you want a different sendAt.`);
@@ -4121,15 +4132,23 @@ export function createServer(config, options = {}) {
4121
4132
  // mailbox each time.
4122
4133
  const currentUidValidity = emailIds ? await imapService.getMailboxUidValidity(folder) : undefined;
4123
4134
  const notFoundEmailIds = getBulkNotFoundEmailIds(emailIds, folder, currentUidValidity);
4124
- const preview = await imapService.bulkMove({
4125
- emailIds,
4126
- match,
4127
- folder,
4128
- targetFolder,
4129
- dryRun: true,
4130
- });
4131
- ensureBulkBatchSize(preview.total, max);
4135
+ // Resolve the match/emailIds set exactly once and reuse it for both
4136
+ // the preview and the real run — see resolveUidsForBulkOp's
4137
+ // comment for why re-resolving `match` a second time (the old
4138
+ // dry-run-then-real-run pattern) could silently exceed maxBatchSize.
4139
+ // Also excludes any id whose embedded UIDVALIDITY is stale.
4140
+ const uids = await imapService.resolveUidsForBulkOp(folder, emailIds, match, currentUidValidity);
4141
+ ensureBulkBatchSize(uids.length, max);
4132
4142
  if (normalizeBoolean(args.dryRun, false)) {
4143
+ const preview = await imapService.bulkMove({
4144
+ emailIds,
4145
+ match,
4146
+ folder,
4147
+ targetFolder,
4148
+ resolvedUids: uids,
4149
+ uidValidity: currentUidValidity,
4150
+ dryRun: true,
4151
+ });
4133
4152
  return createTextResult(withBulkNotFound(preview, notFoundEmailIds));
4134
4153
  }
4135
4154
  const result = await withAudit(auditService, name, args, () => imapService.bulkMove({
@@ -4137,6 +4156,8 @@ export function createServer(config, options = {}) {
4137
4156
  match,
4138
4157
  folder,
4139
4158
  targetFolder,
4159
+ resolvedUids: uids,
4160
+ uidValidity: currentUidValidity,
4140
4161
  dryRun: false,
4141
4162
  }));
4142
4163
  return createTextResult(withBulkNotFound(result, notFoundEmailIds));
@@ -4534,6 +4555,10 @@ export function createServer(config, options = {}) {
4534
4555
  if (emailIds.length === 0) {
4535
4556
  throw new McpError(ErrorCode.InvalidParams, "emailIds must contain at least one email id.");
4536
4557
  }
4558
+ // Same safety rail as the bulk_* tools (bulk_delete etc.) — without
4559
+ // it, an arbitrarily large emailIds array would be processed in
4560
+ // full with no size limit at all.
4561
+ ensureBulkBatchSize(emailIds.length, getBulkMaxBatchSize(args));
4537
4562
  const action = requireEmailAction(args);
4538
4563
  ensureEmailActionAllowed(config.runtime, action);
4539
4564
  if (action === "delete") {
@@ -5116,6 +5141,10 @@ export function createServer(config, options = {}) {
5116
5141
  const emailIds = [...new Set(thread.messages
5117
5142
  .filter((message) => !unreadOnly || !message.isRead)
5118
5143
  .map((message) => message.primaryEmailId))];
5144
+ // Same safety rail as the bulk_* tools (bulk_delete etc.) — without
5145
+ // it, a thread with an arbitrarily large message count would be
5146
+ // processed in full with no size limit at all.
5147
+ ensureBulkBatchSize(emailIds.length, getBulkMaxBatchSize(args));
5119
5148
  if (action === "delete") {
5120
5149
  ensureDestructiveConfirmed(config.runtime, normalizeBoolean(args.confirmed, false), `Permanently delete ${emailIds.length} email(s) in thread (cannot be recovered)`);
5121
5150
  }