# Shell interpolation: agent input को commands बनने से रोकें

Agent को shell tool देने की ज़रूरत नहीं होती कि वह shell execution शुरू कर सके। उसे कोई path, branch, image tag, hostname या resource name दें, फिर उस value को आगे कहीं command string में जोड़ दें। इस तरह untrusted text को चलने वाले program को बदलने का अवसर मिल जाता है।

इसका समाधान कोई अधिक चतुर quote function नहीं है। असली सुधार यह है कि command construction को string formatting समझना बंद करें। किसी action को fixed operation बताना चाहिए, सीमित नियमों वाले typed fields लेने चाहिए, real argument vector के साथ program चलाना चाहिए और authorization को parsing से अलग रखना चाहिए। मैंने इतनी incident reports देखी हैं कि साफ कह सकता हूँ: जो tool free-form command स्वीकार करता है क्योंकि वह «सिर्फ हमारे agent के लिए» है, वह अंततः किसी और के instructions भी स्वीकार करेगा।

Agents इस failure तक पहुँचना आसान बना देते हैं। वे issue text, repository contents, tool output, web pages और chat messages पढ़ते हैं। इनमें से कोई भी input अगले argument को प्रभावित कर सकता है। Agent का नुकसान पहुँचाने का इरादा था या नहीं, इससे फर्क नहीं पड़ता। Operating system या remote host तक पहुँचने वाली boundary को यह मानकर चलना चाहिए कि attacker input को प्रभावित कर सकता है।

## Shell interpolation data को syntax में बदल देता है

Shell interpolation तब होती है जब code process API को अलग-अलग arguments देने के बजाय command interpreter के लिए text बनाता है। Shell को वह text मिलने पर वह अपनी language rules लागू करता है। Separators दूसरा command शुरू कर सकते हैं, command substitutions nested command चला सकती हैं, redirections files बदल सकती हैं और expansions एक value को कई words में बदल सकती हैं।

मान लें agent से release से पहले branch inspect करने को कहा गया है। यह handler देखने में ठीक लगता है:

```python
branch = request["branch"]
command = f"git show {branch}"
subprocess.run(command, shell=True, check=True)
```

अगर `branch` की value `release; id` है, तो shell दो commands देखता है। पहला Git से revision दिखाने को कहता है और दूसरा `id` चलाता है। यही गलती JavaScript में `exec`, Ruby में `system` को दी गई string, Go में `sh -c` और उन CI scripts में दिखती है जो agent output को shell variable में डालकर बाद में नियंत्रित नहीं करतीं।

खतरनाक character agent के prompt में होना ज़रूरी नहीं है। वह pull request title, error message, package manifest या repository scan से लौटे filename में छिपा हो सकता है। Agent बाद के tool call में उस text को quote कर सकता है, क्योंकि उसके task में उसे inspect या repair करने को कहा गया है। केवल यह देखना कि model किसी instruction का पालन करता है या नहीं, उस जगह को छोड़ देता है जहाँ text executable syntax बनता है।

POSIX Shell Command Language में expansions, लागू स्थितियों में field splitting, pathname expansion और quote removal का क्रम शामिल है। यह क्रम महत्वपूर्ण है। Developer अक्सर कहता है, «मैंने इसे quotes में रख दिया है», जैसे quotes कोई सामान्य सुरक्षित string बनाते हों। ऐसा नहीं है। वे किसी खास shell में expansion sequence के किसी खास चरण पर कुछ parsing rules को सीमित करते हैं।

Shell input पाने के कई तरीके भी रखता है। Value command text, environment assignment, redirection target, command substitution, shell startup file या बाद में किसी दूसरे interpreter द्वारा evaluate किए जाने वाले code को प्रभावित कर सकती है। पहले shell के लिए किया गया escaping अगले parser से अपने आप सुरक्षा नहीं देता।

यह अंतर स्पष्ट रखें:

- **Command injection** तब होता है जब input command language द्वारा interpret किए जाने वाले syntax को बदल देता है।
- **Argument injection** तब होता है जब input एक ही argument रहता है, लेकिन called program उसे option या expression की तरह अलग अर्थ में interpret करता है।
- **Authorization failure** तब होता है जब valid argument caller की अनुमति से बाहर का action माँगता है।

Argument vector पहली श्रेणी को रोकता है। दूसरी और तीसरी को नहीं। Workspace से बाहर जाने वाला path, अनपेक्षित object चुनने वाला Git revision और internal service तक पहुँचने वाला URL, तीनों पूरी तरह अलग-अलग arguments हो सकते हैं।

## Quoted command string में अब भी बहुत-से parsers होते हैं

Input को shell command में जोड़ने से पहले quote करना लोकप्रिय है, क्योंकि यह छोटा सुधार लगता है। यह fragile भी है, क्योंकि command अक्सर एक से अधिक grammars से होकर गुजरता है।

मान लें local program SSH call बनाता है और remote system shell चलाता है। Developer branch को local shell के लिए quote कर सकता है, फिर SSH के लिए दोबारा और remote shell के लिए तीसरी बार। हर layer में whitespace, quotes, backslashes, command substitution और encoding के बारे में अलग assumptions होती हैं। बाद में maintenance change quoting की एक layer हटा सकता है या पहले से quoted string को नए context में रख सकता है। Code सावधान दिखता रहता है, जबकि protection खत्म हो चुकी होती है।

यह pattern खास तौर पर खराब है:

```javascript
const command = `git checkout '${branch}'`;
execFile("ssh", [host, command], callback);
```

`execFile` SSH के local invocation को सुरक्षित करता है, जो अच्छी बात है। लेकिन इससे `command` remote side पर सुरक्षित नहीं हो जाता। SSH आमतौर पर remote side को command string देता है। Remote account अक्सर उस string को shell को सौंप देता है। इसलिए branch एक hop बाद shell syntax में पहुँचती है।

Single quotes सामान्य समाधान नहीं हैं। Single quote वाली input intended quoted region को बंद कर सकती है। POSIX shell के लिए लिखा quote routine PowerShell के लिए गलत है। Shell word के लिए सही routine redirection, arithmetic expression या language interpreter में पहुँचने पर गलत हो सकती है। आज सही routine maintenance burden बनी रहती है, खासकर जब कोई `git show` को `git log --format=...` में बदलकर template rearrange कर दे।

Logging के लिए argument list serialize करने और execution के लिए उसे फिर से बनाना अलग बातें हैं। `git show release-42` जैसी log line human के लिए उपयोगी है, लेकिन इससे मूल argument boundaries साबित नहीं होतीं। यह safe executable representation नहीं है। Arguments को structured array में रखें और display के लिए ही स्पष्ट escaping rules के साथ render करें।

Environment variables भी एक trap हैं। यह interpolation से सुरक्षित है:

```sh
BRANCH="$branch" git show "$BRANCH"
```

लेकिन यह तभी सुरक्षित रहता है जब shell script दोनों quoted expansions को नियंत्रित करती हो और value को बाद में कभी evaluate न करती हो। Downstream script `eval` इस्तेमाल करे, कोई दूसरा command string बनाए या value को execution features वाले template engine को दे, तो शुरुआती सावधानी बेकार हो जाती है। बेहतर boundary agent की value को shell तक पहुँचने ही नहीं देती।

## Fixed executables और argument arrays shell grammar हटाते हैं

Local actions के लिए fixed executable को argument array के साथ call करें और shell execution बंद रखें। Operating system child process को आपके दिए हुए अलग-अलग strings देता है। वह semicolons, dollar signs, spaces, parentheses या glob characters को shell language की तरह दोबारा interpret नहीं करता।

Python `subprocess` documentation arguments की sequence इस्तेमाल करने की सलाह देती है और बताती है कि `shell=False` default है। इस default पर संयोग से निर्भर न रहें, इसे जानबूझकर इस्तेमाल करें:

```python
import subprocess

result = subprocess.run(
    ["git", "status", "--porcelain=v1"],
    cwd=repo_dir,
    text=True,
    capture_output=True,
    check=True,
)
print(result.stdout)
```

इस action में agent-controlled executable या command template नहीं है। यह एक approved directory में एक known operation करता है। Output plain text है और हर status record अपनी line पर है। Interface को आगे बेहतर बनाने के लिए output parse करके raw text को दूसरे agent decision में भेजने के बजाय structured records लौटाएँ।

जब operation को input चाहिए, executable और operation fixed रखें और validated values को अलग entries के रूप में जोड़ें:

```python
subprocess.run(
    ["tool", "fetch-resource", resource_id],
    cwd=workspace,
    check=True,
    shell=False,
)
```

Node.js में array of arguments के साथ `spawn` या `execFile` को प्राथमिकता दें। Compound command को सुविधाजनक बनाने के लिए `shell` option चालू न करें। Go में हर argument अलग देकर `exec.Command` इस्तेमाल करें। Rust में `Command` और बार-बार `arg` calls इस्तेमाल करें। APIs अलग हो सकती हैं, invariant एक है: कोई input command language में नहीं जाना चाहिए।

एक आम सलाह के विरुद्ध यहाँ स्पष्ट होना ज़रूरी है: «Safe wrapper के रूप में shell script इस्तेमाल करें।» Fixed shell script compatibility boundary हो सकती है, लेकिन script होने भर से सुरक्षित नहीं बनती। वह तभी सुरक्षित होती है जब fixed positional parameters या standard input से values ले, हर expansion को quote करे, `eval` से बचे और दूसरा command string न बनाए। Process API से लिखा छोटा program आमतौर पर audit करना आसान होता है।

Pipelines, conditional flow, redirection और shell builtins जैसी सुविधाएँ कभी-कभी सच में ज़रूरी होती हैं। इन्हें हटाया न जा सके तो reviewed static script में रखें। Agent को pipeline assemble न करने दें। उसे `collect_build_logs` नाम का action दें और wrapper को fixed file locations तथा सीमित parameters के साथ fixed pipeline चलाने दें।

Executable भी policy का हिस्सा है। इसे agent input, writable configuration file या attacker-controlled `PATH` search से कभी न चुनें। जहाँ ज़रूरी हो, absolute और administrator-controlled path इस्तेमाल करें। Known working directory और reduced environment सेट करें, agent process से arbitrary variables inherit न करें।

## Validation तय करता है कि action का अर्थ क्या हो सकता है

Argument arrays parser boundary की रक्षा करती हैं। Validation action boundary की। अगर action `read_file` कहता है, तो service को तय करना होगा कि कौन-सी files इस action के लिए readable हैं। Process API यह निर्णय आपके लिए नहीं कर सकती।

Narrow schema से शुरू करें। Deployment name में lowercase letters, digits और सीमित internal separators हों तथा length limit हो सकती है। Branch selector Git rules के चुने हुए subset में हो सकता है। Resource identifier UUID या inventory का integer हो सकता है। Server target कोई ID हो सकता है जिसे service stored host से map करे, न कि arbitrary hostname।

«Semicolon और ampersand reject करें» जैसी generic blacklists से बचें। वे एक parser पर आधारित होती हैं और semantic abuse को अनदेखा करती हैं। समय के साथ वे इतनी बड़ी हो जाती हैं कि सामान्य users भी नहीं समझ पाते कि क्या काम करेगा। Allowlist grammar caller को contract और reviewer को सीमित input set देती है।

Release workflow के branch field के लिए Git की हर संभव ref स्वीकार करने के बजाय जानबूझकर सीमित grammar बेहतर हो सकती है:

```python
import re

BRANCH = re.compile(r"[A-Za-z0-9][A-Za-z0-9._/]{0,127}")

def release_ref(value: str) -> str:
    if not isinstance(value, str):
        raise ValueError("branch must be text")
    if not BRANCH.fullmatch(value):
        raise ValueError("branch has unsupported characters")
    if "/./" in value or "//" in value or value.endswith("/"):
        raise ValueError("branch has an unsupported path form")
    return "refs/heads/" + value
```

यह code Git की पूरी ref grammar लागू करने का दावा नहीं करता। यह release action के लिए छोटी grammar जानबूझकर तय करता है। अक्सर यही सही engineering choice होती है। Users को spaces या किसी अन्य unusual form की ज़रूरत हो तो उसे workflow की आवश्यकता के कारण जोड़ें, test करें और document करें। जब action केवल release branches deploy करता है, तब general version control system के हर edge case को विरासत में न लें।

लौटी हुई value में fixed namespace prefix है। यह महत्वपूर्ण है। Arbitrary revision expression tags, commit ancestry, reflogs या ऐसी syntax को refer कर सकती है जिसे Git command विशेष अर्थ में समझता है। Release branch action चुपचाप «agent द्वारा बताए गए किसी भी Git object को दिखाने» वाला action नहीं बनना चाहिए। Full Git compatibility चाहिए तो library या Git का अपना branch validation mode इस्तेमाल करें, फिर accepted result को allowed namespace में map करें।

Resource names के लिए भी यही अनुशासन चाहिए। Agent cloud object माँगे तो allowed account, region, bucket या project context free-form field के बाहर stored रखें। Agent को केवल आपके समर्थित एक grammar वाला object name देने दें। पूरी URL को resource name न मानें। Full URL protocol, host, port, path और कभी-कभी credentials चुनती है। यह string के रूप में छिपा network authorization decision है।

Length limits validation का हिस्सा हैं। वे log growth और accidental command line limits को सीमित करती हैं तथा denial of service को कठिन बनाती हैं। Character encoding rules भी इसका हिस्सा हैं। Text fields में control characters reject करें, जब तक action को सच में उनकी आवश्यकता न हो। NUL को process arguments के लिए हमेशा reject करें, क्योंकि operating system process interfaces उसे argument के हिस्से के रूप में ले नहीं सकतीं।

## Paths के लिए character filters नहीं, containment checks चाहिए

Path shell के लिए सुरक्षित और filesystem के लिए खतरनाक हो सकता है। `../../secrets.env` में वे shell metacharacters नहीं हैं जिनकी quote functions चिंता करती हैं। फिर भी वह file operation को workspace से बाहर ले जा सकता है।

Safe path policy action के लिए root directory तय करने से शुरू होती है। Requested relative path को उस root के सापेक्ष resolve करें, फिर जाँचें कि resolved target resolved root के भीतर ही रहे। Absolute path स्वीकार न करें, अगर action को केवल workspace files की आवश्यकता है। String normalize करके यह न मानें कि resulting spelling बताती है कि filesystem कहाँ जाएगा।

एक conceptual check ऐसा दिख सकता है:

```python
from pathlib import Path

workspace = Path("/srv/agent-workspaces/repo-a").resolve()

def permitted_file(relative_name: str) -> Path:
    if not isinstance(relative_name, str) or relative_name.startswith("/"):
        raise ValueError("file must be a relative path")
    candidate = (workspace / relative_name).resolve(strict=True)
    if candidate == workspace or workspace not in candidate.parents:
        raise ValueError("file is outside the workspace")
    if not candidate.is_file():
        raise ValueError("requested target is not a regular file")
    return candidate
```

`resolve` ordinary traversal पकड़ता है और existing symlinks follow करता है, इसलिए raw string में `..` खोजने से बेहतर है। फिर भी sensitive write operation के लिए यह पूरा समाधान नहीं है। कोई attacker directory tree बदल सकता है, check के बाद और बाद के open से पहले path component को symlink से बदल सकता है। यह check और use के बीच की time of check to time of use race है।

Controlled workspace में read operations के लिए resolved containment check आपके risk के अनुरूप हो सकता है। Writes, deletion, permission changes या attacker-influenced archives के लिए directory descriptors रखने वाले filesystem operations इस्तेमाल करें और जहाँ operating system controls देता हो वहाँ symlink traversal अस्वीकार करें। Writable agent workspaces को allowed roots तय करने वाले code से अलग रखें। अगर threat model में local attacker filesystem race कर सकता है, तो high-level path library पर्याप्त नहीं है।

यह भी तय करें कि dotfiles, repository metadata, generated dependency directories और symbolic links scope में हैं या नहीं। «Repo के अंदर कुछ भी» जैसा अस्पष्ट rule credential files, build caches या ऐसी configuration उजागर कर सकता है जिसकी workflow को ज़रूरत ही नहीं थी। Read permission action के उद्देश्य के अनुसार होनी चाहिए, recursive glob की सुविधा के अनुसार नहीं।

Archive extraction पर अलग से ध्यान दें। हर archive member को destination root से जोड़ने के बाद validate करें और archive के अंदर links को संभावित hostile मानें। Path traversal केवल agent के सीधे किए request तक सीमित नहीं है। Agent द्वारा चुने गए artifact को extract करने वाला tool भी उसी की ओर से escape कर सकता है।

## Shell के बिना भी options और expressions खतरनाक हैं

Argument vector value को shell syntax से अलग रखता है, लेकिन called program उस value को फिर भी parse करता है। Programs अक्सर hyphen से शुरू होने वाले arguments को options मानते हैं। कुछ expressions, configuration references, file inclusion syntax, plugins या command hooks स्वीकार करते हैं। Untrusted input को एक argument के रूप में देना इन अर्थों को harmless नहीं बनाता।

मान लें wrapper user-supplied pattern के साथ search program call करता है। Wrapper `["search", pattern, directory]` सही तरह देता हो सकता है। अगर program pattern को regular expression मानता है, तो pathological expression बहुत CPU ले सकती है। अगर वह configuration file पढ़ने वाला option support करता है और pattern गलत position में पहुँचता है, तो caller program का behavior बदल सकता है। अगर program की language code evaluation support करती है, तो आपने agent को एक अलग interpreter सौंप दिया है।

Fixed options को data से पहले रखें और जहाँ program support करता हो वहाँ conventional end-of-options sentinel इस्तेमाल करें। कई command line parsers में यह दो hyphen characters होते हैं। इससे parser को संकेत मिलता है कि आगे की values flags नहीं, operands हैं। इसे validation का विकल्प न मानें। कुछ tools इसे नहीं मानते और operand स्वयं semantic रूप से खतरनाक हो सकता है।

Git का command surface बड़ा है, इसलिए agents को generic «run Git» escape hatch के बजाय narrow Git actions चाहिए। `show_release_branch` action restricted branch name ले सकता है, उसे `refs/heads/` और उस name में बदल सकता है और fixed Git subcommand चला सकता है। Arbitrary revision syntax लेने वाला `checkout_anything` व्यापक अर्थ रखता है और व्यापक authorization decision माँगता है। दोनों Git process शुरू करें, फिर भी ये अलग products हैं।

यही नियम package managers, database clients, media tools, archivers और infrastructure commands पर लागू होता है। सोचें कि operating system द्वारा argument पहुँचाने के बाद target program कौन-सी grammar parse करेगा। Command line अक्सर parser stack का केवल पहला parser होती है।

Regular expressions पर विशेष ध्यान दें। Pattern shell code नहीं है, लेकिन regex engine के भीतर executable work है। Agent को text search करना हो तो जहाँ संभव हो predictable performance वाला regex engine चुनें, pattern और input limits लगाएँ और literal search को default action बनाएँ। केवल इसलिए unrestricted expression language वाला endpoint न बनाएं कि developer एक flexible endpoint चाहता है।

## SSH local command को remote parsing problem बना देता है

SSH दूसरी execution boundary बनाता है। आप local SSH client को perfect argument array से चला सकते हैं और फिर भी remote host को unsafe command string भेज सकते हैं। SSH के कई implementations requested remote command को remote account के shell या equivalent command parser से चलाते हैं।

Agent fields से remote commands न बनाएँ। Local call array इस्तेमाल करने पर भी यह गलत है:

```python
remote = "deploy " + environment + " " + branch
subprocess.run(["ssh", host, remote], check=True)
```

Safe arrangement constant remote command भेजता है और data को structured input channel में ले जाता है। उदाहरण के लिए remote account fixed path पर एक reviewed program expose कर सकता है। Local side उस fixed program को शुरू करे, standard input पर JSON request भेजे और remote program कोई local operation चलाने से पहले `environment` तथा `branch` validate करे।

```python
import json
import subprocess

request = {"environment": "staging", "branch": "release/42"}
subprocess.run(
    ["ssh", "deploy.internal", "/usr/local/libexec/receive-deploy-request"],
    input=json.dumps(request) + "\n",
    text=True,
    check=True,
)
```

इस example में remote command constant है। JSON standard input पर data है, remote command में embedded text नहीं। Remote receiver को फिर भी unknown fields reject करने, length limits लागू करने, हर value validate करने और किसी child process के लिए argument vector इस्तेमाल करने होंगे। Structured transport quoting maze हटाता है, trust नहीं देता।

Host name आमतौर पर stored configuration से opaque environment ID द्वारा चुना जाना चाहिए। Agent को `host` देने का अर्थ है कि वह तय कर सकता है credentials कहाँ जाएँ और action कौन-सी network boundary पार करे। Workflow को सच में कई hosts चाहिए हों तो `staging` और `production` जैसे approved names को known connection settings से map करें। Host verification चालू रखें। Setup की परेशानी से बचने के लिए host checking बंद करने वाले tool ने boundary का महत्वपूर्ण हिस्सा हटा दिया है।

Credentials remote command, command line या JSON request में न भेजें। Remote account की existing authentication व्यवस्था इस्तेमाल करें और उस account को केवल आवश्यक action तक सीमित रखें। केवल एक application deploy कर सकने वाला deployment receiver, broadly capable shell session पाने वाले agent की तुलना में समझना आसान है।

## Approval unsafe action design को ठीक नहीं कर सकता

Human approval और audit trail उपयोगी controls हैं, लेकिन validation से अलग सवालों का जवाब देते हैं। Approval पूछता है कि caller अभी action करने की कोशिश कर सकता है या नहीं। Validation पूछता है कि request का shape परिभाषित और अनुमत है या नहीं। Logging बताती है कि क्या हुआ और बाद में record बदला या नहीं। इनमें से किसी एक को बाकी का विकल्प मानने पर attractive screens वाले weak tools बनते हैं।

Fixed action gateway के लिए one-time session approval उचित हो सकती है। यह उस session में process द्वारा shell को दी जाने वाली हर interpolated string की सावधानी से समीक्षा नहीं है। Per-call approval human को odd target notice करने के अधिक अवसर देती है, लेकिन किसी से nested quote rules audit करने या dense dialog में malicious Unicode lookalike पहचानने की उम्मीद नहीं करनी चाहिए।

Approval card पर semantic request दिखाएँ: `deploy branch release/42 to staging`, न कि template से फिर बनाई गई shell command। Receiver को displayed environment और branch की तुलना request schema से करने में सक्षम होना चाहिए। अगर command को समझाने के लिए raw script field चाहिए, तो action reliable approval के लिए बहुत broad है।

Action boundary पर structured facts log करें। Caller या agent process identity, approved action name, separate arguments या request fields, selected target, timestamps, exit status और data retention rules के अधीन output references रखें। Validation failures भी record करें। बार-बार rejected traversal strings या option-like values खराब prompt, integration mistake या active probing का संकेत हो सकते हैं।

Sallyport इस्तेमाल करने वाली teams के लिए per-session authorization, per-call key approval और अलग Sessions तथा Activity journals agent actions के आसपास control और evidence बनाए रख सकते हैं, लेकिन command template को ऊपर दिए design rules का पालन फिर भी करना होगा।

Tamper-evident log event के बाद तभी उपयोगी है जब उसमें महत्वपूर्ण boundaries दर्ज हों। `ssh deploy internal deploy staging release 42` जैसी string बाद में अस्पष्ट होती है। अलग host identity, fixed receiver path, JSON fields, authorization result और receiver exit status वाला event investigation में मदद कर सकता है।

## Rejection path को happy path जितनी ही गंभीरता से test करें

अधिकांश command injection tests केवल यह साबित करते हैं कि `release/42` जैसी normal branch काम करती है। यही सबसे कम रोचक input है। Test suite को साबित करना चाहिए कि handler child process शुरू करने या path खोलने से पहले dangerous values reject करता है।

हर field type के लिए छोटी table बनाएँ। Exact inputs grammar पर निर्भर होंगे, लेकिन categories में shell separators, command substitutions, quote characters, whitespace और newlines, option-like prefixes, traversal components, empty values, control characters, oversized values और valid syntax में होने पर भी action scope से बाहर जाने वाले values शामिल होने चाहिए। Branch action के लिए tag-like name और ऐसा revision expression भी test करें जिसे interface support नहीं करता।

Unit tests में fake process runner इस्तेमाल करें। Executable और हर argument को अलग value के रूप में assert करें। Invalid input runner को कभी invoke न करे। केवल rendered command string की तुलना करने वाला test उस boundary को miss कर सकता है जिसकी आप रक्षा करना चाहते हैं।

```python
class RecordingRunner:
    def __init__(self):
        self.calls = []

    def run(self, argv, **kwargs):
        self.calls.append((argv, kwargs))

def test_rejects_branch_separator():
    runner = RecordingRunner()
    try:
        release_ref("release/42; id")
    except ValueError:
        pass
    else:
        raise AssertionError("expected rejection")
    assert runner.calls == []
```

यह test policy function जाँचता है, shell नहीं। Process boundary के लिए integration test भी जोड़ें। ऐसा harmless fixture program चलाएँ जो received arguments को explicit indexes और lengths के साथ print करे। Spaces, quotes, wildcard characters, parentheses और line breaks वाले inputs दें। पुष्टि करें कि हर value एक argument के रूप में पहुँची और कोई दूसरा program शुरू नहीं हुआ।

Schema तय होने के बाद fuzzing उपयोगी है। Accepted set के भीतर और बाहर random Unicode generate करें, फिर दो outcomes में से एक assert करें: validator reject करे या fixed operation उसे एक bounded field के रूप में receive करे। Production deployment target पर fuzz न करें। उद्देश्य gateway और receiver की parser assumptions को real credentialed environment तक पहुँचने से पहले खोजना है।

अंत में हर उस जगह की समीक्षा करें जहाँ structured input को फिर text में बदला जाता है। इसमें clickable commands वाले log viewers, generated shell snippets, CI configuration, error reports, chat notifications और remote wrappers शामिल हैं। Injection bugs अक्सर तब लौटते हैं जब safe structured request बाद के component में convenient string बन जाती है।

सबसे पहले आमतौर पर सबसे flexible action को ठीक करना चाहिए। `run_command(command)` को ऐसे operation से बदलें जिसका नाम, छोटा request schema, fixed executable और ऐसे inputs से भरी test suite हो जिन्हें normal demo में कोई type नहीं करेगा। यह बदलाव model behavior की एक पूरी श्रेणी को threat model से हटा देता है और आपको ऐसे rules देता है जिन्हें वास्तव में inspect किया जा सकता है।
