spamscanner 7.0.12 → 7.0.13
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/index.cjs +66 -25
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/esm/cli.js +65 -24
- package/dist/esm/cli.js.map +2 -2
- package/dist/esm/index.js +65 -24
- package/dist/esm/index.js.map +2 -2
- package/package.json +1 -1
package/dist/esm/cli.js
CHANGED
|
@@ -6310,7 +6310,7 @@ import net from "node:net";
|
|
|
6310
6310
|
var package_default = {
|
|
6311
6311
|
name: "spamscanner",
|
|
6312
6312
|
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.",
|
|
6313
|
-
version: "7.0.
|
|
6313
|
+
version: "7.0.13",
|
|
6314
6314
|
author: "Forward Email (https://forwardemail.net)",
|
|
6315
6315
|
bin: {
|
|
6316
6316
|
spamscanner: "./dist/esm/cli.js"
|
|
@@ -8398,8 +8398,8 @@ var MAX_EMBEDDED_LENGTH = 10 * 1024 * 1024;
|
|
|
8398
8398
|
var MAX_ATTACHED = 100;
|
|
8399
8399
|
var MAX_ATTACHED_BYTES = 50 * 1024 * 1024;
|
|
8400
8400
|
var MAX_READ_TIME = 5e3;
|
|
8401
|
-
var MAX_CARD_BYTES = 4 * 1024 * 1024;
|
|
8402
8401
|
var MAX_SHOWN_BYTES = 1024 * 1024;
|
|
8402
|
+
var MAX_CARD_TEXT = 8 * 1024 * 1024;
|
|
8403
8403
|
function isHostCode2(code) {
|
|
8404
8404
|
return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 45 || code === 46;
|
|
8405
8405
|
}
|
|
@@ -8519,9 +8519,22 @@ function* attributesOf(html) {
|
|
|
8519
8519
|
}
|
|
8520
8520
|
}
|
|
8521
8521
|
var MAX_VALUE2 = 1024 * 1024;
|
|
8522
|
+
var MAX_REFERENCES = 64 * 1024;
|
|
8523
|
+
function isCut(value) {
|
|
8524
|
+
if (value.length <= MAX_VALUE2) {
|
|
8525
|
+
return false;
|
|
8526
|
+
}
|
|
8527
|
+
let references = 0;
|
|
8528
|
+
for (let index = value.indexOf("&"); index !== -1; index = value.indexOf("&", index + 1)) {
|
|
8529
|
+
if (++references > MAX_REFERENCES) {
|
|
8530
|
+
return true;
|
|
8531
|
+
}
|
|
8532
|
+
}
|
|
8533
|
+
return false;
|
|
8534
|
+
}
|
|
8522
8535
|
var LONG_VALUE = 64 * 1024;
|
|
8523
8536
|
function linksOf(name, value, { add, embed, cut }) {
|
|
8524
|
-
if (value
|
|
8537
|
+
if (isCut(value)) {
|
|
8525
8538
|
cut();
|
|
8526
8539
|
value = value.slice(0, MAX_VALUE2);
|
|
8527
8540
|
}
|
|
@@ -8944,14 +8957,27 @@ function quotedPrintableValues(text) {
|
|
|
8944
8957
|
}
|
|
8945
8958
|
return out.join("\n");
|
|
8946
8959
|
}
|
|
8947
|
-
function
|
|
8960
|
+
function hasQuotedPrintable(text) {
|
|
8961
|
+
const unfolded = text.replaceAll(FOLD, "");
|
|
8962
|
+
if (!/quoted-printable/i.test(unfolded)) {
|
|
8963
|
+
return false;
|
|
8964
|
+
}
|
|
8965
|
+
return unfolded.split(LINE_BREAK).some((line) => {
|
|
8966
|
+
const colon = valueColon(line);
|
|
8967
|
+
return colon !== -1 && QUOTED_PRINTABLE.test(line.slice(0, colon));
|
|
8968
|
+
});
|
|
8969
|
+
}
|
|
8970
|
+
function shownText(text, full = true) {
|
|
8948
8971
|
const unfolded = text.replaceAll(FOLD, "");
|
|
8949
8972
|
const shown = /* @__PURE__ */ new Set([text, readEscapes(unfolded)]);
|
|
8950
|
-
|
|
8951
|
-
if (
|
|
8952
|
-
|
|
8953
|
-
if (
|
|
8954
|
-
shown.add(
|
|
8973
|
+
let decoded = unfolded;
|
|
8974
|
+
if (full) {
|
|
8975
|
+
decoded = quotedPrintableValues(unfolded);
|
|
8976
|
+
if (decoded !== unfolded) {
|
|
8977
|
+
shown.add(readEscapes(decoded));
|
|
8978
|
+
if (unfolded !== text) {
|
|
8979
|
+
shown.add(quotedPrintableValues(text));
|
|
8980
|
+
}
|
|
8955
8981
|
}
|
|
8956
8982
|
}
|
|
8957
8983
|
if (/\\[^nN,;\\]/.test(decoded)) {
|
|
@@ -8984,40 +9010,50 @@ function htmlDescriptions(text) {
|
|
|
8984
9010
|
return html.join("\n");
|
|
8985
9011
|
}
|
|
8986
9012
|
var BINARY = /;\s*(?:(?:encoding\s*=\s*)?"?(?:b|base64)"?|value\s*=\s*"?binary"?)\s*(?:;|$)/i;
|
|
8987
|
-
function withoutBinaryValues(text) {
|
|
9013
|
+
function withoutBinaryValues(text, limit = Number.POSITIVE_INFINITY) {
|
|
8988
9014
|
if (!/base64|binary|encoding\s*=\s*"?b|;\s*b\s*[;:]/i.test(text)) {
|
|
8989
9015
|
return text;
|
|
8990
9016
|
}
|
|
8991
9017
|
const lines2 = text.split(LINE_BREAK);
|
|
8992
9018
|
const out = [];
|
|
9019
|
+
let length = 0;
|
|
9020
|
+
const push = (line) => {
|
|
9021
|
+
out.push(line);
|
|
9022
|
+
length += line.length + 1;
|
|
9023
|
+
};
|
|
8993
9024
|
for (let i = 0; i < lines2.length; i++) {
|
|
9025
|
+
if (length > limit) {
|
|
9026
|
+
break;
|
|
9027
|
+
}
|
|
8994
9028
|
const line = lines2[i];
|
|
8995
9029
|
const colon = valueColon(line);
|
|
8996
9030
|
if (colon === -1 || !BINARY.test(line.slice(0, colon))) {
|
|
8997
|
-
|
|
9031
|
+
push(line);
|
|
8998
9032
|
continue;
|
|
8999
9033
|
}
|
|
9000
|
-
|
|
9034
|
+
let base64 = /^[\w+/=\s]*$/.test(line.slice(colon + 1));
|
|
9001
9035
|
let next = i + 1;
|
|
9002
9036
|
while (next < lines2.length && (lines2[next].startsWith(" ") || lines2[next].startsWith(" "))) {
|
|
9003
|
-
|
|
9037
|
+
base64 &&= /^[\w+/=\s]*$/.test(lines2[next]);
|
|
9038
|
+
next++;
|
|
9004
9039
|
}
|
|
9005
|
-
if (!
|
|
9040
|
+
if (!base64) {
|
|
9006
9041
|
for (let index = i; index < next; index++) {
|
|
9007
|
-
|
|
9042
|
+
push(lines2[index]);
|
|
9008
9043
|
}
|
|
9009
9044
|
i = next - 1;
|
|
9010
9045
|
continue;
|
|
9011
9046
|
}
|
|
9012
9047
|
while (next < lines2.length && lines2[next] !== "" && /^[\w+/=]+$/.test(lines2[next])) {
|
|
9013
|
-
|
|
9048
|
+
next++;
|
|
9014
9049
|
}
|
|
9050
|
+
const end = next;
|
|
9015
9051
|
if (next < lines2.length && lines2[next] === "") {
|
|
9016
9052
|
next++;
|
|
9017
9053
|
}
|
|
9018
|
-
i = next - 1;
|
|
9019
9054
|
const name = line.slice(0, colon).split(";")[0].split(".").pop().trim().toLowerCase();
|
|
9020
|
-
|
|
9055
|
+
push(MEDIA_PROPERTIES.has(name) ? line.slice(0, colon + 1) : line.slice(0, colon + 1) + Buffer13.from([line.slice(colon + 1), ...lines2.slice(i + 1, end)].join("").replaceAll(/\s/g, ""), "base64").toString("utf8"));
|
|
9056
|
+
i = next - 1;
|
|
9021
9057
|
}
|
|
9022
9058
|
return out.join("\n");
|
|
9023
9059
|
}
|
|
@@ -9102,16 +9138,20 @@ function* documentsOf(attachments, state, late) {
|
|
|
9102
9138
|
yield { text };
|
|
9103
9139
|
continue;
|
|
9104
9140
|
}
|
|
9105
|
-
let readable = withoutBinaryValues(text
|
|
9106
|
-
if (
|
|
9141
|
+
let readable = withoutBinaryValues(text, MAX_CARD_TEXT);
|
|
9142
|
+
if (readable.length > MAX_CARD_TEXT) {
|
|
9143
|
+
state.tooComplex = true;
|
|
9144
|
+
readable = readable.slice(0, MAX_CARD_TEXT);
|
|
9145
|
+
}
|
|
9146
|
+
const full = readable.length <= MAX_SHOWN_BYTES;
|
|
9147
|
+
if (!full && hasQuotedPrintable(readable)) {
|
|
9107
9148
|
state.tooComplex = true;
|
|
9108
|
-
readable = readable.slice(0, MAX_SHOWN_BYTES);
|
|
9109
9149
|
}
|
|
9110
9150
|
if (late()) {
|
|
9111
9151
|
state.incomplete = true;
|
|
9112
9152
|
return;
|
|
9113
9153
|
}
|
|
9114
|
-
yield { text: shownText(readable) };
|
|
9154
|
+
yield { text: shownText(readable, full) };
|
|
9115
9155
|
if (late()) {
|
|
9116
9156
|
state.incomplete = true;
|
|
9117
9157
|
return;
|
|
@@ -9300,10 +9340,11 @@ function carriedLinkHosts(mail, {
|
|
|
9300
9340
|
if (done() || spend(link.length)) {
|
|
9301
9341
|
break;
|
|
9302
9342
|
}
|
|
9303
|
-
|
|
9343
|
+
const cut = isCut(link);
|
|
9344
|
+
if (cut) {
|
|
9304
9345
|
state.tooComplex = true;
|
|
9305
9346
|
}
|
|
9306
|
-
add(decodeEntities(link.slice(0, MAX_VALUE2), { attribute: true }));
|
|
9347
|
+
add(decodeEntities(cut ? link.slice(0, MAX_VALUE2) : link, { attribute: true }));
|
|
9307
9348
|
}
|
|
9308
9349
|
}
|
|
9309
9350
|
for (const chunk2 of chunksOf(text)) {
|