apple-mail-mcp 1.6.7 → 1.6.9
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.
|
@@ -36,6 +36,17 @@ export declare function splitSearchDiagnostics(output: string, account: string):
|
|
|
36
36
|
payload: string;
|
|
37
37
|
diagnostics: SearchDiagnostics;
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* True if `resolvedPath` is one of the allowed roots or strictly inside one.
|
|
41
|
+
*
|
|
42
|
+
* Uses a path-segment boundary check rather than a bare `startsWith`, which
|
|
43
|
+
* would let a sibling whose name merely shares the prefix slip through —
|
|
44
|
+
* `/Volumes-evil` startsWith `/Volumes`, `/Users/robother` startsWith
|
|
45
|
+
* `/Users/rob` (audit finding #12). `resolvedPath` must already be absolute
|
|
46
|
+
* (caller passes `resolve(...)` output).
|
|
47
|
+
*/
|
|
48
|
+
export declare function isPathWithinAllowedRoots(resolvedPath: string): boolean;
|
|
49
|
+
export declare function escapeForAppleScript(text: string): string;
|
|
39
50
|
/**
|
|
40
51
|
* Emits AppleScript that builds a date into the variable `varName` from numeric
|
|
41
52
|
* components.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appleMailManager.d.ts","sourceRoot":"","sources":["../../src/services/appleMailManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,OAAO,EACP,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,SAAS,EAET,oBAAoB,EACpB,UAAU,EACV,qBAAqB,EACrB,QAAQ,EACR,OAAO,EACP,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACb,MAAM,YAAY,CAAC;AA2DpB;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAK7F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,iBAAiB,CAAA;CAAE,CAsCrD;
|
|
1
|
+
{"version":3,"file":"appleMailManager.d.ts","sourceRoot":"","sources":["../../src/services/appleMailManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { spawnSync } from "child_process";
|
|
16
16
|
import { existsSync, writeFileSync } from "fs";
|
|
17
|
-
import { isAbsolute, resolve } from "path";
|
|
17
|
+
import { isAbsolute, resolve, sep } from "path";
|
|
18
18
|
import { homedir } from "os";
|
|
19
19
|
import { executeAppleScript } from "../utils/applescript.js";
|
|
20
20
|
import { parseMimeAttachments, extractMimeAttachment, extractHtmlBody } from "../utils/mimeParse.js";
|
|
@@ -139,10 +139,38 @@ export function splitSearchDiagnostics(output, account) {
|
|
|
139
139
|
* @param text - Raw text to escape
|
|
140
140
|
* @returns Text safe for AppleScript string embedding
|
|
141
141
|
*/
|
|
142
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Roots under which `save-attachment` is permitted to write.
|
|
144
|
+
*/
|
|
145
|
+
const ALLOWED_SAVE_ROOTS = [homedir(), "/tmp", "/private/tmp", "/Volumes"];
|
|
146
|
+
/**
|
|
147
|
+
* True if `resolvedPath` is one of the allowed roots or strictly inside one.
|
|
148
|
+
*
|
|
149
|
+
* Uses a path-segment boundary check rather than a bare `startsWith`, which
|
|
150
|
+
* would let a sibling whose name merely shares the prefix slip through —
|
|
151
|
+
* `/Volumes-evil` startsWith `/Volumes`, `/Users/robother` startsWith
|
|
152
|
+
* `/Users/rob` (audit finding #12). `resolvedPath` must already be absolute
|
|
153
|
+
* (caller passes `resolve(...)` output).
|
|
154
|
+
*/
|
|
155
|
+
export function isPathWithinAllowedRoots(resolvedPath) {
|
|
156
|
+
return ALLOWED_SAVE_ROOTS.some((root) => {
|
|
157
|
+
const base = root.endsWith(sep) ? root.slice(0, -1) : root;
|
|
158
|
+
return resolvedPath === base || resolvedPath.startsWith(base + sep);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export function escapeForAppleScript(text) {
|
|
143
162
|
if (!text)
|
|
144
163
|
return "";
|
|
145
|
-
|
|
164
|
+
// Escape backslash and double-quote for the AppleScript string literal, and
|
|
165
|
+
// strip ASCII control characters. An AppleScript double-quoted literal cannot
|
|
166
|
+
// contain a raw newline, so an interpolated value with a `\n` (or other
|
|
167
|
+
// control char) would terminate the literal early and could inject a
|
|
168
|
+
// statement; stripping them closes that gap (audit finding #10).
|
|
169
|
+
return (text
|
|
170
|
+
.replace(/\\/g, "\\\\")
|
|
171
|
+
.replace(/"/g, '\\"')
|
|
172
|
+
// eslint-disable-next-line no-control-regex
|
|
173
|
+
.replace(/[\x00-\x1f\x7f]/g, ""));
|
|
146
174
|
}
|
|
147
175
|
/**
|
|
148
176
|
* Validates attachment file paths and builds AppleScript commands to attach them.
|
|
@@ -1730,9 +1758,7 @@ export class AppleMailManager {
|
|
|
1730
1758
|
}
|
|
1731
1759
|
// Resolve the save path to prevent symlink / ".." traversal bypass
|
|
1732
1760
|
const resolvedPath = resolve(savePath);
|
|
1733
|
-
|
|
1734
|
-
const isAllowed = allowedPrefixes.some((prefix) => resolvedPath.startsWith(prefix));
|
|
1735
|
-
if (!isAllowed) {
|
|
1761
|
+
if (!isPathWithinAllowedRoots(resolvedPath)) {
|
|
1736
1762
|
console.error(`Save path "${savePath}" is outside allowed directories`);
|
|
1737
1763
|
return false;
|
|
1738
1764
|
}
|
|
@@ -1783,8 +1809,7 @@ export class AppleMailManager {
|
|
|
1783
1809
|
try {
|
|
1784
1810
|
const outPath = resolve(resolvedPath, attachmentName);
|
|
1785
1811
|
// Verify the resolved output path is still within allowed directories
|
|
1786
|
-
|
|
1787
|
-
if (!isOutAllowed) {
|
|
1812
|
+
if (!isPathWithinAllowedRoots(outPath)) {
|
|
1788
1813
|
console.error(`Output path "${outPath}" is outside allowed directories`);
|
|
1789
1814
|
return false;
|
|
1790
1815
|
}
|
|
@@ -1816,7 +1841,10 @@ export class AppleMailManager {
|
|
|
1816
1841
|
return mailboxList as text
|
|
1817
1842
|
`;
|
|
1818
1843
|
const script = buildAccountScopedScript(targetAccount, listCommand);
|
|
1819
|
-
|
|
1844
|
+
// Counts every mailbox's message total, so it needs more than the default
|
|
1845
|
+
// 30s on accounts with many/large mailboxes; a timeout here silently
|
|
1846
|
+
// returned an empty list (audit finding #8).
|
|
1847
|
+
const result = executeAppleScript(script, { timeoutMs: 60000 });
|
|
1820
1848
|
if (!result.success) {
|
|
1821
1849
|
console.error(`Failed to list mailboxes: ${result.error}`);
|
|
1822
1850
|
return [];
|
|
@@ -1860,7 +1888,9 @@ export class AppleMailManager {
|
|
|
1860
1888
|
`;
|
|
1861
1889
|
}
|
|
1862
1890
|
const script = buildAccountScopedScript(targetAccount, command);
|
|
1863
|
-
|
|
1891
|
+
// Summing unread across every mailbox can exceed the default 30s; a timeout
|
|
1892
|
+
// previously degraded silently to 0 ("all read") — audit finding #8.
|
|
1893
|
+
const result = executeAppleScript(script, { timeoutMs: 60000 });
|
|
1864
1894
|
if (!result.success) {
|
|
1865
1895
|
console.error(`Failed to get unread count: ${result.error}`);
|
|
1866
1896
|
return 0;
|
|
@@ -1929,24 +1959,55 @@ export class AppleMailManager {
|
|
|
1929
1959
|
const safeOld = escapeForAppleScript(resolvedOld);
|
|
1930
1960
|
const safeNew = escapeForAppleScript(resolvedNew);
|
|
1931
1961
|
const safeAccount = escapeForAppleScript(targetAccount);
|
|
1962
|
+
// Mail.app has no reliable in-place mailbox rename across account types, so
|
|
1963
|
+
// rename is emulated as create-new + move-all + delete-old. The risk (issue
|
|
1964
|
+
// #33) is that the old code iterated `messages of srcMailbox` *while moving*
|
|
1965
|
+
// (mutating the collection it was iterating, which can skip messages) and
|
|
1966
|
+
// then deleted the source unconditionally — so a move that errored or timed
|
|
1967
|
+
// out part-way lost the un-moved remainder. This version:
|
|
1968
|
+
// - snapshots the message references up front (move can't disturb iteration),
|
|
1969
|
+
// - moves each within its own `try` so one bad message doesn't abort the rest,
|
|
1970
|
+
// - and deletes the source ONLY if it is empty afterwards (every message
|
|
1971
|
+
// moved). On a partial move the source is left intact and we report how
|
|
1972
|
+
// many remain, so no mail is lost.
|
|
1932
1973
|
const moveScript = buildAppLevelScript(`
|
|
1933
1974
|
try
|
|
1934
1975
|
set srcMailbox to mailbox "${safeOld}" of account "${safeAccount}"
|
|
1935
1976
|
set destMailbox to mailbox "${safeNew}" of account "${safeAccount}"
|
|
1936
|
-
|
|
1937
|
-
|
|
1977
|
+
set srcCount to count of messages of srcMailbox
|
|
1978
|
+
set msgs to (every message of srcMailbox)
|
|
1979
|
+
repeat with m in msgs
|
|
1980
|
+
try
|
|
1981
|
+
move m to destMailbox
|
|
1982
|
+
end try
|
|
1938
1983
|
end repeat
|
|
1939
|
-
|
|
1940
|
-
|
|
1984
|
+
set srcAfter to count of messages of srcMailbox
|
|
1985
|
+
if srcAfter is 0 then
|
|
1986
|
+
delete mailbox "${safeOld}" of account "${safeAccount}"
|
|
1987
|
+
return "ok${FIELD_SEP}" & srcCount
|
|
1988
|
+
else
|
|
1989
|
+
return "partial${FIELD_SEP}" & (srcCount - srcAfter) & "${FIELD_SEP}" & srcCount & "${FIELD_SEP}" & srcAfter
|
|
1990
|
+
end if
|
|
1941
1991
|
on error errMsg
|
|
1942
1992
|
return "error:" & errMsg
|
|
1943
1993
|
end try
|
|
1944
1994
|
`);
|
|
1945
|
-
|
|
1995
|
+
// Moving a large source mailbox is slow; give it room. If it's still killed,
|
|
1996
|
+
// the source is never deleted (delete only runs after a verified-empty
|
|
1997
|
+
// check), so a truncated move is recoverable rather than lossy.
|
|
1998
|
+
const result = executeAppleScript(moveScript, { timeoutMs: 120000 });
|
|
1946
1999
|
if (!result.success || result.output.startsWith("error:")) {
|
|
1947
2000
|
console.error(`Failed to rename mailbox: ${result.error || result.output}`);
|
|
1948
2001
|
return false;
|
|
1949
2002
|
}
|
|
2003
|
+
if (result.output.startsWith("partial")) {
|
|
2004
|
+
const parts = result.output.split(FIELD_SEP);
|
|
2005
|
+
const remaining = parts[3] ?? "?";
|
|
2006
|
+
const total = parts[2] ?? "?";
|
|
2007
|
+
console.error(`Failed to rename mailbox: only ${parts[1] ?? "?"} of ${total} messages moved, ${remaining} remain in "${resolvedOld}"; source was NOT deleted (both mailboxes left intact). Retry to move the rest.`);
|
|
2008
|
+
this.invalidateCache(); // the new mailbox now exists and holds the moved messages
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
1950
2011
|
this.invalidateCache();
|
|
1951
2012
|
return true;
|
|
1952
2013
|
}
|
|
@@ -2096,18 +2157,22 @@ export class AppleMailManager {
|
|
|
2096
2157
|
set pid to id of p
|
|
2097
2158
|
if seenIds does not contain pid then
|
|
2098
2159
|
set end of seenIds to pid
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
set pEmails to
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
set pPhones to
|
|
2109
|
-
|
|
2110
|
-
|
|
2160
|
+
-- Per-person try: one malformed contact (e.g. no emails) must not
|
|
2161
|
+
-- abort the whole search and return an empty list (audit finding #13).
|
|
2162
|
+
try
|
|
2163
|
+
set pName to name of p
|
|
2164
|
+
set pEmails to ""
|
|
2165
|
+
repeat with e in emails of p
|
|
2166
|
+
if pEmails is not "" then set pEmails to pEmails & ","
|
|
2167
|
+
set pEmails to pEmails & (value of e)
|
|
2168
|
+
end repeat
|
|
2169
|
+
set pPhones to ""
|
|
2170
|
+
repeat with ph in phones of p
|
|
2171
|
+
if pPhones is not "" then set pPhones to pPhones & ","
|
|
2172
|
+
set pPhones to pPhones & (value of ph)
|
|
2173
|
+
end repeat
|
|
2174
|
+
set end of matchedContacts to pName & "${FIELD_SEP}" & pEmails & "${FIELD_SEP}" & pPhones
|
|
2175
|
+
end try
|
|
2111
2176
|
end if
|
|
2112
2177
|
end repeat
|
|
2113
2178
|
|
|
@@ -2172,10 +2237,13 @@ export class AppleMailManager {
|
|
|
2172
2237
|
const template = this.templates.get(id);
|
|
2173
2238
|
if (!template)
|
|
2174
2239
|
return false;
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
const
|
|
2240
|
+
// Use `??` (not `||`) for subject/body so an intentional empty-string
|
|
2241
|
+
// override is honored rather than falling back to the template value
|
|
2242
|
+
// (audit finding #14).
|
|
2243
|
+
const to = overrides?.to ?? template.to ?? [];
|
|
2244
|
+
const cc = overrides?.cc ?? template.cc;
|
|
2245
|
+
const subject = overrides?.subject ?? template.subject;
|
|
2246
|
+
const body = overrides?.body ?? template.body;
|
|
2179
2247
|
if (to.length === 0)
|
|
2180
2248
|
return false;
|
|
2181
2249
|
return this.createDraft(to, subject, body, cc);
|
|
@@ -2398,7 +2466,9 @@ export class AppleMailManager {
|
|
|
2398
2466
|
|
|
2399
2467
|
return "running${FIELD_SEP}" & accountCount & "${FIELD_SEP}" & totalMailboxes
|
|
2400
2468
|
`);
|
|
2401
|
-
|
|
2469
|
+
// Counts mailboxes across every account; give it headroom over the 30s
|
|
2470
|
+
// default so a slow account doesn't silently report "not syncing" (#8).
|
|
2471
|
+
const result = executeAppleScript(script, { timeoutMs: 60000 });
|
|
2402
2472
|
if (!result.success) {
|
|
2403
2473
|
return {
|
|
2404
2474
|
syncDetected: false,
|