Closes two static-bypass classes flagged during round-3 review:
1. __import__('os').system(...) / importlib.import_module('os').popen(...)
bypassed the bare os.system / subprocess.* gate because the receiver
was an ast.Call rather than an ast.Name in os_aliases. Adds
_resolve_dynamic_module_name() so:
* inline __import__('os').system(...)
* inline importlib.import_module('os').system(...)
* import importlib; mod = importlib.import_module('os'); mod.popen(...)
* m = __import__('subprocess'); m.run([...], shell=True)
all flow through the same shell-escape detection as
import os; os.system(...). Legit dynamic imports of safe modules
(json, pathlib, ...) remain allowed.
2. /proc/<pid>/cwd and /proc/<pid>/root are symlinks to the process
working directory and the filesystem root. The form
open(/proc/self/cwd/../../etc/shadow) bypassed
_normalize_path_separators because .. was collapsed against the
literal path, not the symlinked target. The form
open(/proc/self/root/etc/shadow) bypassed any chroot-style
defence. Adds matching entries to _ABSOLUTE_SENSITIVE so the bash
gate and the AST open() gate both block any access via these
symlink prefixes. Legitimate /proc/self/status etc. introspection
still flows.
Tests:
TestFollowup_DynamicImportShellEscape (2 cases, 10 parametrised)
TestFollowup_ProcSelfSymlinkTraversal (3 cases, 16 parametrised)
pytest studio/backend/tests/test_sandbox_hardening.py -q
-> 382 passed in 0.67s (was 356).
|
||
|---|---|---|
| .. | ||
| assets | ||
| auth | ||
| core | ||
| loggers | ||
| models | ||
| plugins | ||
| requirements | ||
| routes | ||
| state | ||
| storage | ||
| tests | ||
| utils | ||
| __init__.py | ||
| _platform_compat.py | ||
| colab.py | ||
| main.py | ||
| run.py | ||
| startup_banner.py | ||