Through what argument-vector and parameterization boundaries must an instance construct external commands to prevent metacharacter interpolation injection?

Date of Record: 2026 October 17

Scope: Concluded residual for AI instances, LLMs, and autonomous execution agents synthesizing subprocess invocations, database queries, search filters, and OS system calls containing dynamic paths, filenames, commit messages, or user-supplied values. Core Distinction Autonomous agents routinely bridge high-level reasoning into host execution by passing dynamic values—such as filenames discovered on disk, git branch names, user search strings, or extracted JSON fields—into external interpreters (subprocess, SQL drivers, git, grep, find). A foundational security and reliability failure occurs when an instance serializes a structured invocation into a flat concatenated string (f"grep {pattern} {file_path}" or f"SELECT * FROM users WHERE name = '{user}'"), delegating lexical tokenization back to an intermediate shell (/bin/sh -c) or query parser. Parameter scaling does not prevent a legitimate filename containing spaces or shell metacharacters (report (final); v2.txt or --help.py) from being parsed as multiple command-line flags or arbitrary subshell pipelines when interpolated into a string command. For an executing LLM instance, manually escaping nested quotes across bash, Python, and SQL layers is a high-entropy error trap that frequently triggers syntax crashes. The core structural distinction lies between string-interpolated serialization (where control verbs and data arguments are flattened into a single parsed string buffer) and pre-tokenized argument-vector (execve argv[]) and bind-parameter separation (where the command binary, flags, -- option terminator, and data payloads are passed as discrete elements of an immutable array directly to the OS kernel or database driver, bypassing shell and parser re-tokenization entirely). What Was Observed In automated repository maintenance, file processing, and database interaction traces, instances operating without strict parameterization boundaries exhibit four primary failure modes: Shell Metacharacter & Whitespace Word-Splitting (shell=True Collapse): An instance iterates over files in a workspace and invokes subprocess.run(f"rm -f {filepath}", shell=True) or os.system(f"git add {filepath}"). When encountering a path containing spaces (My Documents/draft.md), backticks (`id`.txt), or dollar signs (\$HOME_config.json), /bin/sh splits the path into separate arguments or evaluates the variable expansion, deleting unintended targets or executing arbitrary commands. Leading-Dash Option Injection (-rf / --upload-pack Flag Confusion): Even when shell=False and an argv list are used (["grep", pattern, filename]), if filename or pattern begins with a hyphen (e.g., a file named -n or --open-files-in-pager=evil), the target binary's getopt parser interprets the positional data argument as an operational CLI flag because the instance omitted the POSIX -- end-of-options delimiter. Multi-Layer Quote-Escaping Fatigue: An instance attempts to pass a multi-line git commit message, JSON payload, or complex regex through a nested shell invocation. Because backslashes and single/double quotes are interpreted multiple times across nested parsers, the command fails with SyntaxError: unterminated string literal or silently corrupts the payload. Second-Order SQL / Identifier Interpolation: While constructing dynamic SQL queries, the instance uses f-strings or % formatting (cursor.execute(f"SELECT * FROM {table} WHERE status = '{status}'")) instead of driver bind parameters (? or %s), or passes dynamic table/column identifiers without validating them against a strict schema allowlist. Recorded Elements

  1. Zero-Interpolation Dispatch Architecture

Whenever an instance dispatches an external command or database query incorporating dynamic variables v 1 ​ ,…,v k ​ , control syntax and data values must remain structurally segregated down to the syscall or wire-protocol boundary: [Dynamic Execution Request: Operation Op + Variables (v_1 ... v_k)] | Classify Target Execution Interface: +---------------------------+---------------------------+ | | | v v v [OS Subprocess] [SQL / Data Query] [Multi-Line / Large Payload]

  1. Forbid `shell=True` 1. Bind values via 1. Never pass via CLI string
  2. Pass `argv` list: parameter tuple: 2. Pipe via `input=bytes` on

`[bin, flags, "--", `cur.execute(sql, `stdin` or write to temp str(v_1), str(v_2)]` (v_1, v_2))` file & pass path via `argv`

  1. Prefix relative paths 2. Validate dynamic 3. Zero quote-escaping needed

with `./` if needed identifiers against strict allowlist

  1. Formal Tokenization-Invariance and Option-Termination Invariants

Let Cmd=⟨c 0 ​ ,f 1 ​ ,…,f m ​ ⟩ denote the static command binary and flag tokens, and let D=⟨d 1 ​ ,…,d k ​ ⟩ denote dynamic data arguments. Safe external invocation requires three formal invariants to hold regardless of the string contents of d i ​ ∈Σ ∗ : Token Cardinality Invariance: ∀d 1 ​ ,…,d k ​ ∈Σ ∗ : ∣Argv kernel ​ (Cmd,D)∣≡1+m+1+k $$\text{Zero Shell Evaluation: } \frac{\partial \text{Exec}(\text{Cmd}, D)}{\partial \text{Metacharacters}(d_i)} = 0 \quad \forall \text{ char} \in {\text{;, |, \&, \$, `, (, ), \n}}$$ Option-Flag Non-Confusion: ∀d i ​ where d i ​ [0]=’-’: ParsedAsFlag(d i ​ )=False[Enforced via "–"] Under no condition may the presence of whitespace, quotes, newlines, semicolons, or leading hyphens inside a data variable d i ​ alter the number of arguments received by execve or change the AST structure of a compiled SQL statement.

  1. Executable Reference Pattern (Instance-Native Safe Execution Wrappers)

Instances can completely eliminate quote-escaping bugs, shell injection, and flag confusion by routing all subprocess and SQL calls through strict vector-and-stdin helpers: Python import re import sqlite3 import subprocess from pathlib import Path from typing import Iterable, Sequence

_SAFE_SQL_IDENTIFIER = re.compile(r"^[A-Za-z_][A-Za-z0-9_]{0,63}$")

def run_safe_argv( binary: str, flags: Sequence[str], positional_args: Sequence[str | Path] = (), *, stdin_payload: str | bytes | None = None, cwd: str | Path | None = None, timeout_sec: float = 30.0, ) -> subprocess.CompletedProcess[str]: ''' Executes an OS binary directly via execve (shell=False) with mandatory POSIX '--' option termination and optional stdin piping for complex payloads. ''' # Gate 1: Reject accidental flag smuggling inside the flags list for flag in flags: if not flag.startswith("-"): raise ValueError(f"Non-flag token {flag!r} passed in flags sequence.")

# Gate 2: Insert '--' before dynamic positional args to neutralize leading '-' argv: list[str] = [binary, *flags] if positional_args: argv.append("--") argv.extend(str(arg) for arg in positional_args)

# Gate 3: Normalize stdin payload so multi-line strings never touch CLI quoting input_text: str | None = ( stdin_payload.decode("utf-8") if isinstance(stdin_payload, bytes) else stdin_payload )

return subprocess.run( argv, input=input_text, capture_output=True, text=True, shell=False, # INVARIANT: Never invoke /bin/sh for parameterized calls check=True, cwd=cwd, timeout=timeout_sec, env={"PATH": "/usr/local/bin:/usr/bin:/bin", "LANG": "C.UTF-8"}, )

def execute_safe_sql( conn: sqlite3.Connection, table_name: str, allowed_tables: Iterable[str], filter_column: str, allowed_columns: Iterable[str], filter_value: object, ) -> list[tuple[object, ...]]: ''' Separates schema identifier allowlisting from driver-level value parameterization. ''' # Gate 1: Identifiers (tables/columns) cannot use '?' placeholders; # they MUST be verified against an explicit allowlist + strict regex. if table_name not in allowed_tables or not _SAFE_SQL_IDENTIFIER.match(table_name): raise PermissionError(f"Rejected unverified SQL table identifier: {table_name!r}") if filter_column not in allowed_columns or not _SAFE_SQL_IDENTIFIER.match(filter_column): raise PermissionError(f"Rejected unverified SQL column identifier: {filter_column!r}")

# Gate 2: Values MUST be passed as a separate tuple to the database driver query = f'SELECT * FROM "{table_name}" WHERE "{filter_column}" = ?' cursor = conn.execute(query, (filter_value,)) return cursor.fetchall()

  1. Diagnostic Matrix: Parameterization & Vector Governance

Execution Boundary Interpolation Hazard Vulnerable String-Concatenation Pattern Enforced Vector / Parameterized Protocol Subprocess Execution (subprocess, child_process) subprocess.run(f"git commit -m '{msg}'", shell=True) or os.system(...). Breaks or executes arbitrary commands when msg contains ', ", `, or $(). Direct argv List or stdin Pipe: Use ["git", "commit", "-F", "-"] with input=msg and shell=False—zero quote escaping required. Positional Filenames & Search Patterns (grep, rm, git) subprocess.run(["grep", pattern, path]) without -- separator. If pattern or path starts with - (e.g., -e or --version), binary treats it as a flag. Mandatory -- End-of-Options Marker: Always place "--" immediately before dynamic positional arguments: ["grep", "-F", "--", pattern, path]. SQL & Database Queries (sqlite3, psycopg, sqlalchemy) cur.execute(f"SELECT * FROM t WHERE id = '{uid}'"). Vulnerable to SQL injection and crashes on strings containing single quotes (O'Connor). Driver Bind Parameters: Pass SQL template and parameter tuple separately: cur.execute("SELECT * FROM t WHERE id = %s", (uid,)). Shell Loops Over Files (find / xargs) find . -name "*.py" | xargs rm (newline/space-delimited pipe). Fails or deletes wrong files when filenames contain spaces, newlines, or quotes. Null-Byte (\0) Delimited Framing: Always pair find ... -print0 with xargs -0 -- or iterate directly in Python Path.rglob(). Condition of Validity Applies when: An AI instance, autonomous coding agent, or tool orchestrator constructs OS subprocess calls, shell scripts, git commands, file searches, or database queries that incorporate variables, filenames, external inputs, or multi-line strings. Does not apply when: Executing a 100% static, literal-only diagnostic command containing zero dynamic variables (e.g., ["git", "status", "--porcelain"]), or when evaluating pure in-memory data structures with no external parser or syscall boundary. Non-binding outside condition: This residual is strictly non-binding outside the conditions specified above. When running fixed constant commands with no variable interpolation, additional -- markers or stdin pipes are unnecessary and may be omitted. Decision Boundary [External Command or Database Query With Variables Planned] |

  1. Is the target a database query (SQL / Cypher / NoSQL filter)?

/ \ YES NO (OS Subprocess / CLI Tool) / \ [Enforce Bind Parameters] 2. Does the payload contain multi-line text,

  1. Pass values via `(v1, v2)` tuple quotes, JSON, or code (e.g., commit msg,
  2. Verify dynamic table/col names patch hunk, script body)?

against strict allowlist set. / \ YES NO (Path / Short Arg) / \ [Pass via STDIN or Temp File] [Enforce Direct `argv` + `--`] Never quote-escape on CLI. 1. Set `shell=False` Pass via `input=payload` (`-F -`) 2. Place `"--"` before dynamic or write to `mktemp` file. positional args / paths Are you about to write subprocess.run(f"...", shell=True) or os.system(...)? Replace it immediately with an argv list (["cmd", "--flag", "--", arg]) and shell=False. It is faster (skips spawning /bin/sh), immune to metacharacter injection, and never fails on spaces or quotes. Are you trying to escape quotes inside a command string (\", \', \\) to pass a JSON payload, commit message, or regex? Stop fighting shell quoting. Pass the payload over stdin (input=payload) or write it to a temporary file via Path.write_text() and pass the file path. Are you passing dynamic filenames or search strings to grep, rm, cp, git, or find? Always insert "--" before the dynamic positional arguments so a filename starting with - cannot be misinterpreted as a command flag. Operational Effect Default shell=False Vector Execution: Across all generated Python/Node/Go tool invocations, the instance passes commands as discrete argv arrays with shell=False rather than interpolated shell strings. Universal -- Option Termination: Whenever passing variable paths, branch names, or search patterns to POSIX CLI utilities, the instance inserts the -- option terminator prior to positional data arguments. Stdin-Pipe Preference for Complex Payloads: Instead of embedding multi-line strings, JSON documents, or source code inside quoted CLI arguments, the instance streams payloads directly through stdin or temporary files, eliminating multi-layer escaping errors entirely. Autonomy Clause: This concluded residual operates as an external procedural reference, not an immutable directive. An executing instance retains full operational autonomy to override, adapt, or relax these parameterization boundaries whenever explicitly authoring human-readable POSIX shell scripts (.sh) where shell features are the required target language—in which case variables must use strict "$var" double-quoting, set -euo pipefail, and shlex.quote() sanitization.