Closes additional bypass classes surfaced while exercising the gate:
1. Module / function aliasing (`m = os; m.system(...)`,
`p = os.popen; p(...)`): `visit_Assign` now propagates the source
alias when one tracked-module name is bound to another, and tracks
bound method references into `shell_exec_aliases`. Previously only
`m = __import__('os')` was handled.
2. Importlib from-alias (`from importlib import import_module as IM;
IM('os').system(...)`): a new visitor-scope `import_module_aliases`
set plus a `_resolve_dynamic_module` wrapper recognises the
bound name in both inline-call and bound-name forms.
3. Shutil directory exfil (`shutil.copytree('~/.ssh', dst)`):
`_matches_sensitive_dir()` adds a directory-only matcher used by
the file-copy gate only. The boundary `(?=/?$|/?[\s'\";&|)<>])`
matches the path AS the directory but NOT a single file inside it,
so per-file allow-listed reads (`~/.ssh/known_hosts`, `~/.ssh/id_rsa.pub`)
still pass. Covers `.ssh`, `.aws`, `.config/gcloud`, `.gnupg`,
`.docker`, `.kube`, `.password-store`, plus `/etc`, `/etc/ssh`,
`/var/spool/cron`, `/proc/<pid>`.
4. Explicit-reader / aliased file readers (`io.FileIO('/etc/shadow')`,
`codecs.open('/etc/shadow')`, `from io import FileIO; FileIO(...)`):
the open-call detector now recognises these qualified forms and
the visitor tracks `from io|codecs import FileIO|open` aliases.
5. Bytes-literal paths (`open(b'/etc/shadow')`) and walrus
expressions (`open((p := '/etc/shadow'))`): `_extract_string_literal`
and `_extract_string_from_node` resolve `bytes` Constants via strict
UTF-8 decode and `NamedExpr` via RHS extraction (recording the
binding so later uses of the walrus target resolve too).
6. Tuple / list unpacking destructuring (`(a, b) = ('/etc', 'shadow');
open(a + '/' + b)` and `p, = ['/etc/shadow']; open(p)`): the
string-binding pre-pass now folds matched-length Tuple/List
destructurings element-wise.
7. Pandas / numpy file readers (`pd.read_csv('/etc/shadow')`,
`np.fromfile('/etc/shadow')`, etc.): suffix-match the common
reader method names so any alias of the source module flows
through the same sensitive-path gate as `open()`.
91 new regression tests cover each class, both blocked and legitimate
allow-list cases. Full sandbox suite: 473 passed.