test(provider-pipeline): reject unsafe or conflicting render evidence

This commit is contained in:
OpenAI Codex
2026-10-07 16:58:23 +08:00
parent 238dd68dd0
commit d046d7ee0d
2 changed files with 16 additions and 2 deletions

View File

@@ -42,6 +42,14 @@ class ProviderPipelineContractTests(unittest.TestCase):
rows[0]["renderEvidence"]["components"][0][key] = components[0][{"styleLayerId": "style_layer_id", "styleIndex": "style_index"}[key]] rows[0]["renderEvidence"]["components"][0][key] = components[0][{"styleLayerId": "style_layer_id", "styleIndex": "style_index"}[key]]
rows[0]["renderEvidence"]["sourceLayer"] = "wrong" rows[0]["renderEvidence"]["sourceLayer"] = "wrong"
self.assertTrue(any("sourceLayer:mismatch" in x for x in validate_mapping(mapping, style))) self.assertTrue(any("sourceLayer:mismatch" in x for x in validate_mapping(mapping, style)))
rows[0]["renderEvidence"]["sourceLayer"] = "points"
rows[0]["renderEvidence"]["geometryRole"] = "eval(1)"
self.assertTrue(any("script_forbidden" in x for x in validate_mapping(mapping, style)))
rows[0]["renderEvidence"]["geometryRole"] = "Point"
duplicate = json.loads(json.dumps(rows[0]))
duplicate["renderEvidence"]["occurrenceCount"] = 124
mapping["assignments"].append(duplicate)
self.assertTrue(any("nondeterministic_duplicate" in x for x in validate_mapping(mapping, style)))
def test_execution_catalog_style_hash_and_schema(self): def test_execution_catalog_style_hash_and_schema(self):
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:

View File

@@ -63,13 +63,21 @@ def validate_mapping(mapping: dict[str, Any], style: dict[str, Any] | None = Non
errors.append("mapping:renderEvidenceSource:invalid") errors.append("mapping:renderEvidenceSource:invalid")
layers = style.get("layers", []) if style is not None else None layers = style.get("layers", []) if style is not None else None
by_id = {layer.get("id"): (index, layer) for index, layer in enumerate(layers or [])} by_id = {layer.get("id"): (index, layer) for index, layer in enumerate(layers or [])}
seen_evidence: dict[str, bytes] = {}
for i, row in enumerate(mapping.get("assignments", [])): for i, row in enumerate(mapping.get("assignments", [])):
evidence = row.get("renderEvidence") evidence = row.get("renderEvidence")
if evidence is None: if evidence is None:
continue continue
visual_id = row.get("visualObjectId")
encoded = canonical(evidence)
if visual_id in seen_evidence and seen_evidence[visual_id] != encoded:
errors.append(f"mapping[{i}]:renderEvidence:nondeterministic_duplicate")
seen_evidence[visual_id] = encoded
if not isinstance(evidence, dict) or not isinstance(evidence.get("sourceLayer"), str) or not isinstance(evidence.get("geometryRole"), str) or not isinstance(evidence.get("occurrenceCount"), int) or evidence["occurrenceCount"] < 0 or not isinstance(evidence.get("components"), list): if not isinstance(evidence, dict) or not isinstance(evidence.get("sourceLayer"), str) or not isinstance(evidence.get("geometryRole"), str) or not isinstance(evidence.get("occurrenceCount"), int) or evidence["occurrenceCount"] < 0 or not isinstance(evidence.get("components"), list):
errors.append(f"mapping[{i}]:renderEvidence:invalid") errors.append(f"mapping[{i}]:renderEvidence:invalid")
continue continue
if re.search(r"<script|javascript:|eval\s*\(|function\s*\(|=>", json.dumps(evidence, ensure_ascii=False), re.IGNORECASE):
errors.append(f"mapping[{i}]:renderEvidence:script_forbidden")
previous = -1 previous = -1
for j, component in enumerate(evidence["components"]): for j, component in enumerate(evidence["components"]):
if not isinstance(component, dict): if not isinstance(component, dict):
@@ -80,8 +88,6 @@ def validate_mapping(mapping: dict[str, Any], style: dict[str, Any] | None = Non
if not isinstance(layer_id, str) or not isinstance(index, int) or isinstance(index, bool) or not isinstance(component.get("role"), str) or not component["role"] or (component.get("resourceKey") is not None and not isinstance(component["resourceKey"], str)) or (component.get("resourceVisualHash") is not None and not isinstance(component["resourceVisualHash"], str)): if not isinstance(layer_id, str) or not isinstance(index, int) or isinstance(index, bool) or not isinstance(component.get("role"), str) or not component["role"] or (component.get("resourceKey") is not None and not isinstance(component["resourceKey"], str)) or (component.get("resourceVisualHash") is not None and not isinstance(component["resourceVisualHash"], str)):
errors.append(f"mapping[{i}]:component[{j}]:invalid") errors.append(f"mapping[{i}]:component[{j}]:invalid")
continue continue
if any(token in value.lower() for value in (layer_id, component["role"], component.get("resourceKey") or "") for token in ("<script", "javascript:", "eval(", "function(", "=>")):
errors.append(f"mapping[{i}]:component[{j}]:script_forbidden")
if index < previous: if index < previous:
errors.append(f"mapping[{i}]:component_order:invalid") errors.append(f"mapping[{i}]:component_order:invalid")
previous = index previous = index