forked from cnlimiter/codex-manager
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheck_otp_timing.py
More file actions
155 lines (123 loc) · 4.52 KB
/
Copy pathcheck_otp_timing.py
File metadata and controls
155 lines (123 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python3
"""
离线验证 TempmailService 的 OTP 时间锚点过滤行为。
场景 1:
- 30 秒内先后收到两封邮件
- 在两封邮件之间设置新的 otp_sent_at
- 期望过滤第一封,命中第二封
场景 2:
- 第二封邮件已经入箱后才刷新 otp_sent_at
- 期望复现严格时间过滤导致第二封也被排除的窗口
"""
from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import Any, Dict, List, Optional
import src.services.tempmail as tempmail_module
from src.services.tempmail import TempmailService
@dataclass(frozen=True)
class Scenario:
name: str
anchor_offset_seconds: int
expected_code: Optional[str]
expected_message: str
class FakeResponse:
def __init__(self, payload: Dict[str, Any], status_code: int = 200):
self._payload = payload
self.status_code = status_code
def json(self) -> Dict[str, Any]:
return self._payload
class FakeHTTPClient:
def __init__(self, payload: Dict[str, Any]):
self.payload = payload
self.calls: List[Dict[str, Any]] = []
def get(self, url: str, **kwargs: Any) -> FakeResponse:
self.calls.append({"url": url, "kwargs": kwargs})
return FakeResponse(self.payload)
class FakeClock:
def __init__(self, start: float):
self.current = float(start)
def time(self) -> float:
return self.current
def sleep(self, seconds: float) -> None:
self.current += float(seconds)
def build_inbox_payload(base_timestamp: int) -> Dict[str, Any]:
return {
"emails": [
{
"id": "mail-1",
"received_at": base_timestamp + 10,
"from": "noreply@openai.com",
"subject": "First OTP",
"body": "111111",
},
{
"id": "mail-2",
"received_at": base_timestamp + 20,
"from": "noreply@openai.com",
"subject": "Second OTP",
"body": "222222",
},
]
}
def run_scenario(scenario: Scenario) -> Dict[str, Any]:
base_timestamp = 1_700_000_000
service = TempmailService({"base_url": "https://api.tempmail.test"})
service._email_cache["tester@example.com"] = {"token": "token-1"}
service.http_client = FakeHTTPClient(build_inbox_payload(base_timestamp))
fake_clock = FakeClock(start=base_timestamp + scenario.anchor_offset_seconds)
anchor_timestamp = fake_clock.time()
original_time = tempmail_module.time.time
original_sleep = tempmail_module.time.sleep
try:
tempmail_module.time.time = fake_clock.time
tempmail_module.time.sleep = fake_clock.sleep
code = service.get_verification_code(
email="tester@example.com",
timeout=1,
otp_sent_at=anchor_timestamp,
)
finally:
tempmail_module.time.time = original_time
tempmail_module.time.sleep = original_sleep
passed = code == scenario.expected_code
return {
"name": scenario.name,
"anchor_timestamp": anchor_timestamp,
"code": code,
"passed": passed,
"http_calls": len(service.http_client.calls),
"message": scenario.expected_message,
}
def main() -> int:
logging.getLogger("src.services.tempmail").setLevel(logging.ERROR)
scenarios = [
Scenario(
name="anchor_between_two_emails",
anchor_offset_seconds=15,
expected_code="222222",
expected_message="新锚点位于两封邮件之间,第一封应被过滤,第二封应被命中。",
),
Scenario(
name="anchor_set_after_second_email",
anchor_offset_seconds=21,
expected_code=None,
expected_message="锚点晚于第二封邮件时,严格大于过滤会把第二封也排除,复现登录阶段的竞态窗口。",
),
]
print("Tempmail OTP timing check")
print("=========================")
failed = False
for scenario in scenarios:
result = run_scenario(scenario)
status = "PASS" if result["passed"] else "FAIL"
print(f"{status} {result['name']}")
print(f" anchor_timestamp={result['anchor_timestamp']}")
print(f" returned_code={result['code']}")
print(f" inbox_polls={result['http_calls']}")
print(f" note={result['message']}")
if not result["passed"]:
failed = True
return 1 if failed else 0
if __name__ == "__main__":
raise SystemExit(main())