Files
webmail/lib/attachment-text/sink.ts
T
Linus Rath 7c97eb2691 fix: keep attachment text reads within their size and character limits
Every reader now stops at the read's character budget, a cell at 4,000
characters, one Office XML part at 12 MB, and parts with a DTD are
refused. A compound file named .docx is encrypted only when it holds an
EncryptedPackage stream, else unsupported; .eml files are unsupported
instead of raw MIME; maxChars that isn't a number falls back to 20,000.

attachment-text.test.ts: the zip bomb test passes the cap as an option
now, and a bare compound file named secret.docx is expected unsupported
(it is a legacy file under a new name; limits.test.ts covers the
password-protected one).
2026-11-02 23:10:51 +01:00

29 lines
1.0 KiB
TypeScript

// Where a reader writes its lines: it takes text until the read's character
// budget is used up, then says it is full, so a file whose few bytes stand
// for millions of characters (repeated cells, a long PDF) is never copied
// out in full before the final cut.
export class TextSink {
private readonly lines: string[] = [];
private used = 0;
/** The budget is used up: whatever comes now is dropped, and the text is cut. */
full = false;
constructor(readonly budget: number) {}
/** Adds one line (cut to what is left); false once the budget is used up. */
line(text: string): boolean {
if (this.full) return false;
const room = this.budget - this.used;
const kept = text.length > room ? text.slice(0, room) : text;
this.lines.push(kept);
this.used += kept.length + 1;
if (kept.length < text.length || this.used >= this.budget) this.full = true;
return !this.full;
}
text(): string {
return this.lines.map((l) => l.replace(/[ \t]+$/g, '')).join('\n').replace(/\n{3,}/g, '\n\n').trim();
}
}