Suspend immediately on strong IP-reputation signals (not vpn alone)
docker-build-push / build-push (push) Failing after 15s
docker-build-push / build-push (push) Failing after 15s
datacenter/proxy/tor/abuser is a much cleaner bulk/bot-signup indicator than vpn, which also flags plenty of privacy-conscious real users. A strong-flagged signup now suspends immediately regardless of the email signal; vpn-only still gets the normal held-welcome/ipblock/lowered- threshold treatment, falling through to the existing combined-signal suspend only if also paired with a flagged email domain.
This commit is contained in:
+104
@@ -533,6 +533,109 @@ def combined_signal_tests():
|
||||
assert ("505", "failsuspend") not in sent, "still held, falls back to the normal flagged path"
|
||||
|
||||
|
||||
def strong_ip_suspend_tests():
|
||||
"""Drive process_signup's strong-IP-signal immediate-suspend path
|
||||
(IP_SCRUTINY_STRONG_SUSPEND_*): datacenter/proxy/tor/abuser suspend
|
||||
immediately regardless of the email signal, but vpn alone does not — it
|
||||
still falls through to the normal hold/welcome path (or the existing
|
||||
combined-signal suspend, if also paired with a flagged email domain)."""
|
||||
sent = []
|
||||
dms = []
|
||||
actions = []
|
||||
ipblocks = []
|
||||
main.send_welcome = lambda account_id, acct: sent.append((account_id, acct))
|
||||
main.dm_moderator = lambda message: dms.append(message)
|
||||
main.apply_action = lambda target_id, action, text: actions.append((target_id, action))
|
||||
main.register_ip_block = lambda ip, acct, org, cidr: ipblocks.append((ip, acct, org, cidr))
|
||||
main.register_email_domain_block = lambda domain, acct: None
|
||||
main.fetch_account_counts = lambda account_id: (0, 0)
|
||||
|
||||
main.IP_SCRUTINY_ENABLED = True
|
||||
main.IP_SCRUTINY_DRY_RUN = False
|
||||
main.IP_SCRUTINY_HOLD_WELCOME = True
|
||||
main.IP_SCRUTINY_AUTO_IPBLOCK = True
|
||||
main.CHECK_MAIL_ENABLED = True
|
||||
main.CHECK_MAIL_API_KEY = "test-key"
|
||||
main.CHECK_MAIL_DRY_RUN = False
|
||||
main.CHECK_MAIL_HOLD_WELCOME = True
|
||||
main.CHECK_MAIL_AUTO_DOMAIN_BLOCK = True
|
||||
main.SUSPICIOUS_COMBINED_ENABLED = True
|
||||
main.SUSPICIOUS_COMBINED_ACTION = "suspend"
|
||||
main.SUSPICIOUS_COMBINED_DRY_RUN = False
|
||||
main.ABUSE_ALLOWLIST = {"trustedstaff"}
|
||||
main.IP_SCRUTINY_STRONG_SUSPEND_ENABLED = True
|
||||
main.IP_SCRUTINY_STRONG_SUSPEND_ACTION = "suspend"
|
||||
main.IP_SCRUTINY_STRONG_SUSPEND_DRY_RUN = False
|
||||
|
||||
ip_classifications = {
|
||||
"198.51.100.60": ("datacenter", "Example Cloud Hosting Inc", True, "198.51.100.48/28"),
|
||||
"198.51.100.61": ("proxy", "Example Proxy Networks", True, "198.51.100.48/28"),
|
||||
"198.51.100.62": ("vpn", "Example VPN Provider", True, "198.51.100.48/28"),
|
||||
"198.51.100.63": ("vpn", "Example VPN Provider", True, "198.51.100.48/28"),
|
||||
}
|
||||
main.classify_signup_ip = lambda ip: ip_classifications[ip]
|
||||
email_classifications = {
|
||||
"temp-mail.org": (True, 99),
|
||||
"gmail.com": (False, 5),
|
||||
}
|
||||
main.classify_email_domain = lambda domain: email_classifications[domain]
|
||||
|
||||
# A. datacenter (strong), clean email -> suspended immediately regardless
|
||||
# of the (non-flagged) email signal; ip_block still registered.
|
||||
main.process_signup("601", "strongdc", "198.51.100.60", "[email protected]")
|
||||
assert ("601", "suspend") in actions, actions
|
||||
assert ("601", "strongdc") not in sent, "must not welcome an immediately-suspended signup"
|
||||
assert any("strongdc" in d and "strong IP signal" in d for d in dms), dms
|
||||
assert ("198.51.100.60", "strongdc", "Example Cloud Hosting Inc", "198.51.100.48/28") in ipblocks, ipblocks
|
||||
|
||||
# B. proxy (strong), no email at all -> still suspended immediately.
|
||||
actions.clear(); sent.clear(); dms.clear()
|
||||
main.process_signup("602", "strongproxy", "198.51.100.61")
|
||||
assert ("602", "suspend") in actions, actions
|
||||
assert ("602", "strongproxy") not in sent
|
||||
|
||||
# C. vpn only (not strong), clean email -> NOT suspended, falls through to
|
||||
# the normal held-welcome path.
|
||||
actions.clear(); sent.clear(); dms.clear()
|
||||
main.process_signup("603", "vpnonly", "198.51.100.62", "[email protected]")
|
||||
assert ("603", "suspend") not in actions, actions
|
||||
assert ("603", "vpnonly") not in sent, "still held by the individual IP-scrutiny hold"
|
||||
|
||||
# D. vpn (not strong) + flagged email -> not caught by the strong path,
|
||||
# but still caught by the existing combined-signal suspend.
|
||||
actions.clear(); sent.clear(); dms.clear()
|
||||
main.process_signup("604", "vpnplusemail", "198.51.100.63", "[email protected]")
|
||||
assert ("604", "suspend") in actions, actions
|
||||
assert ("604", "vpnplusemail") not in sent
|
||||
assert any("vpnplusemail" in d and "BOTH" in d for d in dms), dms
|
||||
|
||||
# E. allowlisted acct with a strong signal -> not suspended.
|
||||
actions.clear(); sent.clear(); dms.clear()
|
||||
main.process_signup("605", "trustedstaff", "198.51.100.60", "[email protected]")
|
||||
assert ("605", "suspend") not in actions, "allowlisted acct must not be auto-suspended"
|
||||
|
||||
# F. strong signal, but IP_SCRUTINY_STRONG_SUSPEND_DRY_RUN -> DM only, no
|
||||
# real suspend; falls through to the normal held-welcome path.
|
||||
actions.clear(); sent.clear(); dms.clear()
|
||||
main.IP_SCRUTINY_STRONG_SUSPEND_DRY_RUN = True
|
||||
main.process_signup("606", "drystrong", "198.51.100.60", "[email protected]")
|
||||
assert ("606", "suspend") not in actions, "dry-run must not call apply_action"
|
||||
assert any("[DRY-RUN]" in d and "drystrong" in d for d in dms), dms
|
||||
assert ("606", "drystrong") not in sent, "still held by the individual IP-scrutiny hold"
|
||||
|
||||
# G. strong signal, live, but apply_action fails -> falls back to the
|
||||
# normal held-welcome path instead of silently dropping the signup.
|
||||
actions.clear(); sent.clear(); dms.clear()
|
||||
main.IP_SCRUTINY_STRONG_SUSPEND_DRY_RUN = False
|
||||
|
||||
def boom(target_id, action, text):
|
||||
raise main.httpx.HTTPError("boom")
|
||||
|
||||
main.apply_action = boom
|
||||
main.process_signup("607", "failstrong", "198.51.100.60", "[email protected]")
|
||||
assert ("607", "failstrong") not in sent, "still held, falls back to the normal flagged path"
|
||||
|
||||
|
||||
def suspicious_watch_tests():
|
||||
"""Drive maybe_start_suspicious_watch: baseline capture on flagged
|
||||
signups only, never on clean ones, and never twice for the same account."""
|
||||
@@ -684,6 +787,7 @@ if __name__ == "__main__":
|
||||
email_scrutiny_tests()
|
||||
range_cache_tests()
|
||||
combined_signal_tests()
|
||||
strong_ip_suspend_tests()
|
||||
suspicious_watch_tests()
|
||||
suspicious_sweep_tests()
|
||||
print("ALL TESTS PASSED")
|
||||
|
||||
Reference in New Issue
Block a user