{"uuid": "3b940e08-b7e4-44f2-acab-baacdb52956d", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "cve-2026-17084", "type": "seen", "source": "https://gist.github.com/raeq/95b1fc5a314951741461bae554e7d6bf", "content": "\"\"\"Reproduction for raeq/disarm#713 \u2014 CVE-2026-17084 against disarm 0.14.1.\n\nRebuilds CPython's stringprep table B.3 from the fix commit and its parent,\nfinds every code point the two disagree on, and asks which disarm entry points\nmap the input, the pre-fix output and the post-fix output to a single key.\n\nNeeds network (two files from raw.githubusercontent.com) and disarm importable.\nExpected output:\n\n    684\n    fold_case              684/684\n    search_key             684/684\n    catalog_key            684/684\n    canonicalize           7/684\n    canonicalize_strict    7/684\n    strip_obfuscation      7/684\n    normalize_confusables  7/684\n\nCompanion data: cve-2026-17084-b3-delta.json holds the 684 triples this prints\nthe count of, so a reader can check the set without the network round trip.\n\"\"\"\n\nimport ast, urllib.request\nimport disarm\n\nSHA = \"7e109d084d55e7eb25837a5f3b47ef9beee547bc\"\nBASE = \"https://raw.githubusercontent.com/python/cpython/{}/Lib/stringprep.py\"\n\ndef b3(ref):\n    src = urllib.request.urlopen(BASE.format(ref)).read().decode()\n    for node in ast.walk(ast.parse(src)):\n        if isinstance(node, ast.Assign) and any(\n            getattr(t, \"id\", None) == \"b3_exceptions\" for t in node.targets\n        ):\n            return ast.literal_eval(node.value)\n\ndef mapper(table):\n    return lambda ch: table.get(ord(ch), ch.lower())\n\nbuggy, fixed = mapper(b3(SHA + \"^\")), mapper(b3(SHA))\n\ndelta = [\n    (cp, buggy(chr(cp)), fixed(chr(cp)))\n    for cp in range(0x110000)\n    if not 0xD800 &lt;= cp &lt;= 0xDFFF and buggy(chr(cp)) != fixed(chr(cp))\n]\nprint(len(delta))  # 684\n\nfor name in (\"fold_case\", \"search_key\", \"catalog_key\", \"canonicalize\",\n             \"canonicalize_strict\", \"strip_obfuscation\", \"normalize_confusables\"):\n    fn = getattr(disarm, name)\n    ok = sum(1 for cp, b, f in delta if fn(chr(cp)) == fn(b) == fn(f))\n    print(f\"{name:22s} {ok}/{len(delta)}\")\n", "creation_timestamp": "2026-08-29T20:04:44.221187Z"}