aws-email-check: only report ban-risk mails (suspension/abuse/suspicious activity)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 15:42:51 +08:00
co-authored by Claude Sonnet 5.5
parent 5db6f351cd
commit ce0eaa9e95
3 changed files with 256 additions and 113 deletions
+56 -1
View File
@@ -39,6 +39,31 @@ ACCESS_VALIDATED = re.compile(
r"(?:successfully\s+)?validated\b|"
r"您访问\s*AWS\s*资源的请求已(?:得到验证|通过验证|验证成功)", re.I
)
DOMAIN_EXPIRATION_NOTICE = re.compile(
r"\[\s*domain\s+expiring\s*\]|\bdomain\s+expiring\s+soon\b|"
r"(?:^|[\n.!?;,::,。])\s*registration\s+for\s+"
r"[a-z0-9.-]+\.[a-z]{2,}\s+expires?\s+on\b|"
r"\bdomain\s+(?:registration\s+)?(?:expiration\s+(?:notice|reminder|date)|"
r"renewal\s+(?:reminder|notice|failed|failure))\b|"
r"\bdomain\s+registration\s+(?:(?:for\s+)?[a-z0-9.-]+\.[a-z]{2,}\s+)?"
r"(?:is\s+scheduled\s+to\s+expire|will\s+expire|expires?\s+on)\b|"
r"(?:^|[\n.!?;,::,。])\s*(?:your|the|this)\s+domain(?:\s+name)?\s+"
r"(?:[a-z0-9.-]+\.[a-z]{2,}\s+)?"
r"(?:is\s+scheduled\s+to\s+expire|will\s+expire|expires?\s+on)\b|"
r"\bdomain\s+renewal\b[^\n]{0,60}\b(?:failed|unsuccessful)\b|"
r"域名(?:\s*[a-z0-9.-]+)?\s*(?:(?:的)?注册(?:有效期|期限)?)?\s*"
r"(?:即将到期|到期提醒|到期通知|过期提醒|续费提醒|续费通知|续费失败|"
r"将于\s*\d{4}[-/年]\d{1,2}[-/月]\d{1,2}日?\s*到期)", re.I
)
DOMAIN_BUSINESS_STATE = re.compile(
r"(?P<prefix>^|[\n.!?。!?;;,,]|\b(?:and|but|while|before|after)\s+|而)"
r"\s*(?:(?:your|the|this)\s+|您的|该)?"
r"(?:\b(?:domain(?:\s+(?:registration|[a-z0-9.-]+\.[a-z]{2,}))?|"
r"(?:automatic(?:\s+annual)?\s+)?renewal)\s+"
r"(?:(?:has\s+been|is|was|will\s+be)\s+)?"
r"(?:suspended|restricted|disabled|terminated|closed)\b|"
r"域名(?:注册|解析)?(?:已被|已|被)?(?:暂停|受限|停用|关闭))", re.I | re.M
)
RISK_ALERT_SUBJECT = re.compile(
r"suspicious activity|security (?:alert|warning)|abuse report|phishing|compromis|"
r"可疑活动|异常活动|滥用|钓鱼|泄露|安全警告", re.I
@@ -144,6 +169,25 @@ def is_access_validated_notice(subject, body):
)
def has_domain_notice_risk(text):
# A domain's business state must not become an account restriction merely
# because the same clause also mentions the AWS account used for billing.
return has_explicit_risk(
DOMAIN_BUSINESS_STATE.sub(r"\g<prefix>", text), ACCESS_NOTICE_RISK_EVIDENCE
)
def is_domain_expiration_notice(subject, body):
"""Ignore domain-expiration business notices, while retaining actual risks."""
combined = subject + "\n" + body
return bool(
DOMAIN_EXPIRATION_NOTICE.search(combined)
and not (has_domain_notice_risk(combined)
or EXPLICIT_CLOSURE_SUBJECT.search(subject)
or RISK_ALERT_SUBJECT.search(subject))
)
class PlainHTML(HTMLParser):
def __init__(self):
super().__init__()
@@ -268,12 +312,23 @@ def extract(message, received):
subject = header_value(message, "Subject")
body = body_text(message)
combined = subject + "\n" + body
if is_verification_code(subject, body) or is_access_validated_notice(subject, body):
if (is_verification_code(subject, body)
or is_access_validated_notice(subject, body)
or is_domain_expiration_notice(subject, body)):
return None
if not RISK_TERMS.search(combined) or not (
WARNING_TERMS.search(combined) or EXPLICIT_CLOSURE_SUBJECT.search(subject)
or (DOMAIN_EXPIRATION_NOTICE.search(combined)
and has_domain_notice_risk(combined))
):
return None
# Allowlist: generic words like "verification" or "risk" are not enough.
# Only mail that signals a possible ban (restriction, suspension, abuse,
# suspicious activity, compromised keys, document requests) is reported.
if not (has_explicit_risk(combined, ACCESS_NOTICE_RISK_EVIDENCE)
or EXPLICIT_CLOSURE_SUBJECT.search(subject)
or RISK_ALERT_SUBJECT.search(subject)):
return None
# Only explicit 12-digit IDs with account context qualify. Case IDs and dates do not.
identifier = account_id(combined)
warning = lines_matching(body, re.compile(