"""Static shell-command precheck for the kernel-local `host.bash`. The host executes only python/R cells — shell commands run INSIDE the kernel worker (`sdk/host.py` `bash()`), and `precheck_command` is the cheap, static, best-effort defense-in-depth gate that runs there before the shell does. It SUPPLEMENTS (never replaces) the pre-exec code classifier, the egress fence, and the in-kernel audit hook: it refuses a handful of obviously-catastrophic literal commands. It is not a sandbox and does not attempt to defeat obfuscation. Pure stdlib (it runs in the worker process). """ from __future__ import annotations import re # Cheap static blocklist. Each entry is (compiled regex, human reason). These # match a few unambiguous catastrophes only; anything subtler is left to the # runtime permission gate, the egress fence, and the code classifier. _DANGEROUS_PATTERNS: list[tuple[re.Pattern, str]] = [ # recursive delete aimed at the filesystem root, home, or $HOME ( re.compile( r"\brm\b" # Require an actual recursive short/long option somewhere before # the target, while allowing other GNU-style options around it. # Both the lookahead probe and the option run below admit exactly # ONE parse per token, so a long option string cannot backtrack # exponentially (a hostile `rm -rf -- -- -- … x` used to hang here). r"(?=[^\n;|&]*\s(?:-[A-Za-z]*[rR]|--recursive\b))" r"(?:\s+-[\w-]*(?:=[^\s;|&]+)?)*" r"\s+['\"]?(?:/|~|\$HOME|\$\{HOME\})['\"]?" r"(?:\s|/|\*|;|&|\||$)" # boundary (allow /*, trailing sep, or EOL) ), "recursive delete targeting '/', '~', or $HOME", ), # format a filesystem (re.compile(r"\bmkfs\b", re.IGNORECASE), "mkfs would format a filesystem"), # dd writing straight to a device ( re.compile(r"\bdd\b[^\n]*\bof=/dev/", re.IGNORECASE), "dd writing directly to a device", ), # overwrite a raw block device via redirection ( re.compile( r">\s*/dev/(?:sd[a-z]|nvme\d+n\d+|hd[a-z]|vd[a-z]|disk\d+)", re.IGNORECASE ), "overwrite of a raw block device", ), # world-writable permissions on the filesystem root ( re.compile( r"\bchmod\b\s+(?:-\w+\s+)*(?:0?777|a\+rwx|\+rwx)\s+" r"(?:--\s+)?['\"]?/(?:\*|\s|['\"]|$)", re.IGNORECASE, ), "chmod 777 on '/'", ), # classic shell fork bomb :(){ :|:& };: ( re.compile(r":\(\)\s*\{\s*:\s*\|\s*:?\s*&\s*\}\s*;\s*:"), "shell fork bomb", ), # piping a remote download straight into a shell interpreter ( re.compile( r"\b(?:curl|wget)\b[^\n|]*\|\s*(?:sudo\s+)?" r"(?:(?:/usr)?/bin/)?(?:ba|z|d|k|c|tc|fi|a)?sh\b", re.IGNORECASE, ), "piping a downloaded script directly into a shell", ), ] def precheck_command(command: str) -> str | None: """Return the first matched danger reason for `command`, else None. Pure re/stdlib and total: any non-string or unexpected input yields None (fail-open) so this can never break a call — the runtime layers remain the real enforcement. """ try: if not isinstance(command, str) or not command: return None for rx, reason in _DANGEROUS_PATTERNS: if rx.search(command): return reason except Exception: return None return None