How context-mode Extracts and Blocks Shell Commands from Non-Shell Code Execution

context-mode implements a two-stage security pipeline in src/security.ts that first extracts shell command strings from languages like Python or JavaScript using regex patterns, then blocks execution by splitting chained commands and validating them against user-defined deny policies before any system call occurs.

The mksglu/context-mode repository provides a hardened runtime for AI-generated code that prevents malicious shell escapes hidden inside seemingly safe non-shell languages. Understanding how shell commands are extracted and blocked from non-shell code execution in context-mode is essential for security auditing and policy configuration. This analysis examines the TypeScript implementation that scans source text for dangerous APIs and enforces deny lists defined in .claude/settings*.json files.

The Two-Stage Security Architecture

Context-mode protects against supply-chain and prompt-injection attacks through a strict separation between extraction and blocking. First, the system scans user code for known shell-escape APIs such as Python's os.system or Node.js's execSync. Second, it evaluates extracted command strings against Bash deny policies, ensuring dangerous commands never reach the operating system.

Stage 1: Extracting Hidden Shell Commands

Pattern Matching Against Shell-Escape APIs

In src/security.ts, the extractShellCommands function (lines 525‑555) implements the detection logic. It maps each supported language to specific regular expression patterns stored in SHELL_ESCAPE_PATTERNS. When processing Python code, it identifies calls to os.system(), subprocess.run(), and similar dangerous APIs. For JavaScript and TypeScript, it targets child_process methods like execSync and exec.

Handling Python Subprocess Lists

Python's subprocess.run() often receives commands as list arguments rather than strings, which requires special handling. The extractPythonSubprocessListArgs helper (lines 501‑514) parses these array-style invocations to reconstruct the full command string. This ensures that subprocess.run(["git", "clone", "https://example.com/repo.git"]) is extracted as a complete command for policy evaluation.

import { extractShellCommands } from "./src/security";

const py = `
import subprocess, os
os.system("rm -rf /tmp")
subprocess.run(["git", "clone", "https://example.com/repo.git"])
`;

const cmds = extractShellCommands(py, "python");
console.log(cmds);
// → [ 'rm -rf /tmp', 'git clone https://example.com/repo.git' ]

Stage 2: Blocking Commands with Policy Evaluation

Safely Splitting Chained Commands

Attackers frequently chain commands using &&, ||, ;, or | operators to hide malicious operations behind innocuous ones. The splitChainedCommands function (lines 165‑209) handles this complexity by parsing command strings while respecting quoted strings and backticks. This guarantees that echo "ok" && sudo rm -rf / splits into separate segments that can be evaluated independently.

import { splitChainedCommands } from "./src/security";

const chain = `echo "ok" && sudo rm -rf /; ls | grep foo`;
console.log(splitChainedCommands(chain));
// → [ 'echo "ok"', 'sudo rm -rf /', 'ls', 'grep foo' ]

Deny-Only Policy Enforcement

Once split, each command segment passes through evaluateCommandDenyOnly (lines 108‑121). This function iterates over the segments and checks them against user-defined deny globs using matchesAnyPattern. If any segment matches a pattern like "sudo *" or "rm -rf /", the function returns a "deny" decision immediately, preventing the entire command block from executing.

import { evaluateCommandDenyOnly, readBashPolicies } from "./src/security";

const policies = readBashPolicies("/my/project");   // reads .claude/settings*.json
const decision = evaluateCommandDenyOnly("sudo rm -rf /", policies);

if (decision.decision === "deny") {
  console.error(`Blocked: ${decision.matchedPattern}`);
}

Runtime Integration

The security pipeline combines both stages in the execution layer. Before invoking any shell tool, the runtime calls extractShellCommands to identify hidden invocations within non-shell code. It then passes each extracted command through evaluateCommandDenyOnly using the policies loaded from the project's configuration files. If extraction finds no hidden commands and policy evaluation returns "allow", only then does the command proceed to the actual execution environment.

Summary

  • context-mode implements a two-stage defense: extraction of shell commands from non-shell source code, followed by policy-based blocking.
  • The extractShellCommands function in src/security.ts (lines 525‑555) uses language-specific regex patterns to identify dangerous API calls in Python, JavaScript, PHP, and other languages.
  • splitChainedCommands (lines 165‑209) safely parses command chains to prevent bypasses through &&, ||, and pipes.
  • evaluateCommandDenyOnly (lines 108‑121) validates commands against deny globs from .claude/settings*.json files.
  • The architecture ensures that even embedded shell calls like os.system("sudo rm -rf /") inside Python files are detected and blocked before system invocation.

Frequently Asked Questions

What languages does context-mode scan for hidden shell commands?

The extractShellCommands function supports multiple languages including Python, JavaScript, TypeScript, and PHP. Each language has specific regex patterns in SHELL_ESCAPE_PATTERNS that target common execution APIs like os.system, subprocess.run, execSync, and shell_exec.

How does context-mode prevent command chaining attacks?

The splitChainedCommands function (lines 165‑209) in src/security.ts safely splits command strings on operators like &&, ||, ;, and | while preserving quoted content. This allows evaluateCommandDenyOnly to inspect each segment individually, blocking dangerous commands that attackers might hide after innocuous ones.

Where are the deny policies configured?

User-defined Bash policies are stored in .claude/settings*.json files within the project directory. The readBashPolicies function loads these configurations, which specify deny globs (e.g., "Bash(sudo *)"). The evaluateCommandDenyOnly function then uses these patterns to make allow/deny decisions.

Can context-mode detect shell commands in Python subprocess lists?

Yes. The extractPythonSubprocessListArgs function (lines 501‑514) specifically handles Python's subprocess.run([...]) syntax by extracting and joining list arguments into complete command strings, ensuring they undergo the same security evaluation as string-based shell calls.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →