feat(privacy): 关掉昵称脱敏 —— 脱敏有损,撞名就分不出博主
需求:评论栏/作品栏要能分清是哪个博主。修好分组字段之后名字仍带星号,因为上游作为 教学版默认对昵称做中间脱敏(首尾各留 1 字,中间星号)。这个脱敏是**有损**的: 「张三」和「张四」都变成「张*」 「小明老师」和「小刚老师」都变成「小***师」 而本仓库的用途是监控一批公开创作者账号,分清谁是谁正是这一层要干的事。所以关掉它。 * config/base_config.py 新增 MASK_NICKNAME = False(和 INJECT_ALL_COOKIES 一样是个 开关,不是删代码 —— 改回 True 就恢复上游行为)。 * tools/user_hash.py 的 mask_nickname 读这个开关,关闭时原样返回。读的是模块属性而 不是导入值,测试才能 monkeypatch。脱敏实现本身一字未动。 * 顺带修一个数据陈旧问题:评论是去重后直接 continue 的,昵称只在首次入库时写一次, 于是开关一改(或评论者改名)老评论永远停在旧值 —— 而重采是唯一能拿到新值的途径。 现在已存在的评论会跟着刷新昵称(作品那边的 creator_name 早就是这么做的)。 * anonymous 的 creator_hash 保持不变:那是分组用的稳定键,不是显示名。 测试: * 三个隐私套件 + weibo 的 autouse fixture 强制把开关打开 —— 它们验的是**脱敏机制 本身**,机制仍然必须正确,所以显式打开来测,而不是让它们随部署配置漂。 * test_mask_and_hash_tools 改成两个方向都覆盖(开着脱敏 / 关着脱敏)。 * test_tieba_extractor.py 里 8 处字面量的脱敏期望值换成真实昵称 —— 提取器现在就是 返回原文的,期望值理应跟着改(这一条是行为变更的直接后果,不是测试放宽)。 * 新增一条:已入库的评论昵称会随重采刷新(且不会因刷新而重复插入)。
This commit is contained in:
@@ -14,6 +14,8 @@ import pathlib
|
||||
|
||||
import pytest
|
||||
|
||||
import config
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
|
||||
# 统一的禁用字段名(键)。昵称字段(nickname/user_nickname/screen_name/name/user_name)允许保留(值需脱敏)。
|
||||
@@ -28,6 +30,17 @@ NICK_KEYS = {"nickname", "user_nickname", "screen_name", "name", "user_name"}
|
||||
MASK_RE = re.compile(r"^.?\*{1,4}.?$")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _force_nickname_masking(monkeypatch):
|
||||
"""这一组验的是**脱敏机制本身**,所以强制把它打开。
|
||||
|
||||
本仓库的部署配置是关掉的(config.MASK_NICKNAME = False)—— 监控的是一批公开创作者
|
||||
账号,而脱敏是有损的(「张三」「张四」都成「张*」),分不出谁是谁。机制仍然必须正确,
|
||||
所以这里显式打开来测;开关两个方向的行为由 test_mask_and_hash_tools 覆盖。
|
||||
"""
|
||||
monkeypatch.setattr(config, "MASK_NICKNAME", True)
|
||||
|
||||
|
||||
# ----------------------------- ORM 自省 -----------------------------
|
||||
|
||||
def test_orm_has_no_forbidden_columns():
|
||||
@@ -84,17 +97,26 @@ def _check_nickname_masked(d: dict, raw: str, label: str):
|
||||
assert MASK_RE.match(val) or "*" in val, f"[{label}] {k} 未脱敏: {val}"
|
||||
|
||||
|
||||
def test_mask_and_hash_tools():
|
||||
def test_mask_and_hash_tools(monkeypatch):
|
||||
from tools.user_hash import anonymize_user_id, mask_nickname
|
||||
h = anonymize_user_id("12345")
|
||||
assert h and h != "12345" and re.fullmatch(r"[0-9a-f]{16}", h)
|
||||
assert anonymize_user_id(None) == "" and anonymize_user_id("") == ""
|
||||
# 昵称脱敏:首尾留1字、中间星号,且不等于原文
|
||||
|
||||
# 开关打开:首尾留 1 字、中间星号,且不等于原文。
|
||||
monkeypatch.setattr(config, "MASK_NICKNAME", True)
|
||||
assert mask_nickname("张三丰") != "张三丰"
|
||||
assert "*" in mask_nickname("张三丰")
|
||||
assert mask_nickname(None) == ""
|
||||
assert mask_nickname("a") == "*"
|
||||
|
||||
# 开关关闭(本仓库的部署配置):原样返回。脱敏是有损的 —— 「张三」和「张四」
|
||||
# 都会变成「张*」,而分清谁是谁正是监控这一层要干的事。
|
||||
monkeypatch.setattr(config, "MASK_NICKNAME", False)
|
||||
assert mask_nickname("张三丰") == "张三丰"
|
||||
assert mask_nickname("a") == "a"
|
||||
assert mask_nickname(None) == ""
|
||||
|
||||
|
||||
def test_xhs_note_extraction_masks_user_info():
|
||||
import asyncio
|
||||
|
||||
Reference in New Issue
Block a user