Two more from the follow-up list closed (569 tests passing):
1. ``ast.Subscript`` resolution. ``open(['/etc/shadow'][0])`` and
``open({'k': '/etc/shadow'}['k'])`` previously slipped because
``_extract_string_from_node`` had no Subscript handler. List /
tuple / dict subscripts are now resolved: when the index is a
static constant we return the indexed value; otherwise any
sensitive entry in the container surfaces so the gate fires.
Indexes outside the container's static range fall back to
sensitive-scan + first-resolvable so adversarial patterns like
``open(['safe.txt', '/etc/shadow'][i])`` are still blocked.
2. UDP / ``connect_ex`` metadata destination. The connect-only
``NetworkAndIoVisitor`` gate missed ``s.sendto(data, address)`` /
``s.sendmsg(buffers, ancdata, flags, address)`` (the destination
tuple is positional but not at index 0) and ``s.connect_ex(addr)``
(non-raising connect variant). The visitor now matches the full
``{connect, connect_ex, sendto, sendmsg}`` set and scans every
positional arg for a ``(host, port)`` tuple shape; the first
resolved host wins.
17 new regression tests cover the Subscript class (8 blocked, 3
allowed) and the UDP / connect_ex class (4 blocked, 2 allowed).
After this commit, ``bypass_hunt.py`` reports zero NEW bypasses;
the only remaining ALLOWs are the documented follow-up list
(``getattr(__builtins__, ...)``, ``vars(__builtins__)[...]``,
``base64.b64decode`` of paths, ``chr()`` / ``str.join`` concat,
trusted-host upload-shape evasion).