fix(cookie-gate): 必需 cookie 改全量校验并收紧各账号必需名单
required_cookie_names 原语义是「任一必需名命中即有效」,单个匿名 cookie (如小红书 a1)就能把游客会话判为有效;改为全部必需名都存在、存活且域名 匹配才算通过。随之按实际登录产物收紧名单:jd 去 pin、erp321 留 u_id+u_co_id、小红书改 customer-sso-sid+solar.beaker.session.id、 星图仅 sid_guard。cookie_gate 用例冻结时钟改相对时钟防过期漂移。
This commit is contained in:
1 parent
7b305b0b12
commit
9feb188d93
3 files changed
+46
-30
No files matched your search
@@ -88,7 +88,6 @@
|
|||||||
"jd.com"
|
"jd.com"
|
||||||
],
|
],
|
||||||
"required_cookie_names": [
|
"required_cookie_names": [
|
||||||
"pin",
|
|
||||||
"thor"
|
"thor"
|
||||||
],
|
],
|
||||||
"credential_env_names": [
|
"credential_env_names": [
|
||||||
@@ -116,7 +115,6 @@
|
|||||||
"jd.com"
|
"jd.com"
|
||||||
],
|
],
|
||||||
"required_cookie_names": [
|
"required_cookie_names": [
|
||||||
"pin",
|
|
||||||
"thor"
|
"thor"
|
||||||
],
|
],
|
||||||
"credential_env_names": [
|
"credential_env_names": [
|
||||||
@@ -145,10 +143,7 @@
|
|||||||
],
|
],
|
||||||
"required_cookie_names": [
|
"required_cookie_names": [
|
||||||
"u_id",
|
"u_id",
|
||||||
"u_co_id",
|
"u_co_id"
|
||||||
"ASP.NET_SessionId",
|
|
||||||
".ASPXAUTH",
|
|
||||||
"token"
|
|
||||||
],
|
],
|
||||||
"credential_env_names": [
|
"credential_env_names": [
|
||||||
"ERP_USERNAME",
|
"ERP_USERNAME",
|
||||||
@@ -299,7 +294,8 @@
|
|||||||
"xiaohongshu.com"
|
"xiaohongshu.com"
|
||||||
],
|
],
|
||||||
"required_cookie_names": [
|
"required_cookie_names": [
|
||||||
"web_session",
|
"customer-sso-sid",
|
||||||
|
"solar.beaker.session.id",
|
||||||
"a1"
|
"a1"
|
||||||
],
|
],
|
||||||
"keepalive": {
|
"keepalive": {
|
||||||
@@ -351,9 +347,6 @@
|
|||||||
"xingtu.cn"
|
"xingtu.cn"
|
||||||
],
|
],
|
||||||
"required_cookie_names": [
|
"required_cookie_names": [
|
||||||
"sessionid",
|
|
||||||
"sessionid_ss",
|
|
||||||
"sid_tt",
|
|
||||||
"sid_guard"
|
"sid_guard"
|
||||||
],
|
],
|
||||||
"keepalive": {
|
"keepalive": {
|
||||||
@@ -365,8 +358,7 @@
|
|||||||
"headless": true,
|
"headless": true,
|
||||||
"real_chrome": true
|
"real_chrome": true
|
||||||
},
|
},
|
||||||
"members": [
|
"members": []
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"chanmama": {
|
"chanmama": {
|
||||||
"cdp_port": 22207,
|
"cdp_port": 22207,
|
||||||
@@ -702,8 +694,12 @@
|
|||||||
],
|
],
|
||||||
"cdp_port": 22165,
|
"cdp_port": 22165,
|
||||||
"login_mode": "C",
|
"login_mode": "C",
|
||||||
"required_cookie_domains": ["xiaohongshu.com"],
|
"required_cookie_domains": [
|
||||||
"credential_env_names": ["XHS_ARK_ACCOUNT"]
|
"xiaohongshu.com"
|
||||||
|
],
|
||||||
|
"credential_env_names": [
|
||||||
|
"XHS_ARK_ACCOUNT"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"content_marketing:data/tools/sync_style_categories.py": {
|
"content_marketing:data/tools/sync_style_categories.py": {
|
||||||
"script_id": "content_marketing:data/tools/sync_style_categories.py",
|
"script_id": "content_marketing:data/tools/sync_style_categories.py",
|
||||||
|
|||||||
@@ -1210,29 +1210,49 @@ def _merge_storage_state(
|
|||||||
|
|
||||||
|
|
||||||
def _account_cookie_valid(account: AccountBinding, cookies: object) -> bool:
|
def _account_cookie_valid(account: AccountBinding, cookies: object) -> bool:
|
||||||
"""A vault cookie set is valid when a live account cookie is present."""
|
"""A vault cookie set is valid when a live account cookie is present.
|
||||||
|
|
||||||
|
With ``required_cookie_names`` configured, every required name must be
|
||||||
|
present as a live, domain-matched cookie; a single anonymous cookie that
|
||||||
|
happens to share a required name (e.g. ``a1``) must not validate a guest
|
||||||
|
session. Without required names, any live cookie on a required domain
|
||||||
|
keeps accepting the set.
|
||||||
|
"""
|
||||||
if not isinstance(cookies, list):
|
if not isinstance(cookies, list):
|
||||||
return False
|
return False
|
||||||
required_names = account.required_cookie_names
|
required_names = account.required_cookie_names
|
||||||
required_domains = account.required_cookie_domains
|
required_domains = account.required_cookie_domains
|
||||||
for item in cookies:
|
|
||||||
if not isinstance(item, dict):
|
def _domain_matches(domain: str) -> bool:
|
||||||
continue
|
return not required_domains or any(
|
||||||
if _cookie_expired(item):
|
|
||||||
continue
|
|
||||||
if not str(item.get("value", "")):
|
|
||||||
continue
|
|
||||||
name = str(item.get("name", ""))
|
|
||||||
domain = str(item.get("domain", "")).casefold().lstrip(".")
|
|
||||||
name_matches = not required_names or name in required_names
|
|
||||||
domain_matches = not required_domains or any(
|
|
||||||
domain == required.casefold()
|
domain == required.casefold()
|
||||||
or domain.endswith("." + required.casefold())
|
or domain.endswith("." + required.casefold())
|
||||||
for required in required_domains
|
for required in required_domains
|
||||||
)
|
)
|
||||||
if name_matches and domain_matches:
|
|
||||||
return True
|
def _live(item: object) -> bool:
|
||||||
return False
|
return (
|
||||||
|
isinstance(item, dict)
|
||||||
|
and not _cookie_expired(item)
|
||||||
|
and bool(str(item.get("value", "")))
|
||||||
|
)
|
||||||
|
|
||||||
|
if not required_names:
|
||||||
|
return any(
|
||||||
|
_live(item)
|
||||||
|
and _domain_matches(
|
||||||
|
str(item.get("domain", "")).casefold().lstrip(".")
|
||||||
|
)
|
||||||
|
for item in cookies
|
||||||
|
)
|
||||||
|
|
||||||
|
outstanding = set(required_names)
|
||||||
|
for item in cookies:
|
||||||
|
if not _live(item):
|
||||||
|
continue
|
||||||
|
if _domain_matches(str(item.get("domain", "")).casefold().lstrip(".")):
|
||||||
|
outstanding.discard(str(item.get("name", "")))
|
||||||
|
return not outstanding
|
||||||
|
|
||||||
|
|
||||||
def _cookie_expired(cookie: Mapping[str, object], *, now: float | None = None) -> bool:
|
def _cookie_expired(cookie: Mapping[str, object], *, now: float | None = None) -> bool:
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from gyxx_flow.workflow.engine import WorkflowEngine
|
|||||||
from gyxx_flow.workflow.model import StepDefinition, WorkflowDefinition
|
from gyxx_flow.workflow.model import StepDefinition, WorkflowDefinition
|
||||||
from gyxx_flow.workflow.steps import StepExecution
|
from gyxx_flow.workflow.steps import StepExecution
|
||||||
|
|
||||||
_NOW = datetime(2026, 10, 8, 12, 0, 0, tzinfo=timezone.utc)
|
_NOW = datetime.now(timezone.utc).replace(microsecond=0)
|
||||||
|
|
||||||
|
|
||||||
def _settings(tmp_path: Path) -> Settings:
|
def _settings(tmp_path: Path) -> Settings:
|
||||||
|
|||||||
Reference in new issue
Block a user