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"]["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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user