spamscanner 7.0.12 → 7.0.14

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.
@@ -6358,8 +6358,20 @@ function* attributesOf(html2) {
6358
6358
  TAG_START.lastIndex = Math.max(i, match.index + 1);
6359
6359
  }
6360
6360
  }
6361
+ function isCut(value) {
6362
+ if (value.length <= MAX_VALUE2) {
6363
+ return false;
6364
+ }
6365
+ let references = 0;
6366
+ for (let index = value.indexOf("&"); index !== -1; index = value.indexOf("&", index + 1)) {
6367
+ if (++references > MAX_REFERENCES) {
6368
+ return true;
6369
+ }
6370
+ }
6371
+ return false;
6372
+ }
6361
6373
  function linksOf(name, value, { add, embed, cut }) {
6362
- if (value.length > MAX_VALUE2) {
6374
+ if (isCut(value)) {
6363
6375
  cut();
6364
6376
  value = value.slice(0, MAX_VALUE2);
6365
6377
  }
@@ -6772,14 +6784,27 @@ function quotedPrintableValues(text) {
6772
6784
  }
6773
6785
  return out.join("\n");
6774
6786
  }
6775
- function shownText(text) {
6787
+ function hasQuotedPrintable(text) {
6788
+ const unfolded = text.replaceAll(FOLD, "");
6789
+ if (!/quoted-printable/i.test(unfolded)) {
6790
+ return false;
6791
+ }
6792
+ return unfolded.split(LINE_BREAK).some((line) => {
6793
+ const colon = valueColon(line);
6794
+ return colon !== -1 && QUOTED_PRINTABLE.test(line.slice(0, colon));
6795
+ });
6796
+ }
6797
+ function shownText(text, full = true) {
6776
6798
  const unfolded = text.replaceAll(FOLD, "");
6777
6799
  const shown = /* @__PURE__ */ new Set([text, readEscapes(unfolded)]);
6778
- const decoded = quotedPrintableValues(unfolded);
6779
- if (decoded !== unfolded) {
6780
- shown.add(readEscapes(decoded));
6781
- if (unfolded !== text) {
6782
- shown.add(quotedPrintableValues(text));
6800
+ let decoded = unfolded;
6801
+ if (full) {
6802
+ decoded = quotedPrintableValues(unfolded);
6803
+ if (decoded !== unfolded) {
6804
+ shown.add(readEscapes(decoded));
6805
+ if (unfolded !== text) {
6806
+ shown.add(quotedPrintableValues(text));
6807
+ }
6783
6808
  }
6784
6809
  }
6785
6810
  if (/\\[^nN,;\\]/.test(decoded)) {
@@ -6811,40 +6836,50 @@ function htmlDescriptions(text) {
6811
6836
  }
6812
6837
  return html2.join("\n");
6813
6838
  }
6814
- function withoutBinaryValues(text) {
6839
+ function withoutBinaryValues(text, limit = Number.POSITIVE_INFINITY) {
6815
6840
  if (!/base64|binary|encoding\s*=\s*"?b|;\s*b\s*[;:]/i.test(text)) {
6816
6841
  return text;
6817
6842
  }
6818
6843
  const lines2 = text.split(LINE_BREAK);
6819
6844
  const out = [];
6845
+ let length = 0;
6846
+ const push = (line) => {
6847
+ out.push(line);
6848
+ length += line.length + 1;
6849
+ };
6820
6850
  for (let i = 0; i < lines2.length; i++) {
6851
+ if (length > limit) {
6852
+ break;
6853
+ }
6821
6854
  const line = lines2[i];
6822
6855
  const colon = valueColon(line);
6823
6856
  if (colon === -1 || !BINARY.test(line.slice(0, colon))) {
6824
- out.push(line);
6857
+ push(line);
6825
6858
  continue;
6826
6859
  }
6827
- const value = [line.slice(colon + 1)];
6860
+ let base64 = /^[\w+/=\s]*$/.test(line.slice(colon + 1));
6828
6861
  let next = i + 1;
6829
6862
  while (next < lines2.length && (lines2[next].startsWith(" ") || lines2[next].startsWith(" "))) {
6830
- value.push(lines2[next++]);
6863
+ base64 &&= /^[\w+/=\s]*$/.test(lines2[next]);
6864
+ next++;
6831
6865
  }
6832
- if (!value.every((part) => /^[\w+/=\s]*$/.test(part))) {
6866
+ if (!base64) {
6833
6867
  for (let index = i; index < next; index++) {
6834
- out.push(lines2[index]);
6868
+ push(lines2[index]);
6835
6869
  }
6836
6870
  i = next - 1;
6837
6871
  continue;
6838
6872
  }
6839
6873
  while (next < lines2.length && lines2[next] !== "" && /^[\w+/=]+$/.test(lines2[next])) {
6840
- value.push(lines2[next++]);
6874
+ next++;
6841
6875
  }
6876
+ const end = next;
6842
6877
  if (next < lines2.length && lines2[next] === "") {
6843
6878
  next++;
6844
6879
  }
6845
- i = next - 1;
6846
6880
  const name = line.slice(0, colon).split(";")[0].split(".").pop().trim().toLowerCase();
6847
- out.push(MEDIA_PROPERTIES.has(name) ? line.slice(0, colon + 1) : line.slice(0, colon + 1) + import_node_buffer7.Buffer.from(value.join("").replaceAll(/\s/g, ""), "base64").toString("utf8"));
6881
+ push(MEDIA_PROPERTIES.has(name) ? line.slice(0, colon + 1) : line.slice(0, colon + 1) + import_node_buffer7.Buffer.from([line.slice(colon + 1), ...lines2.slice(i + 1, end)].join("").replaceAll(/\s/g, ""), "base64").toString("utf8"));
6882
+ i = next - 1;
6848
6883
  }
6849
6884
  return out.join("\n");
6850
6885
  }
@@ -6924,16 +6959,20 @@ function* documentsOf(attachments, state, late) {
6924
6959
  yield { text };
6925
6960
  continue;
6926
6961
  }
6927
- let readable = withoutBinaryValues(text.length > MAX_CARD_BYTES ? text.slice(0, MAX_CARD_BYTES) : text);
6928
- if (text.length > MAX_CARD_BYTES || readable.length > MAX_SHOWN_BYTES) {
6962
+ let readable = withoutBinaryValues(text, MAX_CARD_TEXT);
6963
+ if (readable.length > MAX_CARD_TEXT) {
6964
+ state.tooComplex = true;
6965
+ readable = readable.slice(0, MAX_CARD_TEXT);
6966
+ }
6967
+ const full = readable.length <= MAX_SHOWN_BYTES;
6968
+ if (!full && hasQuotedPrintable(readable)) {
6929
6969
  state.tooComplex = true;
6930
- readable = readable.slice(0, MAX_SHOWN_BYTES);
6931
6970
  }
6932
6971
  if (late()) {
6933
6972
  state.incomplete = true;
6934
6973
  return;
6935
6974
  }
6936
- yield { text: shownText(readable) };
6975
+ yield { text: shownText(readable, full) };
6937
6976
  if (late()) {
6938
6977
  state.incomplete = true;
6939
6978
  return;
@@ -7121,10 +7160,11 @@ function carriedLinkHosts(mail, {
7121
7160
  if (done() || spend(link.length)) {
7122
7161
  break;
7123
7162
  }
7124
- if (link.length > MAX_VALUE2) {
7163
+ const cut = isCut(link);
7164
+ if (cut) {
7125
7165
  state.tooComplex = true;
7126
7166
  }
7127
- add(decodeEntities(link.slice(0, MAX_VALUE2), { attribute: true }));
7167
+ add(decodeEntities(cut ? link.slice(0, MAX_VALUE2) : link, { attribute: true }));
7128
7168
  }
7129
7169
  }
7130
7170
  for (const chunk2 of chunksOf(text)) {
@@ -7159,7 +7199,7 @@ function carriedLinkHosts(mail, {
7159
7199
  incomplete: state.incomplete
7160
7200
  };
7161
7201
  }
7162
- var import_node_buffer7, import_node_net4, import_tldts4, LINK_ATTRIBUTES, TAG_START, URL_START, CSS_URL, CSS_IMPORT, LENIENT, IN_WORD2, NAMESPACES, MAX_HOST, MAX_SEEN, TRAILING, DECODE_CHUNK, CHUNK_OVERLAP, MAX_EMBED_DEPTH, MAX_EMBEDDED, MAX_EMBEDDED_LENGTH, MAX_ATTACHED, MAX_ATTACHED_BYTES, MAX_READ_TIME, MAX_CARD_BYTES, MAX_SHOWN_BYTES, MAX_VALUE2, LONG_VALUE, MAX_LENIENT_VALUE, UNQUOTED_END, UTF16LE, UTF16BE, DATA_DOCUMENT, LINE_BREAK, FOLD, QUOTED_PRINTABLE, MEDIA_PROPERTIES, HTML_PROPERTIES, BINARY, HTML_TYPES, HTML_EXTENSIONS, CARD_TYPES, CARD_EXTENSIONS, MESSAGE_TYPES, LATE_EVERY;
7202
+ var import_node_buffer7, import_node_net4, import_tldts4, LINK_ATTRIBUTES, TAG_START, URL_START, CSS_URL, CSS_IMPORT, LENIENT, IN_WORD2, NAMESPACES, MAX_HOST, MAX_SEEN, TRAILING, DECODE_CHUNK, CHUNK_OVERLAP, MAX_EMBED_DEPTH, MAX_EMBEDDED, MAX_EMBEDDED_LENGTH, MAX_ATTACHED, MAX_ATTACHED_BYTES, MAX_READ_TIME, MAX_SHOWN_BYTES, MAX_CARD_TEXT, MAX_VALUE2, MAX_REFERENCES, LONG_VALUE, MAX_LENIENT_VALUE, UNQUOTED_END, UTF16LE, UTF16BE, DATA_DOCUMENT, LINE_BREAK, FOLD, QUOTED_PRINTABLE, MEDIA_PROPERTIES, HTML_PROPERTIES, BINARY, HTML_TYPES, HTML_EXTENSIONS, CARD_TYPES, CARD_EXTENSIONS, MESSAGE_TYPES, LATE_EVERY;
7163
7203
  var init_links = __esm({
7164
7204
  "src/links.js"() {
7165
7205
  import_node_buffer7 = require("node:buffer");
@@ -7206,9 +7246,10 @@ var init_links = __esm({
7206
7246
  MAX_ATTACHED = 100;
7207
7247
  MAX_ATTACHED_BYTES = 50 * 1024 * 1024;
7208
7248
  MAX_READ_TIME = 5e3;
7209
- MAX_CARD_BYTES = 4 * 1024 * 1024;
7210
7249
  MAX_SHOWN_BYTES = 1024 * 1024;
7250
+ MAX_CARD_TEXT = 8 * 1024 * 1024;
7211
7251
  MAX_VALUE2 = 1024 * 1024;
7252
+ MAX_REFERENCES = 64 * 1024;
7212
7253
  LONG_VALUE = 64 * 1024;
7213
7254
  MAX_LENIENT_VALUE = 4096;
7214
7255
  UNQUOTED_END = /* @__PURE__ */ new Set(["<", ">", '"', "'", "`", "="]);
@@ -8891,7 +8932,7 @@ var init_package = __esm({
8891
8932
  package_default = {
8892
8933
  name: "spamscanner",
8893
8934
  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.",
8894
- version: "7.0.12",
8935
+ version: "7.0.14",
8895
8936
  author: "Forward Email (https://forwardemail.net)",
8896
8937
  bin: {
8897
8938
  spamscanner: "./dist/esm/cli.js"