fix(client): separate screenshot and XML coordinates

This commit is contained in:
QiuSW
2026-08-04 11:57:45 +08:00
parent cf3692112c
commit 44c027a18f
2 changed files with 143 additions and 25 deletions
@@ -19,8 +19,10 @@ sys.path.insert(0, str(CLIENT_ROOT / "src"))
from cmbuyer_client.device.sku_evidence_sanitizer import (
EXPECTED_GOODS_ID,
EXPECTED_HEIGHT,
EXPECTED_WIDTH,
EXPECTED_SCREENSHOT_HEIGHT,
EXPECTED_SCREENSHOT_WIDTH,
EXPECTED_XML_HEIGHT,
EXPECTED_XML_WIDTH,
HUMAN_DECLARED_STATES,
SkuEvidenceSanitizationError,
sanitize_sku_panel_evidence,
@@ -59,14 +61,14 @@ def _write_raw(
model: str = "PKG110",
android_version: str = "16",
pdd_version: str = "8.17.0",
size: tuple[int, int] = (EXPECTED_WIDTH, EXPECTED_HEIGHT),
size: tuple[int, int] = (EXPECTED_SCREENSHOT_WIDTH, EXPECTED_SCREENSHOT_HEIGHT),
xml: str | None = None,
) -> Path:
raw = root / "raw"
raw.mkdir(parents=True)
screenshot = raw / "screenshot.png"
image = Image.new("RGB", size, color=(0, 180, 0))
if size == (EXPECTED_WIDTH, EXPECTED_HEIGHT):
if size == (EXPECTED_SCREENSHOT_WIDTH, EXPECTED_SCREENSHOT_HEIGHT):
for y in range(540):
for x in range(8):
image.putpixel((x, y), (255, 0, 0))
@@ -115,11 +117,17 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
self.assertNotIn(MASKED_PHONE, derived_xml)
self.assertIn(f'"human_declared_state": "{state}"', manifest)
self.assertIn('"privacy_tier": "SANITIZED"', manifest)
self.assertIn('"sanitizer_version": "t103-privacy-v1"', manifest)
self.assertIn('"rectangle": [', manifest)
self.assertIn('"sanitizer_version": "t103-privacy-v2"', manifest)
self.assertIn('"screenshot_space": {', manifest)
self.assertIn('"xml_coordinate_space": {', manifest)
self.assertIn('"height": 2376', manifest)
self.assertIn('"height": 2400', manifest)
self.assertIn('"privacy_mask_rectangle": [', manifest)
self.assertIn('"removed_nodes": 1', manifest)
self.assertIn('"cleared_crossing_nodes": 0', manifest)
self.assertIn('"retained_below_nodes": 1', manifest)
self.assertIn('"max_right": 1080', manifest)
self.assertIn('"max_bottom": 2400', manifest)
self.assertNotIn("canonical_url", manifest)
self.assertNotIn(TEST_SERIAL, manifest)
self.assertNotIn("serial", manifest)
@@ -149,7 +157,13 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
manifest = json.loads(result.manifest_path.read_text(encoding="utf-8"))
self.assertEqual(
manifest["privacy_cleanup"],
{"removed_nodes": 1, "cleared_crossing_nodes": 1, "retained_below_nodes": 1},
{
"removed_nodes": 1,
"cleared_crossing_nodes": 1,
"retained_below_nodes": 1,
"max_right": 1080,
"max_bottom": 2400,
},
)
def test_same_raw_and_config_produce_identical_derived_files(self) -> None:
@@ -167,6 +181,26 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
):
self.assertEqual(left_path.read_bytes(), right_path.read_bytes())
def test_manifest_records_distinct_screenshot_and_xml_coordinate_spaces(self) -> None:
with TemporaryDirectory() as temporary:
raw = _write_raw(Path(temporary))
result = sanitize_sku_panel_evidence(raw, raw.parent / "derived")
manifest = json.loads(result.manifest_path.read_text(encoding="utf-8"))
self.assertEqual(
manifest["screenshot_space"],
{"width": 1080, "height": 2376, "privacy_mask_rectangle": [0, 0, 1080, 540]},
)
self.assertEqual(
manifest["xml_coordinate_space"],
{
"width": 1080,
"height": 2400,
"privacy_mask_rectangle": [0, 0, 1080, 540],
"observed_max": {"right": 1080, "bottom": 2400},
},
)
def test_hash_and_metadata_mismatch_fail_closed_without_leak(self) -> None:
scenarios = (
("hash", {}, "screenshot"),
@@ -175,7 +209,8 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
("version", {"pdd_version": "8.17.1"}, None),
("goods", {"goods_id": "123"}, None),
("state", {"state": "guessed"}, None),
("resolution", {"size": (100, 100)}, None),
("old-screenshot-space", {"size": (1080, 2400)}, None),
("other-screenshot-space", {"size": (100, 100)}, None),
)
for name, kwargs, corrupt_file in scenarios:
with self.subTest(name=name), TemporaryDirectory() as temporary:
@@ -281,6 +316,44 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
self.assertFalse((raw.parent / "derived").exists())
self.assertEqual(list(raw.parent.glob(".derived.staging-*")), [])
def test_xml_coordinate_space_must_have_exact_configured_maximums(self) -> None:
scenarios = (
(
"short-width",
"<hierarchy><node bounds='[0,0][1079,540]' text='private' />"
"<node bounds='[0,540][1079,2400]' text='safe' /></hierarchy>",
"1079x2400",
),
(
"short-height",
"<hierarchy><node bounds='[0,0][1080,540]' text='private' />"
"<node bounds='[0,540][1080,2376]' text='safe' /></hierarchy>",
"1080x2376",
),
(
"wide-width",
"<hierarchy><node bounds='[0,0][1081,540]' text='private' />"
"<node bounds='[0,540][1081,2400]' text='safe' /></hierarchy>",
"1081x2400",
),
(
"tall-height",
"<hierarchy><node bounds='[0,0][1080,540]' text='private' />"
"<node bounds='[0,540][1080,2401]' text='safe' /></hierarchy>",
"1080x2401",
),
)
for name, xml, observed in scenarios:
with self.subTest(name=name), TemporaryDirectory() as temporary:
raw = _write_raw(Path(temporary), xml=xml)
with self.assertRaises(SkuEvidenceSanitizationError) as raised:
sanitize_sku_panel_evidence(raw, raw.parent / "derived")
self.assertIn(observed, str(raised.exception))
self.assertNotIn(TEST_SERIAL, str(raised.exception))
self.assertFalse((raw.parent / "derived").exists())
self.assertEqual(list(raw.parent.glob(".derived.staging-*")), [])
def test_manifest_schema_package_and_artifact_structure_are_required(self) -> None:
def mutate(manifest: dict[str, object], kind: str) -> None:
if kind == "schema":