diff --git a/crates/spam-filter/src/analysis/from.rs b/crates/spam-filter/src/analysis/from.rs index a3f292c5..a98e7c70 100644 --- a/crates/spam-filter/src/analysis/from.rs +++ b/crates/spam-filter/src/analysis/from.rs @@ -184,7 +184,7 @@ impl SpamFilterAnalyzeFrom for Server { && !from_addr.address.is_empty() && !from_raw_utf8.contains(" <") { - ctx.result.add_tag("R_NO_SPACE_IN_FROM"); + ctx.result.add_tag("NO_SPACE_IN_FROM"); } // Check whether read confirmation address is different to from address diff --git a/crates/spam-filter/src/analysis/headers.rs b/crates/spam-filter/src/analysis/headers.rs index 0fe6449f..c7d2dbf9 100644 --- a/crates/spam-filter/src/analysis/headers.rs +++ b/crates/spam-filter/src/analysis/headers.rs @@ -36,7 +36,7 @@ impl SpamFilterAnalyzeHeaders for Server { } else if ch == '-' { tag.push('_'); } else { - tag.push('X'); + tag.push(' '); } } ctx.result.add_tag(tag); diff --git a/crates/spam-filter/src/analysis/html.rs b/crates/spam-filter/src/analysis/html.rs index 4329f2c2..21f51d83 100644 --- a/crates/spam-filter/src/analysis/html.rs +++ b/crates/spam-filter/src/analysis/html.rs @@ -38,11 +38,6 @@ impl SpamFilterAnalyzeHtml for Server { }) { ctx.result.add_tag("MIME_HTML_ONLY"); } - let mut last_href: Option = None; - let mut html_img_words = 0; - let mut html_text_chars = 0; - let mut in_head: i32 = 0; - let mut in_body: i32 = 0; for (part_id, part) in ctx.output.text_parts.iter().enumerate() { let is_body_part = ctx.input.message.text_body.contains(&part_id) @@ -58,7 +53,12 @@ impl SpamFilterAnalyzeHtml for Server { } else { continue; }; + let mut has_link_to_img = false; + let mut last_href: Option = None; + let mut html_img_words = 0; + let mut in_head: i32 = 0; + let mut in_body: i32 = 0; for token in html_tokens { match token { @@ -121,6 +121,11 @@ impl SpamFilterAnalyzeHtml for Server { { // Has Data URI encoding ctx.result.add_tag("HAS_DATA_URI"); + } else if src.starts_with("https://") + || src.starts_with("http://") + { + // Has external image + ctx.result.add_tag("HAS_EXTERNAL_IMG"); } continue; } @@ -139,8 +144,13 @@ impl SpamFilterAnalyzeHtml for Server { } let dimensions = img_width + img_height; - if last_href.is_some() && dimensions >= 210 { - has_link_to_img = true; + if last_href.is_some() { + if dimensions >= 210 { + ctx.result.add_tag("HAS_LINK_TO_LARGE_IMG"); + has_link_to_img = true; + } else { + ctx.result.add_tag("HAS_LINK_TO_IMG"); + } } if dimensions > 100 { @@ -266,10 +276,6 @@ impl SpamFilterAnalyzeHtml for Server { } } } - - if is_body_part { - html_text_chars += text.chars().filter(|t| t.is_alphanumeric()).count(); - } } _ => (), } @@ -281,30 +287,19 @@ impl SpamFilterAnalyzeHtml for Server { ctx.result.add_tag("HTML_UNBALANCED_TAG"); } - if has_link_to_img { - match html_text_chars { - 0..1024 => { - ctx.result.add_tag("HTML_SHORT_LINK_IMG_1"); - } - 1024..1536 => { - ctx.result.add_tag("HTML_SHORT_LINK_IMG_2"); - } - 1536..2048 => { - ctx.result.add_tag("HTML_SHORT_LINK_IMG_3"); - } - _ => (), - } - } - let mut html_words = 0; let mut html_uris = 0; + let mut html_text_chars = 0; for token in tokens { match token { - TokenType::Alphabetic(_) - | TokenType::Alphanumeric(_) - | TokenType::Email(_) => { + TokenType::Alphabetic(s) | TokenType::Alphanumeric(s) => { html_words += 1; + html_text_chars += s.len(); + } + TokenType::Email(s) => { + html_words += 1; + html_text_chars += s.address.len(); } TokenType::Url(_) | TokenType::UrlNoScheme(_) => { html_uris += 1; @@ -313,6 +308,19 @@ impl SpamFilterAnalyzeHtml for Server { } } + match html_text_chars { + 0..1024 => { + ctx.result.add_tag("HTML_SHORT_1"); + } + 1024..1536 => { + ctx.result.add_tag("HTML_SHORT_2"); + } + 1536..2048 => { + ctx.result.add_tag("HTML_SHORT_3"); + } + _ => (), + } + if (!has_link_to_img || html_text_chars >= 2048) && (html_img_words as f64 / (html_words as f64 + html_img_words as f64) > 0.5) { diff --git a/crates/spam-filter/src/analysis/mime.rs b/crates/spam-filter/src/analysis/mime.rs index 1ea34ad5..03ac3af4 100644 --- a/crates/spam-filter/src/analysis/mime.rs +++ b/crates/spam-filter/src/analysis/mime.rs @@ -217,7 +217,7 @@ impl SpamFilterAnalyzeMime for Server { && (!text_part_words.is_empty() || !html_part_words.is_empty()) && cosine_similarity(&text_part_words, &html_part_words) < 0.95 { - ctx.result.add_tag("R_PARTS_DIFFER"); + ctx.result.add_tag("PARTS_DIFFER"); } // Odd URI count between parts @@ -269,7 +269,7 @@ impl SpamFilterAnalyzeMime for Server { .map_or(false, |bytes| !bytes.is_ascii()) { // MIME text part claims to be ASCII but isn't - ctx.result.add_tag("R_BAD_CTE_7BIT"); + ctx.result.add_tag("BAD_CTE_7BIT"); } is_7bit = true; } @@ -292,7 +292,7 @@ impl SpamFilterAnalyzeMime for Server { .map_or(true, |c| c.is_empty()) { // Charset header is missing - ctx.result.add_tag("R_MISSING_CHARSET"); + ctx.result.add_tag("MISSING_CHARSET"); } if ctx @@ -310,7 +310,7 @@ impl SpamFilterAnalyzeMime for Server { }) { // Text part contains multiple scripts - ctx.result.add_tag("R_MIXED_CHARSET"); + ctx.result.add_tag("MIXED_CHARSET"); } has_text_part = true; diff --git a/crates/spam-filter/src/analysis/received.rs b/crates/spam-filter/src/analysis/received.rs index 8c7d8b91..82e176be 100644 --- a/crates/spam-filter/src/analysis/received.rs +++ b/crates/spam-filter/src/analysis/received.rs @@ -23,76 +23,64 @@ impl SpamFilterAnalyzeReceived for Server { let mut rcvd_count = 0; let mut rcvd_from_ip = 0; let mut tls_count = 0; - let mut has_ua = false; for header in ctx.input.message.headers() { - match &header.name { - HeaderName::Received => { - if !ctx - .input - .message - .raw_message() - .get(header.offset_start..header.offset_end) - .unwrap_or_default() - .is_ascii() - { - // Received headers have non-ASCII characters - ctx.result.add_tag("RCVD_ILLEGAL_CHARS"); - } - - if let Some(received) = header.value().as_received() { - let helo_domain = received.from().or_else(|| received.helo()); - let ip_rev = received.from_iprev(); - - if matches!(&helo_domain, Some(Host::Name(hostname)) if hostname.eq_ignore_ascii_case("user")) - { - // HELO domain is "user" - ctx.result.add_tag("RCVD_HELO_USER"); - } else if let (Some(Host::Name(helo_domain)), Some(ip_rev)) = - (helo_domain, ip_rev) - { - if helo_domain.to_lowercase() != ip_rev.to_lowercase() { - // HELO domain does not match PTR record - ctx.result.add_tag("FORGED_RCVD_TRAIL"); - } - } - - if let Some(delivered_for) = received.for_().map(|s| s.to_lowercase()) { - if ctx - .output - .all_recipients() - .any(|r| r.email.address == delivered_for) - { - // Recipient appears on Received trail - ctx.result.add_tag("PREVIOUSLY_DELIVERED"); - } - } - - if matches!(received.from, Some(Host::IpAddr(_))) { - // Received from an IP address rather than a FQDN - rcvd_from_ip += 1; - } - - if received.tls_version().is_some() { - // Received with TLS - tls_count += 1; - } - } else { - // Received header is not RFC 5322 compliant - ctx.result.add_tag("RCVD_UNPARSABLE"); - } - - rcvd_count += 1; + if let HeaderName::Received = &header.name { + if !ctx + .input + .message + .raw_message() + .get(header.offset_start..header.offset_end) + .unwrap_or_default() + .is_ascii() + { + // Received headers have non-ASCII characters + ctx.result.add_tag("RCVD_ILLEGAL_CHARS"); } - HeaderName::Other(name) => { - if !has_ua - && (name.eq_ignore_ascii_case("User-Agent") - || name.eq_ignore_ascii_case("X-Mailer")) + + if let Some(received) = header.value().as_received() { + let helo_domain = received.from().or_else(|| received.helo()); + let ip_rev = received.from_iprev(); + + if matches!(&helo_domain, Some(Host::Name(hostname)) if hostname.eq_ignore_ascii_case("user")) { - has_ua = true; + // HELO domain is "user" + ctx.result.add_tag("RCVD_HELO_USER"); + } else if let (Some(Host::Name(helo_domain)), Some(ip_rev)) = + (helo_domain, ip_rev) + { + if helo_domain.to_lowercase() != ip_rev.to_lowercase() { + // HELO domain does not match PTR record + ctx.result.add_tag("FORGED_RCVD_TRAIL"); + } } + + if let Some(delivered_for) = received.for_().map(|s| s.to_lowercase()) { + if ctx + .output + .all_recipients() + .any(|r| r.email.address == delivered_for) + { + // Recipient appears on Received trail + ctx.result.add_tag("PREVIOUSLY_DELIVERED"); + } + } + + if matches!(received.from, Some(Host::IpAddr(_))) { + // Received from an IP address rather than a FQDN + rcvd_from_ip += 1; + } + + if received.tls_version().is_some() { + // Received with TLS + tls_count += 1; + } + } else { + // Received header is not RFC 5322 compliant + ctx.result.add_tag("RCVD_UNPARSABLE"); } - _ => {} + + rcvd_count += 1; } } @@ -118,15 +106,6 @@ impl SpamFilterAnalyzeReceived for Server { match rcvd_count { 0 => { ctx.result.add_tag("RCVD_COUNT_ZERO"); - - // One received header in a message (currently zero - // but one header will be added later by the MTA) - ctx.result.add_tag("ONCE_RECEIVED"); - - // Message has been directly delivered from MUA to local MX - if has_ua { - ctx.result.add_tag("DIRECT_TO_MX"); - } } 1 => { ctx.result.add_tag("RCVD_COUNT_ONE"); diff --git a/crates/spam-filter/src/analysis/url.rs b/crates/spam-filter/src/analysis/url.rs index c0cb62e5..a1680dcd 100644 --- a/crates/spam-filter/src/analysis/url.rs +++ b/crates/spam-filter/src/analysis/url.rs @@ -145,7 +145,7 @@ impl SpamFilterAnalyzeUrl for Server { } if ch.is_obscured() { - ctx.result.add_tag("R_SUSPICIOUS_URL"); + ctx.result.add_tag("SUSPICIOUS_URL"); } } @@ -159,7 +159,7 @@ impl SpamFilterAnalyzeUrl for Server { url_parsed } else { // URL could not be parsed - ctx.result.add_tag("R_UNPARSABLE_URL"); + ctx.result.add_tag("UNPARSABLE_URL"); continue; }; let host_sld = url_parsed.host.sld_or_default(); @@ -260,7 +260,7 @@ impl SpamFilterAnalyzeUrl for Server { .await; } else { // URL is an ip address - ctx.result.add_tag("R_SUSPICIOUS_URL"); + ctx.result.add_tag("SUSPICIOUS_URL"); } // Check URL DNSBL diff --git a/tests/resources/smtp/antispam/combined.test b/tests/resources/smtp/antispam/combined.test index d5f65693..c23f5b0a 100644 --- a/tests/resources/smtp/antispam/combined.test +++ b/tests/resources/smtp/antispam/combined.test @@ -6,7 +6,7 @@ spf.result none spf_ehlo.result none dmarc.result none remote_ip 195.210.29.48 -expect_header X-Spam-Result: ARC_NA (0.00), DKIM_NA (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_DATA_URI (0.00), MID_RHS_MATCH_ENV_FROM (0.00), RCPT_COUNT_ONE (0.00), RCVD_COUNT_ZERO (0.00), SPF_NA (0.00), SUBJECT_ENDS_EXCLAIM (0.00), TO_DN_NONE (0.00), TO_MATCH_ENVRCPT_ALL (0.00), ONCE_RECEIVED (0.10), RCVD_NO_TLS_LAST (0.10), MIME_HTML_ONLY (0.20), HELO_NORES_A_OR_MX (0.30), AUTH_NA (1.00), DATE_IN_PAST (1.00), DMARC_NA (1.00), MID_RHS_MATCH_FROM (1.00), FROMHOST_NORES_A_OR_MX (1.50), HTML_SHORT_LINK_IMG_1 (2.00), RDNS_NONE (2.00), PYZOR (3.50) +expect_header X-Spam-Result: ARC_NA (0.00), DKIM_NA (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_DATA_URI (0.00), HAS_LINK_TO_LARGE_IMG (0.00), HTML_SHORT_1 (0.00), MID_RHS_MATCH_ENV_FROM (0.00), RCPT_COUNT_ONE (0.00), SPF_NA (0.00), SUBJECT_ENDS_EXCLAIM (0.00), TO_DN_NONE (0.00), TO_MATCH_ENVRCPT_ALL (0.00), RCVD_COUNT_ZERO (0.10), RCVD_NO_TLS_LAST (0.10), MIME_HTML_ONLY (0.20), HELO_NORES_A_OR_MX (0.30), AUTH_NA (1.00), DATE_IN_PAST (1.00), DMARC_NA (1.00), MID_RHS_MATCH_FROM (1.00), FROMHOST_NORES_A_OR_MX (1.50), HTML_SHORT_LINK_IMG_1 (2.00), RDNS_NONE (2.00), PYZOR (3.50) expect_header X-Spam-Status: Yes, score=13.70 From: Client Services @@ -49,7 +49,7 @@ dkim.domains tenthrevolution.com dmarc.result pass remote_ip 185.58.86.181 tls.version TLSv1.3 -expect_header X-Spam-Result: DMARC_POLICY_ALLOW (-0.50), DKIM_ALLOW (-0.20), SPF_ALLOW (-0.20), MIME_GOOD (-0.10), ARC_NA (0.00), DKIM_SIGNED (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_ATTACHMENT (0.00), RCPT_COUNT_ONE (0.00), RCVD_COUNT_THREE (0.00), TO_DN_EQ_ADDR_ALL (0.00), TO_MATCH_ENVRCPT_ALL (0.00), RCVD_NO_TLS_LAST (0.10), HELO_NORES_A_OR_MX (0.30), SUBJECT_ENDS_SPACES (0.50), URI_COUNT_ODD (0.50), DATE_IN_PAST (1.00), FORGED_RCVD_TRAIL (1.00), FROMHOST_NORES_A_OR_MX (1.50) +expect_header X-Spam-Result: DMARC_POLICY_ALLOW (-0.50), DKIM_ALLOW (-0.20), SPF_ALLOW (-0.20), MIME_GOOD (-0.10), ARC_NA (0.00), DKIM_SIGNED (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_ATTACHMENT (0.00), HTML_SHORT_2 (0.00), RCPT_COUNT_ONE (0.00), RCVD_COUNT_THREE (0.00), TO_DN_EQ_ADDR_ALL (0.00), TO_MATCH_ENVRCPT_ALL (0.00), RCVD_NO_TLS_LAST (0.10), HELO_NORES_A_OR_MX (0.30), SUBJECT_ENDS_SPACES (0.50), URI_COUNT_ODD (0.50), DATE_IN_PAST (1.00), FORGED_RCVD_TRAIL (1.00), FROMHOST_NORES_A_OR_MX (1.50) expect_header X-Spam-Status: No, score=3.90 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tenthrevolution.com; @@ -574,7 +574,7 @@ dmarc.result fail dmarc.policy reject remote_ip 51.89.165.39 tls.version TLS1_2 -expect_header X-Spam-Result: DKIM_ALLOW (-0.20), HAS_LIST_UNSUB (-0.01), ARC_NA (0.00), DKIM_SIGNED (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_REPLYTO (0.00), MID_RHS_MATCH_ENV_FROM (0.00), RCPT_COUNT_ONE (0.00), RCVD_COUNT_ZERO (0.00), REPLYTO_ADDR_EQ_FROM (0.00), REPLYTO_EQ_FROM (0.00), SPF_SOFTFAIL (0.00), TO_DN_NONE (0.00), TO_MATCH_ENVRCPT_ALL (0.00), ONCE_RECEIVED (0.10), RCVD_NO_TLS_LAST (0.10), HELO_NORES_A_OR_MX (0.30), DATE_IN_PAST (1.00), MID_RHS_MATCH_FROM (1.00), R_PARTS_DIFFER (1.00), FROMHOST_NORES_A_OR_MX (1.50), HTML_SHORT_LINK_IMG_1 (2.00), RDNS_NONE (2.00), VIOLATED_DIRECT_SPF (3.50), DMARC_POLICY_REJECT (4.00) +expect_header X-Spam-Result: DKIM_ALLOW (-0.20), HAS_LIST_UNSUB (-0.01), ARC_NA (0.00), DKIM_SIGNED (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_EXTERNAL_IMG (0.00), HAS_LINK_TO_LARGE_IMG (0.00), HAS_REPLYTO (0.00), HTML_SHORT_1 (0.00), MID_RHS_MATCH_ENV_FROM (0.00), RCPT_COUNT_ONE (0.00), REPLYTO_ADDR_EQ_FROM (0.00), REPLYTO_EQ_FROM (0.00), SPF_SOFTFAIL (0.00), TO_DN_NONE (0.00), TO_MATCH_ENVRCPT_ALL (0.00), RCVD_COUNT_ZERO (0.10), RCVD_NO_TLS_LAST (0.10), HELO_NORES_A_OR_MX (0.30), DATE_IN_PAST (1.00), MID_RHS_MATCH_FROM (1.00), PARTS_DIFFER (1.00), FROMHOST_NORES_A_OR_MX (1.50), HTML_SHORT_LINK_IMG_1 (2.00), RDNS_NONE (2.00), VIOLATED_DIRECT_SPF (3.50), DMARC_POLICY_REJECT (4.00) expect_header X-Spam-Status: Yes, score=16.29 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=sectionalism; d=grupokonecta.net; @@ -791,7 +791,7 @@ dmarc.result pass dmarc.policy reject remote_ip 52.103.64.5 tls.version TLS1_2 -expect_header X-Spam-Result: DMARC_POLICY_ALLOW (-0.50), DKIM_ALLOW (-0.20), SPF_ALLOW (-0.20), ARC_NA (0.00), ARC_SIGNED (0.00), DKIM_SIGNED (0.00), FREEMAIL_FROM (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_SEO_WORD (0.00), HAS_X_PRIO_ONE (0.00), MID_RHS_MATCH_ENV_FROMTLD (0.00), MID_RHS_MATCH_FROMTLD (0.00), RCPT_COUNT_ONE (0.00), RCPT_IN_BODY (0.00), RCVD_COUNT_TWO (0.00), TO_DN_EQ_ADDR_ALL (0.00), TO_MATCH_ENVRCPT_ALL (0.00), RCVD_NO_TLS_LAST (0.10), HELO_NORES_A_OR_MX (0.30), DATE_IN_PAST (1.00), HEADER_EMPTY_DELIMITER (1.00), FROMHOST_NORES_A_OR_MX (1.50), SEO_SPAM (5.00) +expect_header X-Spam-Result: DMARC_POLICY_ALLOW (-0.50), DKIM_ALLOW (-0.20), SPF_ALLOW (-0.20), ARC_NA (0.00), ARC_SIGNED (0.00), DKIM_SIGNED (0.00), FREEMAIL_FROM (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_SEO_WORD (0.00), HAS_X_PRIO_ONE (0.00), HTML_SHORT_1 (0.00), MID_RHS_MATCH_ENV_FROMTLD (0.00), MID_RHS_MATCH_FROMTLD (0.00), RCPT_COUNT_ONE (0.00), RCPT_IN_BODY (0.00), RCVD_COUNT_TWO (0.00), TO_DN_EQ_ADDR_ALL (0.00), TO_MATCH_ENVRCPT_ALL (0.00), RCVD_NO_TLS_LAST (0.10), HELO_NORES_A_OR_MX (0.30), DATE_IN_PAST (1.00), HEADER_EMPTY_DELIMITER (1.00), FROMHOST_NORES_A_OR_MX (1.50), SEO_SPAM (5.00) expect_header X-Spam-Status: Yes, score=8.00 Return-Path: @@ -1008,3 +1008,104 @@ om you.  --_000_SEYPR04MB74966F6A34B2B0AC5DA8E6DDFD0A2SEYPR04MB7496apcp_-- + +envelope_from marketing@landeray.com +envelope_to hello@stalw.art +helo_domain terminal4.landeray.com +iprev.result pass +dkim.result pass +dkim.domains outlook.com +spf.result pass +spf_ehlo.result pass +dmarc.result pass +dmarc.policy reject +remote_ip 173.224.123.255 +tls.version TLS1_2 +expect_header X-Spam-Result: DMARC_POLICY_ALLOW (-0.50), DKIM_ALLOW (-0.20), SPF_ALLOW (-0.20), ARC_NA (0.00), DKIM_SIGNED (0.00), FROM_EQ_ENV_FROM (0.00), FROM_HAS_DN (0.00), HAS_EXTERNAL_IMG (0.00), HAS_REPLYTO (0.00), HAS_X_PRIO_THREE (0.00), HTML_SHORT_1 (0.00), RCPT_COUNT_ONE (0.00), REPLYTO_DN_EQ_FROM_DN (0.00), REPLYTO_DOM_EQ_FROM_DOM (0.00), TO_DN_ALL (0.00), TO_EQ_FROM (0.00), RCVD_COUNT_ZERO (0.10), RCVD_NO_TLS_LAST (0.10), HELO_NORES_A_OR_MX (0.30), MID_RHS_NOT_FQDN (0.50), UNPARSABLE_URL (0.50), FROMHOST_NORES_A_OR_MX (1.50), DIRECT_TO_MX (2.00), FORGED_RECIPIENTS (2.00), SUBJ_ALL_CAPS (3.00) +expect_header X-Spam-Status: Yes, score=9.10 + +Return-Path: +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=default; d=landeray.com; + h=Message-ID:Reply-To:From:To:Subject:Date:MIME-Version:Content-Type; + i=marketing@landeray.com; + bh=nlE2QLcR2WpTkMQ4zUwkF7IBIw4eOeAhkKS/HXiMvSs=; + b=EwWEML/WtTA6nI6CuagI2LRBWAZuRHk6IJiDNA3R77dTpa80gHPkfzF3NBcKcVIiCYcf6i4HCniZ + bF4DT8VZrFfLCSykeL2t5FLjYwFcjXjX7qCvyaR0hLfyFRSCDO9RytMw8Q3WoODc/jMXWrdxYpqu + phP9D5Q/N8vzSneReGOCV62Me1ss947uc43zYZXvVwuXeyLbU0yPzJtTTEODFXmVp2Ul/M7w963E + UxzrNlTd/lEIUXVvxAOL2DKGxY8oXslevI0euO8E8TQms3SlY6LySkDia1h+N+mVpbPPeE3txZhS + LJwn2MBX59b+W3aMafAt4Ae/UVOOBlMT7FhhvA== +Message-ID: +Reply-To: "RESERVAS Y CONSULTAS" +From: "RESERVAS Y CONSULTAS" +To: "Marketing Online" +Subject: BRISZA ASIA TEMPORADA 2025 +Date: Fri, 3 Jan 2025 14:06:03 +0000 +Organization: CLIENTE-DISCOTECA +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="----=_NextPart_000_0E52_01DB5DE8.9FAFDC60" +X-Priority: 3 +X-MSMail-Priority: Normal +X-Mailer: Microsoft Outlook Express 6.00.2900.5512 +X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.5579 + +This is a multi-part message in MIME format. + +------=_NextPart_000_0E52_01DB5DE8.9FAFDC60 +Content-Type: text/plain; + charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable + +Consulta v=EDa WhatsApp: https://api.whatsapp.com/send?phone=3D5199504167= +6&text=3DDeseo realizar una reserva en BRISZA + + +Si no puedes visualizar la imagen haz click aqu=ED https://i.imgur.com/O1= +2POZ2.png + + + +Si esta informaci=F3n no es de su inter=E9s. Favor de escribirnos un corr= +eo en blanco a eliminarcorreo2016@gmail.com con el Asunto "REMOVER". Grac= +ias. + +------=_NextPart_000_0E52_01DB5DE8.9FAFDC60 +Content-Type: text/html; + charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable + + + + + + + + +
Consulta v=EDa WhatsApp: https://api.whatsapp.com/send?phone=3D51995= +041676&text=3DDeseo=20 +realizar una reserva en BRISZA
+
 
+
 
+
Si no puedes visualizar la image= +n haz click=20 +aqu=ED https://i.imgur.com/O12POZ2.png<= +/A>
+
 
+
3D""=20
+
 
+
+
Si esta informaci= +=F3n no es de=20 +su inter=E9s. Favor de escribirnos un correo en blanco a eliminarcorreo2016@gmail.com con el Asunto "REMOVER".=20 +Gracias.
+ +------=_NextPart_000_0E52_01DB5DE8.9FAFDC60-- diff --git a/tests/resources/smtp/antispam/from.test b/tests/resources/smtp/antispam/from.test index 16e4c53f..8fc2b76f 100644 --- a/tests/resources/smtp/antispam/from.test +++ b/tests/resources/smtp/antispam/from.test @@ -128,7 +128,7 @@ From: "=?iso-8859-1?Q?Die_Hasen_und_die_Fr=F6sche?=" Test envelope_from hello@domain.org -expect R_NO_SPACE_IN_FROM FROM_EQ_ENV_FROM FROM_HAS_DN +expect NO_SPACE_IN_FROM FROM_EQ_ENV_FROM FROM_HAS_DN From: "Hello" diff --git a/tests/resources/smtp/antispam/html.test b/tests/resources/smtp/antispam/html.test index d4504b1c..b0a50da4 100644 --- a/tests/resources/smtp/antispam/html.test +++ b/tests/resources/smtp/antispam/html.test @@ -1,4 +1,4 @@ -expect MIME_HTML_ONLY +expect MIME_HTML_ONLY HTML_SHORT_1 Message-Id: <4.2.0.58.20000519002557.00a88870@pop.example.com> X-Sender: dwsauder@pop.example.com (Unverified) @@ -36,7 +36,7 @@ wir."

-expect HTTP_TO_HTTPS +expect HTTP_TO_HTTPS HTML_SHORT_1 Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -56,7 +56,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect HTTP_TO_IP +expect HTTP_TO_IP HTML_SHORT_1 Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -79,7 +79,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect EXT_CSS +expect EXT_CSS HTML_SHORT_1 Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -100,7 +100,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect EXT_CSS +expect EXT_CSS HTML_SHORT_1 Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -121,7 +121,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect HTML_UNBALANCED_TAG +expect HTML_UNBALANCED_TAG HTML_SHORT_1 Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -142,7 +142,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect HTML_UNBALANCED_TAG +expect HTML_UNBALANCED_TAG HTML_SHORT_1 Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -163,7 +163,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect HTML_SHORT_LINK_IMG_1 +expect HTML_SHORT_LINK_IMG_1 HTML_SHORT_1 HAS_LINK_TO_LARGE_IMG Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -188,7 +188,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect BODY_URI_ONLY +expect BODY_URI_ONLY HTML_SHORT_1 Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -212,7 +212,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect HTML_TEXT_IMG_RATIO +expect HTML_TEXT_IMG_RATIO HTML_SHORT_1 Content-Type: multipart/alternative; boundary="=====================_714967308==_.ALT" @@ -238,7 +238,7 @@ Content-Transfer-Encoding: 8bit --=====================_714967308==_.ALT-- -expect HTML_META_REFRESH_URL MIME_HTML_ONLY +expect HTML_META_REFRESH_URL MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -250,7 +250,7 @@ Content-Transfer-Encoding: 8bit

some text

-expect MIME_HTML_ONLY +expect MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -262,7 +262,7 @@ Content-Transfer-Encoding: 8bit

some text

-expect HAS_DATA_URI DATA_URI_OBFU MIME_HTML_ONLY +expect HAS_DATA_URI DATA_URI_OBFU MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -275,7 +275,7 @@ Content-Transfer-Encoding: 8bit Click me for a hello message -expect HAS_DATA_URI MIME_HTML_ONLY +expect HAS_DATA_URI MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -289,7 +289,7 @@ Content-Transfer-Encoding: 8bit Click me for a hello message -expect PHISHING MIME_HTML_ONLY +expect PHISHING MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -298,7 +298,7 @@ Content-Transfer-Encoding: 8bit https://domain2.com/otherquery -expect MIME_HTML_ONLY +expect MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -307,7 +307,7 @@ Content-Transfer-Encoding: 8bit https://subdomain.domain1.co.uk/otherquery -expect PHISHING MIME_HTML_ONLY +expect PHISHING MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -316,7 +316,7 @@ Content-Transfer-Encoding: 8bit domain2.com/otherquery -expect MIME_HTML_ONLY +expect MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -325,7 +325,7 @@ Content-Transfer-Encoding: 8bit subdomain.domain1.co.uk/otherquery -expect MIME_HTML_ONLY +expect MIME_HTML_ONLY HTML_SHORT_1 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 8bit diff --git a/tests/resources/smtp/antispam/mime.test b/tests/resources/smtp/antispam/mime.test index 0b6bfe78..d886834d 100644 --- a/tests/resources/smtp/antispam/mime.test +++ b/tests/resources/smtp/antispam/mime.test @@ -33,7 +33,7 @@ Content-Type: text/html; charset="us-ascii" Test -expect R_BAD_CTE_7BIT SINGLE_SHORT_PART +expect BAD_CTE_7BIT SINGLE_SHORT_PART Content-Type: text/plain Content-Transfer-Encoding: 7bit @@ -41,7 +41,7 @@ MIME-Version: 1.0 Téstíng -expect R_MISSING_CHARSET SINGLE_SHORT_PART +expect MISSING_CHARSET SINGLE_SHORT_PART Content-Type: text/plain Content-Transfer-Encoding: 8bit @@ -139,7 +139,7 @@ culpa qui officia deserunt mollit anim id est laborum. --boundary-- -expect R_PARTS_DIFFER +expect PARTS_DIFFER MIME-Version: 1.0 Content-Type: multipart/alternative; @@ -353,7 +353,7 @@ this is a test --boundary-- -expect R_MIXED_CHARSET SINGLE_SHORT_PART +expect MIXED_CHARSET SINGLE_SHORT_PART Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit diff --git a/tests/resources/smtp/antispam/received.test b/tests/resources/smtp/antispam/received.test index 183d14c9..97d4bf42 100644 --- a/tests/resources/smtp/antispam/received.test +++ b/tests/resources/smtp/antispam/received.test @@ -50,10 +50,10 @@ Subject: test test -expect DIRECT_TO_MX ONCE_RECEIVED RCVD_COUNT_ZERO RCVD_NO_TLS_LAST +expect DIRECT_TO_MX RCVD_COUNT_ZERO RCVD_NO_TLS_LAST To: user@domain.com -X-Mailer: MUA +X-Mailer: MUA 1.2 Subject: test test diff --git a/tests/resources/smtp/antispam/recipient.test b/tests/resources/smtp/antispam/recipient.test index d56f90b6..2778a04e 100644 --- a/tests/resources/smtp/antispam/recipient.test +++ b/tests/resources/smtp/antispam/recipient.test @@ -58,7 +58,7 @@ Cc: goodbye@world.com Test -expect RCPT_COUNT_ZERO R_UNDISC_RCPT +expect RCPT_COUNT_ZERO UNDISC_RCPT To: Undisclosed recipients:; diff --git a/tests/resources/smtp/antispam/url.test b/tests/resources/smtp/antispam/url.test index 60d64481..a807dae5 100644 --- a/tests/resources/smtp/antispam/url.test +++ b/tests/resources/smtp/antispam/url.test @@ -10,7 +10,7 @@ Subject: test my site is https://url.org -expect R_SUSPICIOUS_URL +expect SUSPICIOUS_URL Subject: test @@ -28,7 +28,7 @@ Subject: test my site is https://www.xn--80ak6aa92e.com/ -expect R_UNPARSABLE_URL +expect UNPARSABLE_URL Subject: test diff --git a/tests/src/smtp/inbound/antispam.rs b/tests/src/smtp/inbound/antispam.rs index b6572112..428c2cc3 100644 --- a/tests/src/smtp/inbound/antispam.rs +++ b/tests/src/smtp/inbound/antispam.rs @@ -512,6 +512,7 @@ async fn antispam() { match test_name { "html" => { server.spam_filter_analyze_html(&mut spam_ctx).await; + server.spam_filter_analyze_rules(&mut spam_ctx).await; } "subject" => { server.spam_filter_analyze_headers(&mut spam_ctx).await; @@ -521,7 +522,11 @@ async fn antispam() { spam_ctx.result.tags.retain(|t| !t.starts_with("X_HDR_")); } "received" => { + server.spam_filter_analyze_headers(&mut spam_ctx).await; + spam_ctx.result.tags.retain(|t| t.starts_with("X_HDR_")); server.spam_filter_analyze_received(&mut spam_ctx).await; + server.spam_filter_analyze_rules(&mut spam_ctx).await; + spam_ctx.result.tags.retain(|t| !t.starts_with("X_HDR_")); } "messageid" => { server.spam_filter_analyze_message_id(&mut spam_ctx).await;