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:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user