spamscanner 7.0.7 → 7.0.8
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/cjs/arf.cjs.map +1 -1
- package/dist/cjs/index.cjs +37 -9
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/esm/arf.js.map +1 -1
- package/dist/esm/cli.js +35 -8
- package/dist/esm/cli.js.map +2 -2
- package/dist/esm/index.js +37 -9
- package/dist/esm/index.js.map +2 -2
- package/dist/types/index.d.ts +13 -3
- package/dist/types/parse.d.ts +1 -1
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -2116,6 +2116,9 @@ function unwrapLink(address2) {
|
|
|
2116
2116
|
}
|
|
2117
2117
|
for (let depth = 0; depth < 3; depth++) {
|
|
2118
2118
|
const target = gatewayTarget(url);
|
|
2119
|
+
if (!target) {
|
|
2120
|
+
break;
|
|
2121
|
+
}
|
|
2119
2122
|
let next;
|
|
2120
2123
|
try {
|
|
2121
2124
|
next = new URL(target);
|
|
@@ -3947,24 +3950,38 @@ async function parseWithin(raw, parse = parseMessage, deadline = Date.now() + PA
|
|
|
3947
3950
|
}
|
|
3948
3951
|
}
|
|
3949
3952
|
}
|
|
3950
|
-
async function expandAttachments(attachments = [], errors = [], parse = parseMessage) {
|
|
3953
|
+
async function expandAttachments(attachments = [], errors = [], parse = parseMessage, unopened = []) {
|
|
3951
3954
|
const all = [];
|
|
3952
3955
|
const deadline = Date.now() + PARSE_TIMEOUT;
|
|
3953
3956
|
let budget = MAX_NESTED_BYTES;
|
|
3954
3957
|
let nested = 0;
|
|
3958
|
+
let tooMany = false;
|
|
3955
3959
|
const visit = async (list, prefix, depth) => {
|
|
3956
3960
|
for (const attachment of list) {
|
|
3957
3961
|
if (prefix) {
|
|
3958
3962
|
if (nested >= MAX_ATTACHMENTS) {
|
|
3963
|
+
if (!tooMany) {
|
|
3964
|
+
tooMany = true;
|
|
3965
|
+
unopened.push({ filename: prefix, reason: `more than ${MAX_ATTACHMENTS} attachments in attached messages` });
|
|
3966
|
+
}
|
|
3959
3967
|
return;
|
|
3960
3968
|
}
|
|
3961
3969
|
nested++;
|
|
3962
3970
|
}
|
|
3963
3971
|
const name = attachment.filename || (prefix ? "unnamed attachment" : "");
|
|
3972
|
+
const filename = prefix ? `${prefix}/${name}` : name || "attached message";
|
|
3964
3973
|
all.push(prefix ? { ...attachment, filename: `${prefix}/${name}` } : attachment);
|
|
3965
3974
|
const { content } = attachment;
|
|
3966
3975
|
const attached = attachment.contentType === "message/rfc822" || /\.eml$/i.test(attachment.filename || "");
|
|
3967
|
-
if (attached &&
|
|
3976
|
+
if (attached && Buffer2.isBuffer(content) && depth >= MAX_NESTED_DEPTH) {
|
|
3977
|
+
unopened.push({ filename, reason: `nested more than ${MAX_NESTED_DEPTH} deep` });
|
|
3978
|
+
continue;
|
|
3979
|
+
}
|
|
3980
|
+
if (attached && Buffer2.isBuffer(content) && content.length > budget) {
|
|
3981
|
+
unopened.push({ filename, reason: `past ${MAX_NESTED_BYTES / 1024 / 1024} MB of attached messages` });
|
|
3982
|
+
continue;
|
|
3983
|
+
}
|
|
3984
|
+
if (attached && Buffer2.isBuffer(content)) {
|
|
3968
3985
|
budget -= content.length;
|
|
3969
3986
|
let inner;
|
|
3970
3987
|
try {
|
|
@@ -7031,7 +7048,7 @@ function scoreResults(results, options) {
|
|
|
7031
7048
|
var package_default = {
|
|
7032
7049
|
name: "spamscanner",
|
|
7033
7050
|
description: "Spam, phishing and malware filter for email in any language: a trainable classifier, link and attachment checks, SPF/DKIM/DMARC, and an optional AI second opinion from any LLM, including local Ollama models. Works as a library, CLI, Postfix milter or HTTP API.",
|
|
7034
|
-
version: "7.0.
|
|
7051
|
+
version: "7.0.8",
|
|
7035
7052
|
author: "Forward Email (https://forwardemail.net)",
|
|
7036
7053
|
bin: {
|
|
7037
7054
|
spamscanner: "./dist/esm/cli.js"
|
|
@@ -8833,14 +8850,16 @@ var SpamScanner = class {
|
|
|
8833
8850
|
* a header field ("Name: value", or an mbox "From " line) is read as the
|
|
8834
8851
|
* body of a message with no header, so its text is the input; other
|
|
8835
8852
|
* input mailparser cannot read gives a message with no text. Other errors
|
|
8836
|
-
* are thrown.
|
|
8853
|
+
* are thrown. `error` is the parser's limit the message exceeds, so that
|
|
8854
|
+
* only its first parts were read (as scan() reports it, see parseError),
|
|
8855
|
+
* or null.
|
|
8837
8856
|
* @param {Buffer|string|Uint8Array|AsyncIterable} source
|
|
8838
|
-
* @returns {Promise<{raw: Buffer, mail: object}>}
|
|
8857
|
+
* @returns {Promise<{raw: Buffer, mail: object, error: string|null}>}
|
|
8839
8858
|
*/
|
|
8840
8859
|
async parse(source) {
|
|
8841
8860
|
const raw = await toBuffer(source);
|
|
8842
|
-
const { mail } = await parseWithin(withHeader(raw));
|
|
8843
|
-
return { raw, mail };
|
|
8861
|
+
const { mail, error } = await parseWithin(withHeader(raw));
|
|
8862
|
+
return { raw, mail, error: error ?? null };
|
|
8844
8863
|
}
|
|
8845
8864
|
/**
|
|
8846
8865
|
* Classifier features of a parsed message.
|
|
@@ -9200,7 +9219,8 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
|
|
|
9200
9219
|
mixed: Math.max(plainObfuscation.mixed, shownObfuscation.mixed),
|
|
9201
9220
|
styled: plainObfuscation.styled || shownObfuscation.styled
|
|
9202
9221
|
};
|
|
9203
|
-
const
|
|
9222
|
+
const unopened = [];
|
|
9223
|
+
const attached = await expandAttachments(mail.attachments, parseErrors, void 0, unopened);
|
|
9204
9224
|
const attachments = config.attachments === false ? [] : inspectAttachments(attached).filter((item) => config.macros !== false || !["macro", "pdf_active", "rtf_object"].includes(item.type)).map((item) => finding(item));
|
|
9205
9225
|
const [links, authentication, checkedReputation, dnsbl, virusScan, toxicity, nsfw] = await Promise.all([
|
|
9206
9226
|
this.checkLinks(checkedLinks, config, extracted.hosts, fromDomain(mail)),
|
|
@@ -9321,6 +9341,13 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
|
|
|
9321
9341
|
// scanned, as {filename, reason}: the same on every try, so not a
|
|
9322
9342
|
// failure.
|
|
9323
9343
|
virusScanSkipped: virusScan.skipped,
|
|
9344
|
+
// Attached messages not opened, past the limits attached messages
|
|
9345
|
+
// are read within (nested more than three deep, more than 100
|
|
9346
|
+
// attachments in them, or more than 25 MB of them), as
|
|
9347
|
+
// {filename, reason}: their attachments were not inspected. Not
|
|
9348
|
+
// scored (forwarded messages can be nested so), for a caller that
|
|
9349
|
+
// needs a message read whole, e.g. to block what it carries.
|
|
9350
|
+
unopenedAttachments: unopened,
|
|
9324
9351
|
// Whether the message has more text than phishing.maxLength, whose
|
|
9325
9352
|
// links were not read.
|
|
9326
9353
|
linksTruncated,
|
|
@@ -9369,6 +9396,7 @@ export {
|
|
|
9369
9396
|
scoreResults,
|
|
9370
9397
|
segmentWords,
|
|
9371
9398
|
spamHeaders,
|
|
9372
|
-
train
|
|
9399
|
+
train,
|
|
9400
|
+
unwrapLink
|
|
9373
9401
|
};
|
|
9374
9402
|
//# sourceMappingURL=index.js.map
|