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/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 && depth < MAX_NESTED_DEPTH && Buffer2.isBuffer(content) && content.length <= budget) {
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.7",
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 attached = await expandAttachments(mail.attachments, parseErrors);
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