Close five gaps Codex found on the round-58 branch (four inline P1 plus one
review-body P1).
- exec/eval namespace-dict aliases: the outward-reference model for an exec /
eval payload only collected ast.Name loads, so a constant-key namespace lookup
(exec("globals()['f']('...')"), and the locals() / vars() forms) referenced the
caller's f = os.system without a Name node and slipped past the alias check.
Treat a literal key of a bare globals() / locals() / vars() subscript as a free
outward reference so the caller-alias resolution runs on it.
- bare-host network APIs: http.client.HTTPConnection / HTTPSConnection and the
socket name-resolution helpers (getaddrinfo, gethostbyname, gethostbyname_ex)
take a HOST, not a URL, so their literal first arg (or host= keyword) was parsed
as a scheme://host URL, found no scheme, and was never checked. Check the literal
directly against the metadata denylist / allowlist for these callees, and add the
forward name-resolution helpers to the scanned set.
- client-instance network calls: a request chained off a client constructor
(requests.Session().get(url), httpx.Client().get(url), build_opener().open(url),
or s.get(url) where s was bound to such a constructor) has a fully-qualified name
of just the method, so it never matched a module-rooted network prefix and the
host went unchecked. Match these by the receiver being a client-ctor call or a
same-name single-assignment alias, extract the host (arg1 for .request), and run
the same host check. A .get / .open on a plain dict or file receiver is excluded.
- shuf / uniq output writers: shuf -o / --output and a uniq second (output) operand
write outside the workdir in an unguarded child that the realpath backstop cannot
see, so they are blocked like the existing sort -o full-block. uniq skip-field /
skip-char counts are not treated as output operands.
- module docstring under the guard splice: a leading string literal is the module
docstring only while it is the first statement, so prepending the runtime guard
ahead of it made __doc__ None. Splice the guard after a leading docstring (as is
already done for future imports) so the docstring stays first; a same-line
"\"\"\"doc\"\"\"; write" tail still moves after the guard and is confined.
Regression coverage: TestRound59Bypasses in tests/test_sandbox_tools.py (exec /
eval namespace-dict aliases, bare-host metadata / allowlist checks, client-instance
and aliased-instance request calls, shuf / uniq output writers, plus a round59
benign-allowed set: trusted-host requests / sessions / HTTPConnection / getaddrinfo,
dict.get and file .open, a benign exec payload, and shuf / uniq without an output
operand) and, in tests/test_sandbox_runtime_backstop.py, a module-docstring-preserved
case and a docstring same-line write-escape denial.