优化 AWS 邮件验证码过滤与有效字段输出
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
"""Offline classification and extraction regression tests."""
|
||||
|
||||
import email
|
||||
from email import policy
|
||||
from email.message import EmailMessage
|
||||
from email.utils import parsedate_to_datetime
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import unittest
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
import check_mail
|
||||
|
||||
|
||||
def sample(name, folder="参考邮件"):
|
||||
path = next((ROOT / folder).glob(name), None)
|
||||
if path is None:
|
||||
raise unittest.SkipTest("本地真实邮件样本未随仓库分发")
|
||||
message = email.message_from_bytes(path.read_bytes(), policy=policy.default)
|
||||
received = parsedate_to_datetime(check_mail.header_value(message, "Date"))
|
||||
return check_mail.extract(message, received)
|
||||
|
||||
|
||||
def synthetic(subject, body, rich=False):
|
||||
message = EmailMessage()
|
||||
message["Subject"] = subject
|
||||
message.set_content(body, subtype="html" if rich else "plain")
|
||||
return check_mail.extract(message, parsedate_to_datetime("Mon, 28 Sep 2026 12:00:00 +0800"))
|
||||
|
||||
|
||||
class ExtractionTests(unittest.TestCase):
|
||||
def test_four_forwarded_reference_messages(self):
|
||||
expected = {
|
||||
"*Account_on_Hold*": "账户验证或暂停",
|
||||
"*账户已暂停*": "账户验证或暂停",
|
||||
"*Abuse_Report*": "资源滥用报告",
|
||||
"*存在可疑活动*": "账号可疑活动警告",
|
||||
}
|
||||
for pattern, kind in expected.items():
|
||||
with self.subTest(pattern=pattern):
|
||||
record = sample(pattern)
|
||||
self.assertIsNotNone(record)
|
||||
self.assertEqual(record["type"], kind)
|
||||
self.assertNotEqual(record["warning"], check_mail.EMPTY)
|
||||
|
||||
def test_forwarded_abuse_report_keeps_explicit_account_and_resources(self):
|
||||
record = sample("*Abuse_Report*")
|
||||
self.assertRegex(record["account"], r"^\d{12}$")
|
||||
self.assertRegex(record["resources"], r"\bi-[0-9a-f]{8,17}\b")
|
||||
self.assertEqual(record["deadline"], check_mail.EMPTY)
|
||||
|
||||
def test_permanent_closure_confirmation_does_not_invent_body_facts(self):
|
||||
record = synthetic(
|
||||
"AWS Account Permanent Closure Confirmation",
|
||||
"This confirms your account is permanently closed.",
|
||||
)
|
||||
self.assertIsNotNone(record)
|
||||
self.assertEqual(record["type"], "账户永久关闭确认")
|
||||
self.assertEqual(record["account"], check_mail.EMPTY)
|
||||
self.assertEqual(record["warning"], check_mail.EMPTY)
|
||||
self.assertEqual(record["action"], check_mail.EMPTY)
|
||||
self.assertEqual(record["deadline"], check_mail.EMPTY)
|
||||
|
||||
def test_identity_verification_footer_does_not_imply_compromise(self):
|
||||
record = synthetic(
|
||||
"验证您的身份",
|
||||
"AWS 身份验证通知。为保护您的安全,如果您发现未授权访问,请联系支持团队。",
|
||||
)
|
||||
self.assertEqual(record["type"], "身份验证通知")
|
||||
self.assertEqual(record["warning"], check_mail.EMPTY)
|
||||
self.assertEqual(record["action"], check_mail.EMPTY)
|
||||
self.assertEqual(record["account"], check_mail.EMPTY)
|
||||
self.assertEqual(record["deadline"], check_mail.EMPTY)
|
||||
|
||||
def test_specific_subject_outweighs_quoted_phishing_report(self):
|
||||
record = synthetic(
|
||||
"AWS Account on Hold: Response Required",
|
||||
"Your AWS account has been placed on hold.\n--- Forwarded history ---\n"
|
||||
"An earlier AWS resource was implicated in hosting content that resembles a phishing website.",
|
||||
)
|
||||
self.assertEqual(record["type"], "账户验证或暂停")
|
||||
|
||||
def test_local_forwarded_login_code_is_filtered(self):
|
||||
self.assertIsNone(sample("*.eml", "过滤邮件"))
|
||||
|
||||
def test_login_codes_with_security_boilerplate_are_filtered(self):
|
||||
cases = [
|
||||
("FW: 验证您的身份", "AWS 登录验证。您的验证码是 123456,10 分钟后过期。"
|
||||
"您是否发现异常活动?如果您没有发起登录,请更换密码并启用 MFA。", False),
|
||||
("Fwd: AWS Verify your identity", "Your verification code is 123456. "
|
||||
"Use this code to sign in. If you notice suspicious activity or unauthorized access, "
|
||||
"change your password and enable multi-factor authentication (MFA).", False),
|
||||
("AWS authentication", "AWS one-time passcode: 654321. Use this code to log in. "
|
||||
"It expires in 10 minutes. Verify your identity.", False),
|
||||
("AWS 验证您的身份", "<div>请输入验证码</div><strong>123456</strong>"
|
||||
"<p>以完成登录。如果发现异常活动,请更换密码。</p>", True),
|
||||
("AWS verification code", "<p>Your security code:</p><b>123456</b>"
|
||||
"<p>It expires in 10 minutes. Use this code to sign in.</p>", True),
|
||||
]
|
||||
for subject, body, rich in cases:
|
||||
with self.subTest(subject=subject, rich=rich):
|
||||
self.assertIsNone(synthetic(subject, body, rich))
|
||||
|
||||
def test_real_risk_with_verification_or_mfa_is_retained(self):
|
||||
cases = [
|
||||
("AWS Account on Hold: Verification Required", "Your account has been placed on hold. "
|
||||
"Please upload your identity documents. MFA security code: 123456; use this code to sign in."),
|
||||
("AWS 身份验证", "您的账户已暂停。请上传身份证核验材料。验证码 123456,可用于登录。"),
|
||||
("AWS verify your identity", "Please submit proof of identity and a bank statement. "
|
||||
"Your verification code is 123456. Use this code to sign in."),
|
||||
("AWS Account Permanent Closure Confirmation", "Your account is permanently closed. "
|
||||
"Verification code: 123456. Use this code to sign in."),
|
||||
("AWS Security verification", "We have limited your ability to access AWS services. "
|
||||
"Your security code is 123456. Use this code to sign in."),
|
||||
("AWS Security warning", "Your access key has been compromised. "
|
||||
"Verification code: 123456. Use this code to sign in."),
|
||||
("AWS Account Suspended", "Verification code: 123456. Use this code to sign in."),
|
||||
("AWS Account Restricted", "Verification code: 123456. Use this code to sign in."),
|
||||
("AWS 账号受限", "验证码 123456,用于登录。"),
|
||||
]
|
||||
for subject, body in cases:
|
||||
with self.subTest(subject=subject):
|
||||
self.assertIsNotNone(synthetic(subject, body))
|
||||
|
||||
def test_conditional_security_tips_do_not_make_login_codes_into_alerts(self):
|
||||
cases = [
|
||||
("AWS verification code", "Your verification code is 987654. Use this code to sign in.\n"
|
||||
"If you think your access key has been compromised, change your password and enable MFA."),
|
||||
("AWS 验证码", "您的验证码是 987654,用于登录。\n如果您的账户已暂停,请联系支持。"),
|
||||
("AWS verification code", "Your verification code is 987654. Use this code to sign in. "
|
||||
"If your account is suspended, please submit identity documents to support."),
|
||||
("AWS verification code", "Your verification code is 987654. Use this code to sign in. "
|
||||
"If you need help, please provide identity documents to support."),
|
||||
("AWS 验证码", "验证码 987654,用于登录。如果您需要帮助,请提交身份证材料给支持团队。"),
|
||||
]
|
||||
for subject, body in cases:
|
||||
with self.subTest(subject=subject):
|
||||
self.assertIsNone(synthetic(subject, body))
|
||||
|
||||
def test_explicit_risk_and_nonresponse_consequences_survive_conditional_tips(self):
|
||||
english_code = "Your verification code is 987654. Use this code to sign in. "
|
||||
cases = [
|
||||
("AWS verification code", english_code +
|
||||
"If you think your access key has been compromised, enable MFA. "
|
||||
"Your account has been placed on hold."),
|
||||
("AWS verification code", english_code +
|
||||
"Your account has been placed on hold, if you think your access key has been compromised, enable MFA."),
|
||||
("AWS verification code", english_code +
|
||||
"If you think your access key has been compromised, enable MFA, your account has been suspended."),
|
||||
("AWS 验证码", "验证码 987654,用于登录。如果您的账户已暂停,请联系支持。您的账户已受限。"),
|
||||
("AWS verification code", english_code +
|
||||
"If you fail to respond by 2026-10-10 your account will be suspended."),
|
||||
("AWS verification code", english_code +
|
||||
"If you do not upload documents by 2026-10-10, your account will be suspended."),
|
||||
("AWS 验证码", "验证码 987654,用于登录。如果您未能在截止日期回复您的账号将被暂停。"),
|
||||
("AWS verification code", english_code +
|
||||
"Your access key has been compromised. If your account is suspended, contact support."),
|
||||
("AWS verification code", english_code +
|
||||
"If you need help, please provide identity documents to support, your account has been suspended."),
|
||||
("AWS verification code", english_code +
|
||||
"If you need help, please provide identity documents to support. Please upload proof of identity."),
|
||||
]
|
||||
for subject, body in cases:
|
||||
with self.subTest(subject=subject, body=body):
|
||||
self.assertIsNotNone(synthetic(subject, body))
|
||||
|
||||
def test_report_omits_missing_fields_without_reordering(self):
|
||||
record = synthetic("AWS Account Permanent Closure Confirmation", "Account permanently closed.")
|
||||
report = check_mail.format_report(record, ZoneInfo("Asia/Shanghai"))
|
||||
self.assertEqual([line.split(":", 1)[0] for line in report.splitlines()],
|
||||
["【AWS账号风控邮件】", "收件时间", "邮件主题", "风险类型"])
|
||||
self.assertNotIn(check_mail.EMPTY, report)
|
||||
self.assertNotIn("AWS账号ID", report)
|
||||
|
||||
def test_report_omits_none_empty_and_absent_fields(self):
|
||||
report = check_mail.format_report({
|
||||
"subject": "", "account": None, "type": "账户验证或暂停",
|
||||
"warning": " ", "resources": check_mail.EMPTY, "deadline": "2026-10-10",
|
||||
}, ZoneInfo("Asia/Shanghai"))
|
||||
self.assertEqual(report.splitlines(), ["【AWS账号风控邮件】", "风险类型:账户验证或暂停",
|
||||
"截止时间:2026-10-10"])
|
||||
|
||||
def test_complete_report_keeps_field_order(self):
|
||||
record = synthetic("AWS Account on Hold: Response Required",
|
||||
"AWS Account ID: 123456789012\nYour account has been placed on hold.\n"
|
||||
"Resource i-12345678\nPlease reply before 2026-10-10.")
|
||||
self.assertEqual(record["account"], "123456789012")
|
||||
self.assertEqual(record["resources"], "i-12345678")
|
||||
report = check_mail.format_report(record, ZoneInfo("Asia/Shanghai"))
|
||||
self.assertEqual([line.split(":", 1)[0] for line in report.splitlines()],
|
||||
["【AWS账号风控邮件】", "收件时间", "邮件主题", "AWS账号ID", "风险类型",
|
||||
"警告信息", "涉及资源", "要求操作", "截止时间"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user