Developer guide

UN Security Council sanctions data in Python: inspect an official XML file

Inspect an official UN sanctions XML snapshot in Python, retain its hash and provenance, and keep structural parsing separate from screening approval.

On this page

Choose the official-data path deliberately

Use the XML link published on the UN Security Council Consolidated List page. The list combines individuals and entities across different sanctions regimes. Preserve permanent references and committee or regime context instead of treating every record as subject to identical measures.

SanctionsKit’s published coverage currently describes this source as reference-only. This guide does not invent a selectable API source ID or promise a production screening endpoint for it. It provides a complete local snapshot-inspection utility, not a matching engine or a finished ingestion service.

Download and record the actual provenance

Follow the official authority page to its current XML download. Save the original bytes as snapshot.xml without editing them. Record the exact official HTTPS URL in SOURCE_URL and the actual download time in DOWNLOADED_AT using a UTC ISO timestamp such as YYYY-MM-DDTHH:MM:SSZ.

The script reads a local file and does not fetch arbitrary URLs. A source URL and SHA-256 hash make the captured artifact traceable; neither proves that the operator downloaded authentic data or that the file is complete and current.

Install the parser dependency

Use Python 3.11 or later for this separate data utility, and install the XML dependency below. Save the client as inspect.py. Keep the parser dependency in your own project environment, not the SanctionsKit content folder.

The utility accepts UTF-8 XML, limits input to 50 MiB, rejects DTD and entity declarations, and refuses obvious HTML or error document roots. Other encodings or larger approved feeds need a deliberately reviewed adapter rather than silently bypassing the checks.

python -m pip install "defusedxml>=0.7.1,<0.8"

Inspect the snapshot without activating it

Successful output has status parsed_not_approved. It records the bytes, SHA-256, root element, and supplied provenance, but intentionally does not produce potential_match or no_match. A file that parses successfully can still be stale, incomplete, or the wrong source schema.

"""Python 3.11+. pip install defusedxml. Structural inspection, not a matcher."""
import hashlib
import json
import os
import re
import sys
from datetime import datetime
from pathlib import Path
from urllib.parse import urlparse
from defusedxml import ElementTree

def main() -> None:
    if len(sys.argv) != 3 or sys.argv[1] not in ("eu", "un"):
        raise ValueError("Usage: python inspect.py eu|un snapshot.xml")
    authority, filename = sys.argv[1:]
    source = urlparse(os.environ["SOURCE_URL"])
    host = source.hostname or ""
    allowed = host in ("scsanctions.un.org", "main.un.org") if authority == "un" else (host == "europa.eu" or host.endswith(".europa.eu"))
    if source.scheme != "https" or source.username or source.password or not allowed:
        raise ValueError("Use an approved official HTTPS source URL")
    downloaded = os.environ["DOWNLOADED_AT"]
    if not re.fullmatch(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z", downloaded):
        raise ValueError("Record the actual UTC download timestamp")
    datetime.fromisoformat(downloaded.replace("Z", "+00:00"))
    path = Path(filename)
    limit = 50 * 1024 * 1024
    if path.stat().st_size > limit:
        raise ValueError("Snapshot exceeds 50 MiB")
    raw = path.read_bytes()
    if not raw or len(raw) > limit or b"\x00" in raw:
        raise ValueError("Empty, oversized, or unsupported-encoding snapshot")
    text = raw.decode("utf-8-sig", errors="strict")
    root = ElementTree.fromstring(text, forbid_dtd=True, forbid_entities=True, forbid_external=True)
    tag = root.tag.rsplit("}", 1)[-1]
    if tag.lower() in ("html", "error"):
        raise ValueError("Unexpected document root")
    print(json.dumps({
        "authority": authority, "sourceUrl": source.geturl(), "downloadedAt": downloaded,
        "sha256": hashlib.sha256(raw).hexdigest(), "byteLength": len(raw),
        "rootElement": root.tag, "status": "parsed_not_approved",
        "checksStillRequired": ["source authenticity", "current source schema", "record identifiers", "completeness", "normalization", "matching tests"],
    }, indent=2))

if __name__ == "__main__":
    try:
        main()
    except Exception:
        print("Snapshot inspection failed. Keep the previous approved dataset active.", file=sys.stderr)
        sys.exit(1)

Run and retain the inspection manifest

Set SOURCE_URL and DOWNLOADED_AT to the actual recorded values, then run the command below. Save the manifest with the original snapshot. Keep the previously approved dataset active if a new file fails inspection; a failed refresh must not publish an empty sanctions dataset.

python inspect.py un snapshot.xml > snapshot-manifest.json

Define source-specific normalization before matching

For a UN adapter, preserve permanent reference numbers, individual/entity distinctions, original-script names, alternative birth dates, and alias-quality information. A low-quality alias is not sufficient by itself to establish identity. Do not turn the source’s unavailable values into actual names, dates, or nationalities.

Before activating an ingestion adapter, validate its current schema, check required identifiers, compare counts and changes with the prior approved version, and test normalization on representative records. Keep the full original snapshot for reconstruction under your approved handling policy.

Build the next controls explicitly

Matching, review, freshness enforcement, and legal interpretation remain separate work. A successful parser cannot determine whether a customer is the listed identity or whether a proposed activity is authorized. See the data-freshness guide for the difference between downloaded, parsed, approved, and active data.

  • Test empty files, truncated XML, HTML responses, and forbidden declarations.
  • Test namespaces, Unicode names, partial dates, and repeatable snapshot hashes.
  • Document the expected schema before accepting a new source version.
  • Keep refresh failures visible and preserve the prior approved dataset.
  • Do not report unsupported API coverage as a successful screening.

Official references