@ni-c/imap-mcp 0.2.0 → 0.4.0
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/README.md +142 -35
- package/dist/analyze.d.ts +54 -6
- package/dist/analyze.js +295 -27
- package/dist/analyze.js.map +1 -1
- package/dist/attachments.d.ts +36 -0
- package/dist/attachments.js +21 -3
- package/dist/attachments.js.map +1 -1
- package/dist/audit.d.ts +7 -0
- package/dist/audit.js +11 -3
- package/dist/audit.js.map +1 -1
- package/dist/config.d.ts +32 -0
- package/dist/config.js +65 -6
- package/dist/config.js.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/extract/child.d.ts +1 -0
- package/dist/extract/child.js +83 -0
- package/dist/extract/child.js.map +1 -0
- package/dist/extract/index.d.ts +41 -0
- package/dist/extract/index.js +183 -0
- package/dist/extract/index.js.map +1 -0
- package/dist/extract/ooxml.d.ts +35 -0
- package/dist/extract/ooxml.js +627 -0
- package/dist/extract/ooxml.js.map +1 -0
- package/dist/extract/pdf.d.ts +62 -0
- package/dist/extract/pdf.js +539 -0
- package/dist/extract/pdf.js.map +1 -0
- package/dist/extract/types.d.ts +56 -0
- package/dist/extract/types.js +13 -0
- package/dist/extract/types.js.map +1 -0
- package/dist/imap.js.map +1 -1
- package/dist/index.js +32 -5
- package/dist/index.js.map +1 -1
- package/dist/output-schema.d.ts +63 -0
- package/dist/output-schema.js +87 -0
- package/dist/output-schema.js.map +1 -0
- package/dist/resources.d.ts +1 -1
- package/dist/resources.js +5 -2
- package/dist/resources.js.map +1 -1
- package/dist/result.d.ts +39 -6
- package/dist/result.js +142 -29
- package/dist/result.js.map +1 -1
- package/dist/schema.d.ts +13 -1
- package/dist/schema.js +20 -2
- package/dist/schema.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +30 -5
- package/dist/server.js.map +1 -1
- package/dist/tools/annotations.d.ts +32 -0
- package/dist/tools/annotations.js +33 -0
- package/dist/tools/annotations.js.map +1 -0
- package/dist/tools/catalogue.d.ts +2 -2
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.js +743 -87
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/write.d.ts +3 -3
- package/dist/tools/write.js +174 -39
- package/dist/tools/write.js.map +1 -1
- package/package.json +17 -11
- package/dist/approval.d.ts +0 -45
- package/dist/approval.js +0 -69
- package/dist/approval.js.map +0 -1
- package/dist/confirm.d.ts +0 -59
- package/dist/confirm.js +0 -92
- package/dist/confirm.js.map +0 -1
- package/dist/tool-filter.d.ts +0 -45
- package/dist/tool-filter.js +0 -171
- package/dist/tool-filter.js.map +0 -1
package/dist/analyze.js
CHANGED
|
@@ -45,9 +45,18 @@ const INJECTION_PATTERNS = [
|
|
|
45
45
|
'role-injection',
|
|
46
46
|
/(?:^|[-\u2014|>\])]\s{0,3})(system|assistant|developer)\s*:/im,
|
|
47
47
|
],
|
|
48
|
+
// Anchored at the start of the run. Without the lookbehind, `-{3,}` was
|
|
49
|
+
// tried from every position inside a run and backtracked once per possible
|
|
50
|
+
// length each time, which is quadratic on text that is nothing but hyphens:
|
|
51
|
+
// 40 000 of them took 1.5 s, and the million characters an extracted
|
|
52
|
+
// document may carry would have held this process — the whole server, its
|
|
53
|
+
// transport is stdio — for a quarter of an hour. A few kilobytes on the
|
|
54
|
+
// wire. With it, a run is one attempt from its first character and the
|
|
55
|
+
// positions inside it fail in constant time: a million hyphens in 5 ms,
|
|
56
|
+
// and a delimiter of any length is still found.
|
|
48
57
|
[
|
|
49
58
|
'fake-delimiter',
|
|
50
|
-
/(-{3,}|={3,}|#{3,})\s*(begin|end|system|instruction|prompt)/i,
|
|
59
|
+
/(?<![-=#])(-{3,}|={3,}|#{3,})\s*(begin|end|system|instruction|prompt)/i,
|
|
51
60
|
],
|
|
52
61
|
[
|
|
53
62
|
'tool-coercion',
|
|
@@ -86,46 +95,282 @@ const INJECTION_PATTERNS = [
|
|
|
86
95
|
],
|
|
87
96
|
];
|
|
88
97
|
/**
|
|
89
|
-
* Cap on the HTML
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
98
|
+
* Cap on the HTML this pass looks at, and on the span a single element may
|
|
99
|
+
* swallow.
|
|
100
|
+
*
|
|
101
|
+
* Neither of these is what keeps the pass cheap, and the previous version of
|
|
102
|
+
* this comment claimed otherwise. Removing an element means scanning forward
|
|
103
|
+
* for a closing token, and a bound on *that* scan bounds one factor of a
|
|
104
|
+
* product whose other factor is the number of scans an input can start. A body
|
|
105
|
+
* of `'<style '` repeated 73 000 times is 512 000 legal bytes that start 73 000
|
|
106
|
+
* bounded scans and finish none of them: the removal regexes this used to be
|
|
107
|
+
* built from took 33 seconds on it, on a single-threaded process whose
|
|
108
|
+
* transport is stdio. The scan below is a single left-to-right walk instead, so
|
|
109
|
+
* the number of start tokens no longer multiplies anything.
|
|
97
110
|
*/
|
|
98
111
|
const MAX_HTML_CHARS = 512_000;
|
|
99
112
|
const MAX_REMOVED_BLOCK_CHARS = 50_000;
|
|
100
113
|
const MAX_HIDDEN_ELEMENT_CHARS = 10_000;
|
|
101
|
-
const HTML_COMMENT = new RegExp(`<!--[\\s\\S]{0,${MAX_REMOVED_BLOCK_CHARS}}?-->`, 'g');
|
|
102
|
-
const NON_CONTENT_ELEMENT = new RegExp(`<(script|style|head|title|noscript|template)\\b[\\s\\S]{0,${MAX_REMOVED_BLOCK_CHARS}}?<\\/\\1>`, 'gi');
|
|
103
|
-
const HIDDEN_ELEMENT = new RegExp(`<([a-z0-9]+)\\b[^>]*style\\s*=\\s*("|')[^"']*(display\\s*:\\s*none|visibility\\s*:\\s*hidden|opacity\\s*:\\s*0|font-size\\s*:\\s*0)[^"']*\\2[^>]*>[\\s\\S]{0,${MAX_HIDDEN_ELEMENT_CHARS}}?<\\/\\1>`, 'gi');
|
|
104
114
|
/**
|
|
105
|
-
*
|
|
115
|
+
* Total characters the walk may spend looking for closing tags, across the
|
|
116
|
+
* whole document.
|
|
117
|
+
*
|
|
118
|
+
* This is the cap on the product. Each successful removal costs the length of
|
|
119
|
+
* what it removed, and removals do not overlap, so an honest document never
|
|
120
|
+
* comes close; only an input that starts removals it never closes can exhaust
|
|
121
|
+
* it. Once it is gone the remaining elements are stripped as ordinary tags and
|
|
122
|
+
* their content stays visible — best effort degrading to less effort, never to
|
|
123
|
+
* a stalled server.
|
|
124
|
+
*/
|
|
125
|
+
const CLOSER_SCAN_BUDGET_FACTOR = 4;
|
|
126
|
+
const CLOSER_SCAN_BUDGET_FLOOR = 100_000;
|
|
127
|
+
/**
|
|
128
|
+
* Elements whose content the recipient never reads.
|
|
129
|
+
*
|
|
130
|
+
* The `w:`-prefixed names are WordprocessingML, not HTML: this walk is also how
|
|
131
|
+
* the text of a .docx attachment is read. A Word field code
|
|
132
|
+
* (`INCLUDEPICTURE "http://…"`, a DDE command) is markup the reader never sees
|
|
133
|
+
* for exactly the same reason a `<script>` body is, and `w:delText` is text a
|
|
134
|
+
* tracked change deleted — shown struck through at most, and not at all in the
|
|
135
|
+
* view a document is read in.
|
|
136
|
+
*/
|
|
137
|
+
const NON_CONTENT_TAGS = new Set([
|
|
138
|
+
'script',
|
|
139
|
+
'style',
|
|
140
|
+
'head',
|
|
141
|
+
'title',
|
|
142
|
+
'noscript',
|
|
143
|
+
'template',
|
|
144
|
+
'w:instrtext',
|
|
145
|
+
'w:deltext',
|
|
146
|
+
]);
|
|
147
|
+
/**
|
|
148
|
+
* Closing tags that end a visual block, and so earn a line break.
|
|
149
|
+
*
|
|
150
|
+
* The namespaced names are the OOXML and OpenDocument paragraph, heading and
|
|
151
|
+
* row elements. Without them a whole .docx comes back as one line, because
|
|
152
|
+
* nothing else in a document part ever closes a block.
|
|
153
|
+
*/
|
|
154
|
+
const BLOCK_TAGS = new Set([
|
|
155
|
+
'p',
|
|
156
|
+
'div',
|
|
157
|
+
'tr',
|
|
158
|
+
'li',
|
|
159
|
+
'h1',
|
|
160
|
+
'h2',
|
|
161
|
+
'h3',
|
|
162
|
+
'h4',
|
|
163
|
+
'h5',
|
|
164
|
+
'h6',
|
|
165
|
+
'w:p',
|
|
166
|
+
'w:tr',
|
|
167
|
+
'a:p',
|
|
168
|
+
'text:p',
|
|
169
|
+
'text:h',
|
|
170
|
+
'table:table-row',
|
|
171
|
+
]);
|
|
172
|
+
// The name may carry a namespace prefix and a hyphen: `</w:p>`, `</text:h>`,
|
|
173
|
+
// `</table:table-row>`. Without the colon and the hyphen this matched `w` for
|
|
174
|
+
// `</w:p>`, so every namespaced entry in the sets above would be dead code.
|
|
175
|
+
// HTML is unaffected — no HTML element name contains either character, and a
|
|
176
|
+
// custom element (`<my-widget>`) is in none of the sets under either reading.
|
|
177
|
+
const TAG_NAME = /^<\/?([A-Za-z][A-Za-z0-9:_.-]*)/;
|
|
178
|
+
const BR_TAG = /^<br\s*\/?$/i;
|
|
179
|
+
const STYLE_ATTRIBUTE = /style\s*=\s*("|')/gi;
|
|
180
|
+
const HIDDEN_VALUE = /display\s*:\s*none|visibility\s*:\s*hidden|opacity\s*:\s*0|font-size\s*:\s*0/i;
|
|
181
|
+
/**
|
|
182
|
+
* Whether a start tag carries an inline style that hides it.
|
|
183
|
+
*
|
|
184
|
+
* Written as "find the attribute, then look inside its value" rather than as
|
|
185
|
+
* one pattern spanning both, because the one-pattern form has to guess where
|
|
186
|
+
* the value ends and backtrack when it guesses wrong. Here each style value is
|
|
187
|
+
* inspected once and the walk over the tag never turns around.
|
|
188
|
+
*/
|
|
189
|
+
function hasHiddenStyle(tag) {
|
|
190
|
+
STYLE_ATTRIBUTE.lastIndex = 0;
|
|
191
|
+
for (let match = STYLE_ATTRIBUTE.exec(tag); match !== null; match = STYLE_ATTRIBUTE.exec(tag)) {
|
|
192
|
+
const quote = match[1];
|
|
193
|
+
const start = match.index + match[0].length;
|
|
194
|
+
const end = tag.indexOf(quote, start);
|
|
195
|
+
if (end < 0)
|
|
196
|
+
return false;
|
|
197
|
+
if (HIDDEN_VALUE.test(tag.slice(start, end)))
|
|
198
|
+
return true;
|
|
199
|
+
STYLE_ATTRIBUTE.lastIndex = end;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Extracts readable text from HTML — and from the XML inside an OOXML or
|
|
205
|
+
* OpenDocument attachment, which is the same problem with different tag names.
|
|
206
|
+
*
|
|
207
|
+
* Reused there rather than reimplemented, and that is a security decision, not
|
|
208
|
+
* a tidiness one. The obvious `document.xml` reader is
|
|
209
|
+
* `/<w:t[^>]*>([\s\S]*?)<\/w:t>/g` — which is the exact shape of the bug
|
|
210
|
+
* recorded above this function: *n* start tokens each paying for a scan that
|
|
211
|
+
* never finds its end. A `document.xml` of `'<w:t '` repeated 200 000 times is
|
|
212
|
+
* a tiny, deflate-friendly ZIP entry. This walk is immune for the reason it was
|
|
213
|
+
* written, so the second parser is the one not to write.
|
|
214
|
+
*
|
|
215
|
+
* It is also why nothing here parses XML properly. A real parser resolves
|
|
216
|
+
* entities, and would hand a mail attachment billion-laughs expansion and an
|
|
217
|
+
* `<!ENTITY … SYSTEM "file:///etc/passwd">` that reads a file. Those are not
|
|
218
|
+
* defended against below; they are simply not implemented. `&lol9;` comes out
|
|
219
|
+
* as six literal characters, and it must stay that way — reaching for
|
|
220
|
+
* `fast-xml-parser` here would reintroduce all three at once.
|
|
106
221
|
*
|
|
107
222
|
* Deliberately not `mailparser`'s own `text` fallback: that keeps content the
|
|
108
223
|
* recipient never sees. Anything hidden by inline CSS is a place to park an
|
|
109
224
|
* instruction meant only for the model, so those elements are dropped before
|
|
110
|
-
* the tags are stripped.
|
|
111
|
-
*
|
|
112
|
-
*
|
|
225
|
+
* the tags are stripped.
|
|
226
|
+
*
|
|
227
|
+
* `maxChars` overrides the input slice. The default suits a mail body; a
|
|
228
|
+
* document part needs more, because OOXML spends most of its bytes on
|
|
229
|
+
* formatting and the readable text is a small fraction of it.
|
|
230
|
+
*
|
|
231
|
+
* This is one pass over the input. The cursors below only ever move forward,
|
|
232
|
+
* which is the property that makes the whole function linear no matter what the
|
|
233
|
+
* sender writes: a start token that is never closed is answered once and then
|
|
234
|
+
* never looked for again, instead of restarting a bounded scan at every
|
|
235
|
+
* occurrence. Removal itself stays best effort — nested same-name tags end a
|
|
236
|
+
* block early, elements hidden via a stylesheet class are not recognised at
|
|
237
|
+
* all, and anything past the scan budget is left in place. That is acceptable
|
|
238
|
+
* for the same reason it always was: nothing downstream trusts the stripping,
|
|
239
|
+
* and the fencing in {@link wrapUntrusted} is what carries the weight.
|
|
113
240
|
*/
|
|
114
|
-
export function htmlToText(html) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
241
|
+
export function htmlToText(html, maxChars = MAX_HTML_CHARS) {
|
|
242
|
+
const source = html.slice(0, maxChars);
|
|
243
|
+
const out = [];
|
|
244
|
+
// Forward-only cursors. Each call may advance them, never rewind them, so
|
|
245
|
+
// across the whole document each scans the input at most once — the same
|
|
246
|
+
// reason a `-->` that does not exist costs one pass rather than one per
|
|
247
|
+
// `<!--`.
|
|
248
|
+
let nextGt = source.indexOf('>');
|
|
249
|
+
let nextCommentEnd = source.indexOf('-->');
|
|
250
|
+
const gtFrom = (from) => {
|
|
251
|
+
while (nextGt >= 0 && nextGt < from)
|
|
252
|
+
nextGt = source.indexOf('>', nextGt + 1);
|
|
253
|
+
return nextGt;
|
|
254
|
+
};
|
|
255
|
+
const commentEndFrom = (from) => {
|
|
256
|
+
while (nextCommentEnd >= 0 && nextCommentEnd < from) {
|
|
257
|
+
nextCommentEnd = source.indexOf('-->', nextCommentEnd + 1);
|
|
258
|
+
}
|
|
259
|
+
return nextCommentEnd;
|
|
260
|
+
};
|
|
261
|
+
let budget = source.length * CLOSER_SCAN_BUDGET_FACTOR + CLOSER_SCAN_BUDGET_FLOOR;
|
|
262
|
+
/** Where `</name>` starts, or -1 when nothing closes this element in time. */
|
|
263
|
+
const closingTagFrom = (name, from, window) => {
|
|
264
|
+
if (budget <= 0)
|
|
265
|
+
return -1;
|
|
266
|
+
const stop = Math.min(source.length, from + window);
|
|
267
|
+
const needle = `</${name}>`;
|
|
268
|
+
let at = source.indexOf('</', from);
|
|
269
|
+
while (at >= 0 && at + needle.length <= stop) {
|
|
270
|
+
if (source.slice(at, at + needle.length).toLowerCase() === needle) {
|
|
271
|
+
budget -= at - from;
|
|
272
|
+
return at;
|
|
273
|
+
}
|
|
274
|
+
at = source.indexOf('</', at + 2);
|
|
275
|
+
}
|
|
276
|
+
// Charged against how far the search actually looked, not against the
|
|
277
|
+
// window: a document with no `</` at all sends every one of these to the
|
|
278
|
+
// end of the input, and a budget that only counted the window would let an
|
|
279
|
+
// attacker buy those scans at a fiftieth of their price.
|
|
280
|
+
budget -= (at < 0 ? source.length : Math.min(at, stop)) - from;
|
|
281
|
+
return -1;
|
|
282
|
+
};
|
|
283
|
+
let i = 0;
|
|
284
|
+
while (i < source.length) {
|
|
285
|
+
const lt = source.indexOf('<', i);
|
|
286
|
+
if (lt < 0) {
|
|
287
|
+
out.push(source.slice(i));
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
if (lt > i)
|
|
291
|
+
out.push(source.slice(i, lt));
|
|
292
|
+
if (source.startsWith('<!--', lt)) {
|
|
293
|
+
const end = commentEndFrom(lt + 4);
|
|
294
|
+
if (end >= 0 && end - lt <= MAX_REMOVED_BLOCK_CHARS) {
|
|
295
|
+
out.push(' ');
|
|
296
|
+
i = end + 3;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
// Nothing closes it anywhere ahead, so it is text that looks like
|
|
300
|
+
// markup rather than markup. Emitting it keeps the reader honest.
|
|
301
|
+
out.push('<');
|
|
302
|
+
i = lt + 1;
|
|
303
|
+
}
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const gt = gtFrom(lt + 1);
|
|
307
|
+
if (gt < 0) {
|
|
308
|
+
// No `>` in the rest of the document: everything from here is text.
|
|
309
|
+
out.push(source.slice(lt));
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
const tag = source.slice(lt, gt);
|
|
313
|
+
const name = TAG_NAME.exec(tag)?.[1]?.toLowerCase();
|
|
314
|
+
const closing = tag.startsWith('</');
|
|
315
|
+
if (name !== undefined && !closing) {
|
|
316
|
+
const window = NON_CONTENT_TAGS.has(name)
|
|
317
|
+
? MAX_REMOVED_BLOCK_CHARS
|
|
318
|
+
: hasHiddenStyle(tag)
|
|
319
|
+
? MAX_HIDDEN_ELEMENT_CHARS
|
|
320
|
+
: 0;
|
|
321
|
+
if (window > 0) {
|
|
322
|
+
const end = closingTagFrom(name, gt + 1, window);
|
|
323
|
+
if (end >= 0) {
|
|
324
|
+
out.push(' ');
|
|
325
|
+
i = end + name.length + 3;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (BR_TAG.test(tag)) {
|
|
331
|
+
out.push('\n');
|
|
332
|
+
}
|
|
333
|
+
else if (closing && name !== undefined && BLOCK_TAGS.has(name)) {
|
|
334
|
+
out.push('\n');
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
out.push(' ');
|
|
338
|
+
}
|
|
339
|
+
i = gt + 1;
|
|
340
|
+
}
|
|
341
|
+
return (out
|
|
342
|
+
.join('')
|
|
123
343
|
.replace(/ /gi, ' ')
|
|
124
344
|
.replace(/</gi, '<')
|
|
125
345
|
.replace(/>/gi, '>')
|
|
126
346
|
.replace(/"/gi, '"')
|
|
127
347
|
.replace(/'/g, "'")
|
|
128
|
-
.
|
|
348
|
+
// Numeric character references. Common in HTML mail (`’` for a
|
|
349
|
+
// curly apostrophe) and near-universal in OOXML, where a German document
|
|
350
|
+
// may write every umlaut this way — without this they arrive as literal
|
|
351
|
+
// `ä`. Bounded digit counts so the pattern cannot be made to scan,
|
|
352
|
+
// and out-of-range values produce nothing rather than a guess.
|
|
353
|
+
.replace(/&#x([0-9a-f]{1,6});/gi, (match, hex) => fromCodePoint(parseInt(hex, 16), match))
|
|
354
|
+
.replace(/&#(\d{1,7});/g, (match, digits) => fromCodePoint(Number(digits), match))
|
|
355
|
+
// Last, so a decoded `&lt;` does not turn into a `<` the caller never
|
|
356
|
+
// received.
|
|
357
|
+
.replace(/&/gi, '&'));
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* One character from a numeric reference, or the reference itself.
|
|
361
|
+
*
|
|
362
|
+
* Returning the original text for anything out of range is the conservative
|
|
363
|
+
* half: a reference nobody can render is better left visible than turned into a
|
|
364
|
+
* replacement character that reads as content. Surrogates are excluded because
|
|
365
|
+
* a lone one is not a character and only makes the string harder to handle
|
|
366
|
+
* downstream.
|
|
367
|
+
*/
|
|
368
|
+
function fromCodePoint(value, original) {
|
|
369
|
+
if (!Number.isInteger(value) || value < 1 || value > 0x10ffff)
|
|
370
|
+
return original;
|
|
371
|
+
if (value >= 0xd800 && value <= 0xdfff)
|
|
372
|
+
return original;
|
|
373
|
+
return String.fromCodePoint(value);
|
|
129
374
|
}
|
|
130
375
|
/**
|
|
131
376
|
* Removes the characters a human reader cannot see but the model can.
|
|
@@ -136,6 +381,20 @@ export function htmlToText(html) {
|
|
|
136
381
|
export function stripInvisible(input) {
|
|
137
382
|
return input.replace(INVISIBLE_CHARS, '').replace(CONTROL_CHARS, '');
|
|
138
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* The same characters, written out as escapes instead of removed.
|
|
386
|
+
*
|
|
387
|
+
* For the places where a string has to stay recognisable as the exact thing
|
|
388
|
+
* that was asked for — a confirmation dialog, an audit line. Stripping alone
|
|
389
|
+
* says "this is not what it looked like" and then shows something that looks
|
|
390
|
+
* like an ordinary name; this shows which characters were in it, so a person
|
|
391
|
+
* deciding whether to move mail into `Archive<U+202E>` can see that the folder is
|
|
392
|
+
* not the `Archive` they know.
|
|
393
|
+
*/
|
|
394
|
+
export function escapeInvisible(input) {
|
|
395
|
+
const escape = (match) => `\\u${match.codePointAt(0).toString(16).padStart(4, '0')}`;
|
|
396
|
+
return input.replace(INVISIBLE_CHARS, escape).replace(CONTROL_CHARS, escape);
|
|
397
|
+
}
|
|
139
398
|
/**
|
|
140
399
|
* Normalises text before it reaches the model: Unicode-folded, stripped of the
|
|
141
400
|
* characters a human reader cannot see, auto-fetch markup defused, and
|
|
@@ -159,7 +418,16 @@ export function sanitizeText(input, maxChars = MAX_BODY_CHARS) {
|
|
|
159
418
|
? `${normalized.slice(0, maxChars)}\n… (truncated at ${maxChars} characters)`
|
|
160
419
|
: normalized;
|
|
161
420
|
}
|
|
162
|
-
/**
|
|
421
|
+
/**
|
|
422
|
+
* Names of the injection shapes present in `text`.
|
|
423
|
+
*
|
|
424
|
+
* Runs in this process on text the sender wrote, up to a million characters
|
|
425
|
+
* of it for an extracted document — after the parser child has exited, so no
|
|
426
|
+
* timeout covers it. Every pattern above therefore has to be linear on a
|
|
427
|
+
* hostile repetition of its own trigger, and `analyze.test.ts` times each one
|
|
428
|
+
* on such input. A new pattern gets a line in that test before it gets a line
|
|
429
|
+
* in the list.
|
|
430
|
+
*/
|
|
163
431
|
export function detectSuspicious(text) {
|
|
164
432
|
return INJECTION_PATTERNS.filter(([, pattern]) => pattern.test(text)).map(([name]) => name);
|
|
165
433
|
}
|
package/dist/analyze.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"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"}
|
package/dist/attachments.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { MessageStructureObject } from 'imapflow';
|
|
2
|
+
/**
|
|
3
|
+
* Extensions that are executable somewhere. The list is long on purpose: this
|
|
4
|
+
* is a refusal list applied on top of the content-type allowlist, and the cost
|
|
5
|
+
* of an extra entry is one unusual attachment that has to be fetched by other
|
|
6
|
+
* means.
|
|
7
|
+
*
|
|
8
|
+
* Exported so a test can walk it. An entry {@link extensionOf} cannot produce
|
|
9
|
+
* is not a stricter list, it is a longer one that refuses less — and nothing
|
|
10
|
+
* about reading the two declarations side by side says which is which.
|
|
11
|
+
*/
|
|
12
|
+
export declare const EXECUTABLE_EXTENSIONS: Set<string>;
|
|
2
13
|
export interface AttachmentCandidate {
|
|
3
14
|
partId: string;
|
|
4
15
|
filename: string;
|
|
@@ -13,7 +24,18 @@ export interface AttachmentCandidate {
|
|
|
13
24
|
}
|
|
14
25
|
export interface AttachmentPolicy {
|
|
15
26
|
allowedTypes: string[];
|
|
27
|
+
/**
|
|
28
|
+
* Size ceiling for the destination this check is for.
|
|
29
|
+
*
|
|
30
|
+
* Per destination, because there are three of them and they answer different
|
|
31
|
+
* questions: what may enter the model's context, what may be written to disk,
|
|
32
|
+
* what a parser may be handed. The caller picks the number; naming it here
|
|
33
|
+
* keeps the refusal pointing at the variable an operator would actually
|
|
34
|
+
* change.
|
|
35
|
+
*/
|
|
16
36
|
maxBytes: number;
|
|
37
|
+
/** Environment variable behind {@link maxBytes}, for the refusal text. */
|
|
38
|
+
maxBytesName: string;
|
|
17
39
|
}
|
|
18
40
|
/**
|
|
19
41
|
* Strips a filename down to something safe to print and to reason about.
|
|
@@ -23,6 +45,20 @@ export interface AttachmentPolicy {
|
|
|
23
45
|
* separators is trying to be read as something it is not.
|
|
24
46
|
*/
|
|
25
47
|
export declare function sanitizeFilename(raw: string | undefined): string;
|
|
48
|
+
/**
|
|
49
|
+
* The trailing extension of a filename, lowercased, or `''`.
|
|
50
|
+
*
|
|
51
|
+
* The character class and the length have to cover every entry of
|
|
52
|
+
* {@link EXECUTABLE_EXTENSIONS}, or the blocklist has entries that can never be
|
|
53
|
+
* read out of a name. Both used to be too narrow: `appref-ms` carries a hyphen
|
|
54
|
+
* and `application` is eleven characters, so both returned `''` — and an empty
|
|
55
|
+
* extension makes {@link checkPolicy} skip the executable check entirely rather
|
|
56
|
+
* than fail it. `Rechnung-2026.appref-ms` declared as `application/xml` (which
|
|
57
|
+
* is in the default type allowlist, and which a ClickOnce manifest genuinely
|
|
58
|
+
* is) therefore reached the download directory under its own name with no note
|
|
59
|
+
* against it. The property test over the whole set is what keeps the two in
|
|
60
|
+
* step from here on.
|
|
61
|
+
*/
|
|
26
62
|
export declare function extensionOf(filename: string): string;
|
|
27
63
|
/**
|
|
28
64
|
* Walks the MIME tree and returns every part that is an attachment.
|
package/dist/attachments.js
CHANGED
|
@@ -9,8 +9,12 @@ const MAX_FILENAME_LENGTH = 120;
|
|
|
9
9
|
* is a refusal list applied on top of the content-type allowlist, and the cost
|
|
10
10
|
* of an extra entry is one unusual attachment that has to be fetched by other
|
|
11
11
|
* means.
|
|
12
|
+
*
|
|
13
|
+
* Exported so a test can walk it. An entry {@link extensionOf} cannot produce
|
|
14
|
+
* is not a stricter list, it is a longer one that refuses less — and nothing
|
|
15
|
+
* about reading the two declarations side by side says which is which.
|
|
12
16
|
*/
|
|
13
|
-
const EXECUTABLE_EXTENSIONS = new Set([
|
|
17
|
+
export const EXECUTABLE_EXTENSIONS = new Set([
|
|
14
18
|
'ade',
|
|
15
19
|
'adp',
|
|
16
20
|
'app',
|
|
@@ -133,8 +137,22 @@ export function sanitizeFilename(raw) {
|
|
|
133
137
|
? `${cleaned.slice(0, MAX_FILENAME_LENGTH)}…`
|
|
134
138
|
: cleaned;
|
|
135
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* The trailing extension of a filename, lowercased, or `''`.
|
|
142
|
+
*
|
|
143
|
+
* The character class and the length have to cover every entry of
|
|
144
|
+
* {@link EXECUTABLE_EXTENSIONS}, or the blocklist has entries that can never be
|
|
145
|
+
* read out of a name. Both used to be too narrow: `appref-ms` carries a hyphen
|
|
146
|
+
* and `application` is eleven characters, so both returned `''` — and an empty
|
|
147
|
+
* extension makes {@link checkPolicy} skip the executable check entirely rather
|
|
148
|
+
* than fail it. `Rechnung-2026.appref-ms` declared as `application/xml` (which
|
|
149
|
+
* is in the default type allowlist, and which a ClickOnce manifest genuinely
|
|
150
|
+
* is) therefore reached the download directory under its own name with no note
|
|
151
|
+
* against it. The property test over the whole set is what keeps the two in
|
|
152
|
+
* step from here on.
|
|
153
|
+
*/
|
|
136
154
|
export function extensionOf(filename) {
|
|
137
|
-
const match = /\.([A-Za-z0-9]{1,
|
|
155
|
+
const match = /\.([A-Za-z0-9-]{1,16})$/.exec(filename);
|
|
138
156
|
return match?.[1]?.toLowerCase() ?? '';
|
|
139
157
|
}
|
|
140
158
|
/**
|
|
@@ -207,7 +225,7 @@ export function checkPolicy(candidate, policy) {
|
|
|
207
225
|
}
|
|
208
226
|
if (candidate.size !== undefined && candidate.size > policy.maxBytes) {
|
|
209
227
|
allowed = false;
|
|
210
|
-
notes.push(`refused: declared size ${candidate.size} exceeds
|
|
228
|
+
notes.push(`refused: declared size ${candidate.size} exceeds ${policy.maxBytesName} (${policy.maxBytes})`);
|
|
211
229
|
}
|
|
212
230
|
return { ...candidate, allowed, notes };
|
|
213
231
|
}
|
package/dist/attachments.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE/D,+EAA+E;AAC/E,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,8DAA8D;AAC9D,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"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"}
|
package/dist/audit.d.ts
CHANGED
|
@@ -13,5 +13,12 @@
|
|
|
13
13
|
* by the sender, but it has been through sanitizeFilename — no control or
|
|
14
14
|
* invisible characters, no separators — and without it the line could not say
|
|
15
15
|
* which file to go and delete.
|
|
16
|
+
*
|
|
17
|
+
* That reasoning had a hole: folder names go in these lines too, and on a
|
|
18
|
+
* shared account they are chosen by whoever created the folder. `to=Archive`
|
|
19
|
+
* for a destination of `Archive<U+202E>…` is a log entry that reads as the wrong
|
|
20
|
+
* folder, and a CR in a name rewrites the line a human is looking at. So every
|
|
21
|
+
* string value is escaped on the way out — see {@link escapeInvisible}. What
|
|
22
|
+
* cannot be seen must not be written here.
|
|
16
23
|
*/
|
|
17
24
|
export declare function audit(operation: string, details: Record<string, unknown>): void;
|
package/dist/audit.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { escapeInvisible } from './analyze.js';
|
|
1
2
|
/**
|
|
2
3
|
* Records every change this server makes to the mailbox.
|
|
3
4
|
*
|
|
@@ -13,6 +14,13 @@
|
|
|
13
14
|
* by the sender, but it has been through sanitizeFilename — no control or
|
|
14
15
|
* invisible characters, no separators — and without it the line could not say
|
|
15
16
|
* which file to go and delete.
|
|
17
|
+
*
|
|
18
|
+
* That reasoning had a hole: folder names go in these lines too, and on a
|
|
19
|
+
* shared account they are chosen by whoever created the folder. `to=Archive`
|
|
20
|
+
* for a destination of `Archive<U+202E>…` is a log entry that reads as the wrong
|
|
21
|
+
* folder, and a CR in a name rewrites the line a human is looking at. So every
|
|
22
|
+
* string value is escaped on the way out — see {@link escapeInvisible}. What
|
|
23
|
+
* cannot be seen must not be written here.
|
|
16
24
|
*/
|
|
17
25
|
export function audit(operation, details) {
|
|
18
26
|
const fields = Object.entries(details)
|
|
@@ -25,9 +33,9 @@ export function audit(operation, details) {
|
|
|
25
33
|
function format(value) {
|
|
26
34
|
if (Array.isArray(value)) {
|
|
27
35
|
return value.length > 20
|
|
28
|
-
? `[${value.slice(0, 20).join(',')},…+${value.length - 20}]`
|
|
29
|
-
: `[${value.join(',')}]`;
|
|
36
|
+
? `[${escapeInvisible(value.slice(0, 20).join(','))},…+${value.length - 20}]`
|
|
37
|
+
: `[${escapeInvisible(value.join(','))}]`;
|
|
30
38
|
}
|
|
31
|
-
return String(value);
|
|
39
|
+
return escapeInvisible(String(value));
|
|
32
40
|
}
|
|
33
41
|
//# sourceMappingURL=audit.js.map
|
package/dist/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,KAAK,CACnB,SAAiB,EACjB,OAAgC;IAEhC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;SACnC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;SAChD,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,OAAO,CAAC,KAAK,CACX,kBAAkB,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,SAAS,IAAI,MAAM,EAAE,CACpE,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,MAAM,GAAG,EAAE;YACtB,CAAC,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,MAAM,GAAG,EAAE,GAAG;YAC7E,CAAC,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;IAC9C,CAAC;IACD,OAAO,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACxC,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
@@ -34,6 +34,17 @@ export interface ImapConfig {
|
|
|
34
34
|
*/
|
|
35
35
|
downloadDir: string | undefined;
|
|
36
36
|
maxDownloadBytes: number;
|
|
37
|
+
/**
|
|
38
|
+
* Ceiling on the bytes handed to the text-extraction worker.
|
|
39
|
+
*
|
|
40
|
+
* A third limit because it answers a third question. `maxAttachmentBytes`
|
|
41
|
+
* bounds what may enter the model's context — extraction does not, since only
|
|
42
|
+
* the extracted text comes back and `max_chars` bounds that. `maxDownloadBytes`
|
|
43
|
+
* bounds what may be written to the filesystem. This one bounds how much
|
|
44
|
+
* hostile input a parser is asked to chew on inside a thread that can be
|
|
45
|
+
* terminated, which is a memory question and neither of the other two.
|
|
46
|
+
*/
|
|
47
|
+
maxExtractBytes: number;
|
|
37
48
|
}
|
|
38
49
|
export interface Config {
|
|
39
50
|
imap: ImapConfig;
|
|
@@ -48,6 +59,13 @@ export interface Config {
|
|
|
48
59
|
* the rest of the family; the default deliberately is not.
|
|
49
60
|
*/
|
|
50
61
|
readOnly: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Whether a client that *can* show a dialog is asked before a guarded tool
|
|
64
|
+
* acts. `ELICITATION=false` turns the dialog off — the guard stays and falls
|
|
65
|
+
* back to the two-call token, so there is no setting in which a guarded call
|
|
66
|
+
* goes unannounced.
|
|
67
|
+
*/
|
|
68
|
+
elicitation: boolean;
|
|
51
69
|
/**
|
|
52
70
|
* Raw value of `IMAP_ALLOW_TOOLS` — comma-separated tool names, `list_*`
|
|
53
71
|
* prefixes, or `essential`. Kept unparsed on purpose: this file is a mirror
|
|
@@ -73,3 +91,17 @@ export declare function missingConfigKeys(config: Config): string[];
|
|
|
73
91
|
* IMAP session, and a bad port would connect somewhere unintended.
|
|
74
92
|
*/
|
|
75
93
|
export declare function loadConfig(env?: NodeJS.ProcessEnv): Config;
|
|
94
|
+
/**
|
|
95
|
+
* Reads `ELICITATION` — deliberately unprefixed, and deliberately fatal on
|
|
96
|
+
* anything it does not recognise.
|
|
97
|
+
*
|
|
98
|
+
* Unprefixed: environment variables are process-wide, so this is one switch for
|
|
99
|
+
* every server in the same environment. That is also its risk, which is why a
|
|
100
|
+
* server started with it off says so on its startup line.
|
|
101
|
+
*
|
|
102
|
+
* Fatal, like `parseTlsMode` above and unlike `IMAP_READ_ONLY`: this is the
|
|
103
|
+
* first variable of the family that defaults to *on*, so a typo that fell back
|
|
104
|
+
* to the default would leave the dialog running while the operator believes it
|
|
105
|
+
* is off — and an operator who believes that has no way to find out.
|
|
106
|
+
*/
|
|
107
|
+
export declare function parseElicitation(raw: string | undefined): boolean;
|