Adds _find_sensitive_paths() and wires it into _bash_exec (alongside the
existing _find_blocked_commands check) and into _check_args_for_blocked
(so the Python AST gate catches os.system('cat ~/.ssh/id_rsa') the same
way bash $ cat ~/.ssh/id_rsa is caught).
The pattern set is intentionally narrow — only clear-cut credential and
process-state targets:
Home-anchored (must be prefixed by ~, $HOME, ${HOME}, /home/<u>,
/root, /Users/<u>):
.ssh/id_rsa, .ssh/id_ed25519, .ssh/id_ecdsa, .ssh/id_dsa, .ssh/identity
.aws/credentials, .docker/config.json, .kube/config
.config/gcloud/{application_default_credentials,access_tokens,credentials}
.pypirc, .npmrc, .cargo/credentials
.netrc, .password-store, .gnupg/private-keys-v1.d
Absolute system targets (match anywhere):
/etc/shadow, /etc/sudoers, /etc/ssh/ssh_host_*
/proc/{self,<pid>}/{environ,mem,maps,auxv}
/proc/kcore, /proc/kallsyms
/var/spool/cron/
The home-anchored category uses a regex that requires a HOME-equivalent
prefix, so project-local rc files like ./project/.npmrc remain readable
while ~/.npmrc is denied. Legitimate LLM-developer-tool paths
(~/.gitconfig, ~/.bashrc, ~/.ssh/config, ~/.ssh/known_hosts, /etc/hosts,
~/.cache/, ~/.bash_history, project rc files) are intentionally NOT in
the list and still flow through unchanged.
Closes gaps #1, #2, #3, #12, #13 from the documented 13-gap audit.
Regression sweep:
* 131/131 studio/backend/tests/test_sandbox_tools.py pass
* 24 legitimate-use cases verified ALLOWED
* 17 attack patterns verified BLOCKED