# क्या SSH environment forwarding स्थानीय संदर्भ लीक कर सकता है?

SSH environment forwarding किसी दूरस्थ काम को जितनी स्थानीय जानकारी चाहिए, उससे कहीं अधिक लीक कर सकता है। चैनल encrypted होता है, लेकिन encryption केवल रास्ते की रक्षा करता है। वह `AWS_PROFILE`, `GIT_AUTHOR_EMAIL`, किसी tenant के नाम या अंदरूनी feature flag को दूसरी मशीन पर चल रहे process के लिए उचित input नहीं बना देता।

मैं forward होने वाले हर variable को दूरस्थ command का argument मानता हूँ। इसलिए उसका नाम, कारण, जिम्मेदार व्यक्ति और test होना चाहिए। laptop पर सुविधाजनक SSH configuration से लिया गया wildcard इस कसौटी पर खरा नहीं उतरता, क्योंकि कोई नया मिलता-जुलता स्थानीय variable जुड़ते ही उसका अर्थ बदल जाता है।

सुरक्षित लक्ष्य पूरी तरह खाली दूरस्थ environment नहीं है। `sshd`, login account, shell और wrapper अपना base environment बनाते हैं। लक्ष्य अधिक सटीक है: client केवल लिखित contract में दर्ज workflow variables दे, server उससे बड़ा set स्वीकार न करे, और test आवश्यक values के पहुँचने तथा canary values के ठुकराए जाने, दोनों को साबित करे।

## Forwarding के लिए SSH के दोनों सिरों की सहमति चाहिए

OpenSSH environment forwarding के दो gate हैं। client पर `SendEnv` स्थानीय process environment से नाम चुनता है, जबकि client-side `SetEnv` सीधे `NAME=VALUE` pair देता है। server पर `AcceptEnv` तय करता है कि client से आए कौन से नाम session environment में जाएँगे। सामान्यतः कोई नाम तभी पार होता है जब client उसे भेजे और server उसे स्वीकार करे।

RFC 4254 इस व्यवस्था के नीचे का protocol बताता है। shell या command शुरू होने से पहले client `env` प्रकार का SSH channel request भेज सकता है, जिसमें एक variable name और एक value होती है। RFC यह भी चेतावनी देता है कि privileged process में environment को बिना नियंत्रण के set करना खतरनाक है। वह allowlist रखने या पर्याप्त privilege हटने के बाद variables set करने की सलाह देता है। यह चेतावनी protocol specification का ही हिस्सा है।

OpenSSH manuals एक अपवाद बताते हैं जो audit में अक्सर भ्रम पैदा करता है: client जब pseudo-terminal माँगता है, तब protocol की जरूरत के कारण `TERM` हमेशा भेजा और स्वीकार किया जाता है। जिस automation को terminal नहीं चाहिए, उसे `ssh -T` के साथ चलाएँ। इससे इस रास्ते से terminal behavior, interactive startup surprises और `TERM` हट जाते हैं।

इस handshake से चार बातें निकलती हैं।

- व्यापक `SendEnv` ऐसे server के सामने निष्क्रिय रहता है जो कुछ स्वीकार नहीं करता, लेकिन administrator के `AcceptEnv` बढ़ाते ही सक्रिय हो जाता है।
- व्यापक `AcceptEnv` सावधान client के साथ निष्क्रिय रहता है, लेकिन कोई authorized user दूसरी client configuration से matching value भेज सकता है।
- जब अलग लोग दोनों configurations नियंत्रित करते हैं, तब कोई एक सिरा पूरा contract लागू नहीं कर सकता।
- command का सफल होना यह नहीं बताता कि server ने कौन से environment requests ठुकराए।

आखिरी बात महत्वपूर्ण है। OpenSSH भेजे गए variable को अनदेखा करके session जारी रख सकता है। यदि दूरस्थ shell उसी नाम को पहले से set करता है, तो केवल final value देखने वाला test forwarding को उस value का श्रेय दे सकता है जो कभी भेजी ही नहीं गई।

## Wildcard भविष्य के स्थानीय state को दूरस्थ input बना देता है

`SendEnv WORKFLOW_*` का अर्थ “आज review किए गए variables” नहीं है। इसका अर्थ हर भविष्य के SSH client process में मौजूद सभी matching names है। कुछ सप्ताह बाद developer profile में `WORKFLOW_DEBUG_DUMP`, `WORKFLOW_CUSTOMER` या `WORKFLOW_TOKEN_FILE` जोड़ सकता है और पुराना SSH rule चुपचाप नया behavior अपना लेता है।

Locale forwarding दिखाता है कि यह दायरा कैसे बढ़ता है। बहुत से workstation configurations `LANG` और `LC_*` भेजते हैं ताकि interactive shell text ठीक दिखाए। मानव login के लिए यह उचित हो सकता है, पर noninteractive build या deploy account के लिए अपने आप उचित नहीं हो जाता। Locale sorting, character classes, date rendering और diagnostic messages बदलता है। output parse करने वाला job किसी secret के भेजे बिना भी टूट सकता है।

सबसे जोखिम वाले patterns अक्सर सुविधाजनक namespace जैसे दिखते हैं:

- `AWS_*` में profiles, regions, credentials से जुड़े values और configuration paths आ सकते हैं।
- `GIT_*` identity, tracing, alternate object directories या askpass behavior ले जा सकता है।
- `CI_*` अक्सर साधारण build labels को provider context और temporary paths के साथ मिला देता है।
- `LC_*` तब तक cosmetic लगता है जब तक script stable sorting या messages पर निर्भर न हो।
- `APP_*` application के साथ बढ़ता रहता है और उसका एक security अर्थ शायद ही होता है।

पूरे namespace को safe न मानें। हर नाम को अलग जाँचें। `DEPLOY_REGION` जरूरी हो सकता है, जबकि `AWS_PROFILE` workstation context है। `BUILD_REF` जरूरी हो सकता है, जबकि `GIT_CONFIG_COUNT` Git का runtime configuration बदलता है। साझा prefix नामों को व्यवस्थित करता है, trust boundary नहीं बनाता।

Values credential न होकर भी context खोलते हैं। path username और repository layout बताता है। profile account या environment पहचानता है। trace flag दूरस्थ tool से command data shared log में लिखवा सकता है। agent इस context को बाद की actions में इस्तेमाल कर सकता है। Leak में अनचाहा प्रभाव और disclosure दोनों आते हैं, केवल secret strings नहीं।

## सुरक्षित दिखने वाला अपवाद महीनों बाद फैल सकता है

यह leak अक्सर किसी एक साफ तौर पर लापरवाह बदलाव से नहीं, configuration drift से बनता है। developer पहले `SendEnv APP_*` जोड़ता है क्योंकि test को `APP_COLOR=0` चाहिए। server उसे ठुकरा देता है, इसलिए कुछ नहीं होता। बाद में administrator उसी shared host पर दूसरी team के लिए `AcceptEnv APP_*` जोड़ता है। दोनों reviews अपनी जगह ठीक लगते हैं क्योंकि हर reviewer handshake का केवल आधा भाग देखता है।

अगला connection दोनों निष्क्रिय rules को जोड़ देता है। developer shell में अब `APP_CUSTOMER=acme-lab`, `APP_TRACE=1` और `APP_CONFIG=/Users/lee/work/private/config` हैं। SSH client सब भेजता है, daemon सब स्वीकार करता है, और release fail होने पर diagnostic wrapper `env` चलाकर output को group-readable log में रखता है। token नहीं गया, लेकिन स्थानीय identity, customer context, path और trace flag बिना approval सीमा पार कर गए।

Server wildcard हटाने से भविष्य के sessions सुधरते हैं, पुराना log नहीं मिटता। इस खोज को छोटे incident की तरह सँभालें:

1. व्यापक pattern स्वीकार करना बंद करें और daemon configuration validate करें।
2. वे accounts, clients और समय अवधि खोजें जिनमें दोनों patterns एक साथ सक्रिय थे।
3. approved logs में exposed names खोजें, sensitive values नहीं।
4. पता करें कि किस value ने command behavior बदला या दूसरे user को context दिखाया।
5. wildcard को exact names से बदलें और rejected classes के लिए canaries जोड़ें।

इसीलिए “server अभी इसे reject करता है” व्यापक client rule का बचाव नहीं है। dormant configuration के सक्रिय होने के क्षण पर अक्सर कोई owner नहीं होता। server strict हो तब भी अनावश्यक client offers हटाएँ, और clients सावधान हों तब भी server acceptance सीमित रखें।

उलटा drift भी होता है। server वर्षों से interactive users के लिए `LC_*` स्वीकार करता है, फिर automation image update ऐसा system client config जोड़ता है जो `LC_*` भेजता है। deploy job developer का चुना `LC_COLLATE` inherit करता है और files का क्रम बदल जाता है। शायद disclosure न हो, लेकिन उसी handshake ने integrity failure बनाया। Confidentiality और command behavior को साथ review करें।

## वही configuration देखें जिसे SSH वास्तव में इस्तेमाल करेगा

किसी file को edit करने से पहले evaluated client configuration पढ़ें। OpenSSH का `ssh -G` `Host`, `Match`, includes, user config और system config लागू करके दिए गए destination का final result print करता है। जो file authoritative दिखती है वह किसी पहले value से हार सकती है या include से अतिरिक्त entries पा सकती है।

इसे workflow वाले account और उसी execution context से चलाएँ:

```sh
ssh -G deploy-prod |
  awk '$1 == "sendenv" || $1 == "setenv" { print }'
```

एक आम unsafe result ऐसा दिखता है:

```text
sendenv LANG
sendenv LC_*
sendenv AWS_*
setenv WORKFLOW_KIND=deploy
```

`ssh -G` `SendEnv` से चुने गए current values नहीं, effective selection rules दिखाता है। इसलिए review output credentials नहीं उगलता। फिर भी names और patterns को उस process के environment से मिलाना होगा जो वास्तव में SSH शुरू करता है।

Values के बिना names capture करें:

```sh
env | sed 's/=.*//' | LC_ALL=C sort > local-env.names
grep -E '^(LANG|LC_|AWS_|WORKFLOW_)' local-env.names
```

ऊपर का regular expression inspection aid है, reusable policy नहीं। उसे `ssh -G` के हर pattern के अनुसार बदलें। यदि supervisor, IDE, agent या scheduled job SSH शुरू करता है, तो उसी process का environment देखें, interactive shell का नहीं। Environment inheritance process tree का अनुसरण करता है।

Server को अलग जाँचें। OpenSSH server पर `sshd -T` effective daemon settings validate और print करता है। `Match` blocks account को प्रभावित करें तो connection criteria दें:

```sh
sudo sshd -T \
  -C user=deploybot,host=deploy.example,addr=192.0.2.44 |
  grep '^acceptenv'
```

Review में असली user, host और client address इस्तेमाल करें। documentation address केवल example में रखें। daemon reload करने से पहले `sudo sshd -t` भी चलाएँ; environment hardening के लिए remote access तोड़ना उचित नहीं है।

## अलग client file allowlist को पढ़ने योग्य बनाती है

Automation के लिए सबसे भरोसेमंद setup एक छोटी dedicated SSH configuration है जिसे `-F` से दिया जाए। `ssh` manual के अनुसार explicit file सामान्य per-user file को बदल देती है और system-wide client file को ignore कराती है। इससे workstation package या fleet setting बाद में locale wildcard जोड़कर workflow नहीं बदल सकती।

```sshconfig
Host deploy-prod
    HostName deploy.example
    User deploybot
    IdentityFile ~/.ssh/deploy_ed25519
    IdentitiesOnly yes
    RequestTTY no
    SendEnv DEPLOY_REGION
    SendEnv BUILD_REF
    SetEnv WORKFLOW_KIND=deploy
```

इसे स्पष्ट रूप से चलाएँ:

```sh
ssh -F ./deploy-ssh.conf deploy-prod /usr/local/bin/release
```

`SendEnv` स्थानीय `ssh` process environment से value पढ़ता है। उसे तब इस्तेमाल करें जब value हर run में वैध रूप से बदलता हो। client `SetEnv` configuration में literal value लिखता है। उसे nonsecret constant label के लिए रखें, credential या ऐसा value जिसके update को लोग भूलेंगे, उसके लिए नहीं।

OpenSSH `-` से शुरू होने वाले `SendEnv` pattern के जरिए पहले चुने names हटाने देता है:

```sshconfig
Host deploy-prod
    SendEnv -*
    SendEnv DEPLOY_REGION BUILD_REF
```

यह cleanup उस समय तक जमा selections पर लागू होता है। बाद का include या system rule names फिर जोड़ सकता है। इसलिए subtraction अंतिम automation boundary नहीं है। `-F` के साथ dedicated file में कम moving parts होते हैं और दूसरा engineer उसे एक screen में समझ सकता है।

Credentials इस contract से बाहर रखें। Forwarded token साधारण remote process state बन जाता है और child processes, debug output, crash reports, अनुमति मिलने पर `/proc` inspection या accidental `env` output तक पहुँच सकता है। SSH transport बचाता है, delivery के बाद value का पूरा जीवन नहीं।

## Server को सीमित account के लिए exact names स्वीकार करने चाहिए

Session शुरू होने से पहले client variable ठुकराने की आखिरी जगह server है। Pseudo-terminal वाले `TERM` अपवाद को छोड़कर OpenSSH default में कोई variable स्वीकार नहीं करता। यह global default बनाए रखें और workflow owner account के लिए केवल exact names जोड़ें।

```sshdconfig
Match User deploybot
    AcceptEnv DEPLOY_REGION
    AcceptEnv BUILD_REF
    AcceptEnv WORKFLOW_KIND
```

`AcceptEnv APP_*`, `AcceptEnv AWS_*` या अकेला wildcard इस्तेमाल न करें। OpenSSH का `sshd_config` manual स्पष्ट चेतावनी देता है कि कुछ environment variables restricted user environment को bypass कर सकते हैं। Wildcard बिना किसी अगले server change के भविष्य के names भी अनुमति देता है।

व्यापक global `AcceptEnv` हर matching account को कमजोर करता है। संकरा `Match User` block पहले से globally accepted names को नहीं मिटाता; entries जुड़ सकती हैं। यदि interactive users को locale forwarding चाहिए और deploy account को नहीं, तो global locale rule से शुरुआत न करें। केवल सही users या groups को अनुमति दें और `sshd -T -C` से हर connection जाँचें।

Server-side `SetEnv` अलग control है। वह child sessions में values set करता है और defaults, `AcceptEnv` या `PermitUserEnvironment` से आए values को override करता है। यदि constant server का है, तो उसे server पर set करें, client से स्वीकार न करें।

`PermitUserEnvironment` एक और source है। वह `~/.ssh/environment` और authorized_keys के `environment=` options को नियंत्रित करता है, और OpenSSH उसे default में बंद रखता है। केवल `AcceptEnv` देखने वाला audit इसे चूक सकता है। बिना खास account design के इसे बंद रखें और जरूरत हो तो इसके patterns को उसी contract review में जोड़ें।

Exact-name allowlist names नियंत्रित करती है, values नहीं। Allowed `DEPLOY_REGION` में whitespace, shell metacharacter, newline या prohibited region हो सकता है। SSH protocol value को data की तरह ले जाता है, लेकिन receiving script `eval`, unquoted expansion, generated configuration या command string से उसे syntax बना सकता है।

हर accepted value को remote entry point पर validate करें:

```sh
case ${DEPLOY_REGION-} in
    us-east-1|us-west-2) ;;
    *)
        printf 'invalid DEPLOY_REGION\n' >&2
        exit 64
        ;;
esac

case ${BUILD_REF-} in
    [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]*)
        ;;
    *)
        printf 'invalid BUILD_REF\n' >&2
        exit 64
        ;;
esac
```

Validation वास्तविक contract से मेल खाना चाहिए। छोटा shell pattern केवल shape check दिखाता है; release को पूरा commit identifier चाहिए तो exact length लागू करें और repository में उसकी मौजूदगी जाँचें। Validator इतना permissive न हो कि केवल empty string रोके।

Edit के बाद indentation पर भरोसा न करके effective result देखें:

```sh
sudo sshd -t
sudo sshd -T \
  -C user=deploybot,host=deploy.example,addr=192.0.2.44 |
  grep -E '^(acceptenv|permituserenvironment|setenv)'
```

इस output को configuration review के साथ रखें। यह दर्ज करता है कि tested connection के लिए daemon ने वास्तव में क्या parse किया, जो includes से भरे directory के एक fragment के screenshot से अधिक उपयोगी है।

## Remote process में required values और rejected canaries साबित करें

उपयोगी test remote process में positive और negative दोनों cases जाँचता है। हर required value को साफ marker दें, पुराने broad patterns से match होने वाले canaries set करें, dedicated file के साथ noninteractive session चलाएँ और contract गलत हो तो remote shell को fail कराएँ।

यह sequence client से केवल `DEPLOY_REGION`, `BUILD_REF` और fixed `WORKFLOW_KIND` की अपेक्षा करता है:

```sh
export DEPLOY_REGION='env-audit-region'
export BUILD_REF='env-audit-ref'
export AWS_PROFILE='must-not-cross'
export LC_AUDIT_CANARY='must-not-cross'
export APP_PRIVATE_PATH='must-not-cross'

ssh -F ./deploy-ssh.conf -T deploy-prod 'sh -s' <<'REMOTE'
set -eu

test "$(printenv DEPLOY_REGION)" = 'env-audit-region'
test "$(printenv BUILD_REF)" = 'env-audit-ref'
test "$(printenv WORKFLOW_KIND)" = 'deploy'

for name in AWS_PROFILE LC_AUDIT_CANARY APP_PRIVATE_PATH; do
    if printenv "$name" >/dev/null 2>&1; then
        printf 'unexpected forwarded variable: %s\n' "$name" >&2
        exit 1
    fi
done

printf '%s\n' 'SSH environment contract passed'
REMOTE
```

Expected output एक line है:

```text
SSH environment contract passed
```

फिर एक required local value unset करके negative test चलाएँ। `SendEnv` local process में absent name नहीं भेज सकता, इसलिए remote assertion fail होना चाहिए:

```sh
unset BUILD_REF
if ssh -F ./deploy-ssh.conf -T deploy-prod 'test -n "$BUILD_REF"'; then
    printf '%s\n' 'test failed: BUILD_REF appeared unexpectedly' >&2
    exit 1
fi
```

यह optional forwarded value और required workflow input में फर्क करता है। यदि job को `BUILD_REF` के बिना fail होना चाहिए, launcher में `${BUILD_REF:?BUILD_REF is required}` भी रखें। Remote check फिर भी जरूरी है क्योंकि वह transport contract और server rejection जाँचता है।

Canaries audit में मिले हर wildcard या questionable class को cover करें और साफ तौर पर fake हों। Leakage test में real credential कभी न रखें; failure उसी value को log में लिख सकता है जिसका जोखिम आप साबित कर रहे हैं।

एक सफल canary run यह साबित नहीं करता कि कोई भी कल्पनीय variable नहीं जा सकता। वह एक configuration और एक process environment में नामित negative cases साबित करता है। उसे दो static checks से जोड़ें: `ssh -G` में केवल exact `SendEnv` names हों और `sshd -T -C` में केवल expected `AcceptEnv` names। `*` या `?` दिखे तो future environment की पूरी सूची test नहीं कर सकता।

हर अलग launcher से contract test चलाएँ। Developer shell, CI worker, editor task और autonomous agent एक ही binary को अलग configuration paths और environments से चला सकते हैं। Dedicated file का path launcher में दर्ज करें, केवल interactive alias में नहीं।

Final environment देखना और SSH contribution साबित करना अलग बातें हैं। यदि `/etc/profile` पहले से `DEPLOY_REGION=us-east-1` set करता है और client वही value भेजता है, server request reject करके भी positive assertion पास करा सकता है। Unique marker और missing-input test दोनों चलाएँ।

Test यह दावा नहीं करता कि remote environment में केवल तीन variables हैं। सामान्य OpenSSH session को `HOME`, `USER`, `SHELL`, `PATH` और SSH connection metadata भी मिलते हैं, और startup files या wrappers और values जोड़ सकते हैं। Assertion केवल client-contributed हिस्सा साबित करता है। पूरे minimal environment की जरूरत हो तो server baseline अलग audit करें।

## Command construction SendEnv को पूरी तरह bypass कर सकता है

`SendEnv` साफ करना local shell को remote command string में variables expand करने से नहीं रोकता। यह अलग data path है:

```sh
ssh deploy-prod "release '$TENANT' '$TOKEN'"
```

Local shell `ssh` चलने से पहले दोनों values बदल देता है। वे encrypted command request में जाते हैं, `env` request में नहीं, इसलिए `AcceptEnv` उन्हें रोक नहीं सकता। Launcher command कैसे बनाता और record करता है, उसके अनुसार वे process inspection, shell history, CI logs या errors में भी दिख सकते हैं।

यह prefix local SSH process को बदलता है, लेकिन name configuration में चुना गया हो तभी पार होता है:

```sh
DEPLOY_REGION=west ssh -F ./deploy-ssh.conf deploy-prod /usr/local/bin/release
```

Teams इन cases को अक्सर मिला देती हैं। “SSH चलते समय value environment में था” यह नहीं साबित करता कि forwarding ने भेजा; “हमने `SendEnv` बंद किया” यह नहीं साबित करता कि wrapper ने उसे arguments या standard input में interpolate नहीं किया। असली channel trace करें।

Remote startup code एक और source है। Login shell, `/etc/environment`, PAM, forced command, `sudo`, service manager या container launcher SSH के स्वीकार करने के बाद values हटा, बदल या जोड़ सकता है। Marker न मिले तो client decisions के लिए `ssh -vvv`, permitted server logs और application wrapper से पहले छोटा command चलाएँ। तुरंत `AcceptEnv` न बढ़ाएँ।

`sudo` को अलग जाँच चाहिए। sudoers environment reset, selected names retain या preservation request allow कर सकता है, लेकिन वह केवल privileged child तक पहुँचने वाला environment बदलता है। Value उससे पहले unprivileged SSH session में आ चुका था, जहाँ scripts और wrappers उसे देख सकते थे। दोनों layers अलग configure करें।

Remote host passive pipe नहीं है। Remote account नियंत्रित करने वाला व्यक्ति आम तौर पर process environment print कर सकता है, और administrator machine नियंत्रित करता है। यदि security requirement कहती है कि remote host कोई value कभी न जाने, तो उसे SSH से किसी रूप में न भेजें।

Routine logs में पूरा `env` dump न करें। Approved audit में पहले names capture करें और values केवल fake markers के लिए दिखाएँ। पूरा dump unrelated variables को भी incident material बना सकता है।

## Agent के SSH को छोटी process boundary चाहिए

Autonomous agent अक्सर उस terminal, editor या orchestrator का environment inherit करता है जिसने उसे शुरू किया। वह environment कई repositories और accounts पर काम करने वाले मानव के लिए बना है, एक remote action के लिए नहीं। उसके namespaces भेजना agent को अतिरिक्त context और remote tools को unreviewed inputs देता है।

Workflow को explicit local environment और explicit SSH configuration दोनों के साथ शुरू करें। Minimal wrapper दो changing values माँग सकता है, unrelated inherited state हटा सकता है और केवल client की जरूरत वापस दे सकता है:

```sh
#!/bin/sh
set -eu
: "${DEPLOY_REGION:?DEPLOY_REGION is required}"
: "${BUILD_REF:?BUILD_REF is required}"

exec env -i \
  HOME="$HOME" \
  PATH='/usr/bin:/bin:/usr/sbin:/sbin' \
  DEPLOY_REGION="$DEPLOY_REGION" \
  BUILD_REF="$BUILD_REF" \
  ssh -F ./deploy-ssh.conf deploy-prod /usr/local/bin/release
```

Operating system के अनुसार `PATH` और required inputs बदलें, और list explicit रखें। यहाँ `HOME` इसलिए है कि OpenSSH known_hosts और identity file path इस्तेमाल कर सकता है। Managed runner ये paths दूसरी options से दे तो `HOME` भी हटाएँ।

Mac पर agent-operated SSH के लिए Sallyport SSH key को encrypted vault में रख सकता है और bundled `sp-ssh` से action चला सकता है, इसलिए agent को credential नहीं मिलता। इससे credential custody बदलती है, पर vague remote environment contract सही नहीं हो जाता; allowed command के आसपास exact-name test बनाए रखें।

Shortcut के रूप में स्थानीय cloud credentials forward न करें। Remote workload को अपनी narrowly scoped identity दें या credential रखने वाले gateway से action कराएँ। Operator का ambient credential remote process में copy करना दो trust boundaries जोड़ देता है और revocation कठिन बनाता है।

## Configuration बदलने पर contract को executable रखें

Environment rules drift करते हैं क्योंकि client packages defaults जोड़ते हैं, administrators daemon snippets जोड़ते हैं और workflows नए inputs लेते हैं। Prose standard यह drift नहीं पकड़ेगा। Dedicated client file, server account configuration और canary assertion को साथ review करें।

इनमें किसी बदलाव के बाद assertion चलाएँ:

- SSH client या operating-system packages
- user और system SSH configuration includes
- `sshd_config`, PAM, shell startup files या forced commands
- agent launcher, CI runner, service manager या container image
- workflow की required input list

नए variable को interface change मानें। लिखें कि remote command को वह क्यों चाहिए, value client का है या server का, उसमें sensitive context है या नहीं, और कौन सा test दूसरी जगह उसकी अनुपस्थिति साबित करता है। जवाब न हों तो wildcard बढ़ाने के बजाय validated command input दें या remote action redesign करें।

Failure स्पष्ट रखें। Forwarding टूटने पर remote default पर चुपचाप गिरने वाला deployment उस deployment से कठिन और अधिक खतरनाक है जो missing marker पर रुकता है। Required inputs local launcher और remote entry point दोनों पर fail होने चाहिए।

अंत में account को remote side से भी review करें। Perfect `SendEnv` list ऐसे deploy account की रक्षा नहीं करती जो arbitrary shells चला सकता है, दूसरे users के process environments पढ़ सकता है या अपनी startup files बदल सकता है। इस input channel को exact बनाएँ, behavior साबित करें और remote command की authority इतनी सीमित रखें कि unexpected value unrelated action न बन सके।
