feat(server): 业务事件契约——白名单+6事件与枚举校验
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,39 @@ from tracker import constants as C
|
||||
_SENSITIVE = re.compile("|".join(C.SENSITIVE_KEY_PATTERNS), re.I)
|
||||
_URL_DROP_KEY = re.compile("|".join(C.URL_PARAM_DROP_PATTERNS), re.I)
|
||||
|
||||
# 业务事件属性值域(spec §2 枚举/格式表;全部非自由文本)
|
||||
_BIZ_CHANNEL = {"whatsapp", "phone", "email"}
|
||||
_BIZ_RESULT = {"mail_sent", "mail_failed"}
|
||||
_BIZ_ERROR = {"invalid", "spam", "other"}
|
||||
_SUBMIT_RE = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$|^$") # uuid4,可空
|
||||
_FORM_RE = re.compile(r"^cf7-\d{1,10}$") # CF7 表单 DOM id(f{n}-p{m} → 服务端规范化为 cf7-{form_id})
|
||||
_PROD_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$") # slug ≤100
|
||||
|
||||
def _biz_props_ok(name, props) -> bool:
|
||||
"""业务事件属性逐键校验(spec §2 枚举/格式表;全部非自由文本)。
|
||||
键级白名单已在调用方过完,这里管值:类型 + 枚举 + 格式。"""
|
||||
def s(k): return isinstance(props.get(k), str)
|
||||
def form_ok(): return s("form_id") and bool(_FORM_RE.match(props["form_id"]))
|
||||
def sid_ok():
|
||||
sid = props.get("submission_id", "")
|
||||
return isinstance(sid, str) and bool(_SUBMIT_RE.match(sid))
|
||||
if name == "contact_click":
|
||||
return s("channel") and props["channel"] in _BIZ_CHANNEL
|
||||
if name == "form_start":
|
||||
return form_ok()
|
||||
if name in ("form_submit", "form_submit_result"):
|
||||
return form_ok() and sid_ok() and (
|
||||
name == "form_submit" or (s("result_code") and props["result_code"] in _BIZ_RESULT))
|
||||
if name == "form_error":
|
||||
fc = props.get("field_count")
|
||||
return (form_ok() and sid_ok() and s("error_code") and props["error_code"] in _BIZ_ERROR
|
||||
and isinstance(fc, int) and 0 <= fc <= 99 and not isinstance(fc, bool))
|
||||
if name == "product_view":
|
||||
return (s("product_id") and bool(_PROD_RE.match(props["product_id"]))
|
||||
and isinstance(props.get("inferred"), bool)
|
||||
and s("rule_version") and len(props["rule_version"]) <= 16)
|
||||
return True
|
||||
|
||||
def _utf8_ok(s: str) -> bool:
|
||||
"""孤代理(如 "\ud800")在下游 encode/JSON 序列化时必然 500,入口处即拒。"""
|
||||
try:
|
||||
@@ -140,6 +173,8 @@ def validate_event(ev, *, v2=False, batch_site_id="", batch_visitor_id="") -> st
|
||||
return "sensitive_field"
|
||||
if not set(props) <= C.EVENT_PROP_WHITELIST.get(ev["event_name"], set()):
|
||||
return "property_not_allowed"
|
||||
if ev["event_name"] in C.BIZ_EVENT_NAMES and not _biz_props_ok(ev["event_name"], props):
|
||||
return "property_not_allowed"
|
||||
if not depth_ok:
|
||||
return "properties_depth"
|
||||
# 体积按 UTF-8 字节数守门(汉字多字节,按字符数会被绕过);孤代理 encode 必炸,按 invalid 拒;
|
||||
|
||||
Reference in New Issue
Block a user