test(provider-pipeline): reject unsafe or conflicting render evidence
This commit is contained in:
@@ -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"]["sourceLayer"] = "wrong"
|
||||
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):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
|
||||
@@ -63,13 +63,21 @@ def validate_mapping(mapping: dict[str, Any], style: dict[str, Any] | None = Non
|
||||
errors.append("mapping:renderEvidenceSource:invalid")
|
||||
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 [])}
|
||||
seen_evidence: dict[str, bytes] = {}
|
||||
for i, row in enumerate(mapping.get("assignments", [])):
|
||||
evidence = row.get("renderEvidence")
|
||||
if evidence is None:
|
||||
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):
|
||||
errors.append(f"mapping[{i}]:renderEvidence:invalid")
|
||||
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
|
||||
for j, component in enumerate(evidence["components"]):
|
||||
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)):
|
||||
errors.append(f"mapping[{i}]:component[{j}]:invalid")
|
||||
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:
|
||||
errors.append(f"mapping[{i}]:component_order:invalid")
|
||||
previous = index
|
||||
|
||||
Reference in New Issue
Block a user