{"manifest":{"name":"Secure CLI Execution","version":"1.0.0","description":"Teaches agents to run CLI commands, scripts, and tool invocations without leaking secrets through stdout, process arguments, shell history, or execution traces. Covers env-var hygiene, stdin-based secret passing, history suppression, log discipline, and tool-specific patterns for curl, git, Docker, and CI/CD pipelines.","tags":["security","cli","secrets","devops","agent-safety"],"standard":"agentskills.io","standard_version":"1.0","content_checksum":"a99279c86874d308d8a303977a005867b4cdc47ff2fc863c43fe650cd653eedb","bundle_checksum":null,"metadata":{},"files":[]},"files":{"SKILL.md":"\n# Secure CLI Execution\n\nTeaches agents to run CLI commands, scripts, and tool invocations without exposing\nsecrets, API keys, or credentials through stdout, process argument lists, shell\nhistory, execution traces, or any other observable channel.\n\n## Why This Matters\n\nSecrets can leak through more channels than most agents account for:\n\n| Channel | Example leak | Risk |\n|---------|-------------|------|\n| stdout/stderr | `echo $API_KEY` | Captured in traces, logs, terminal history |\n| Process args | `curl --token abc123` | Visible in `ps aux`, `/proc/<pid>/cmdline` |\n| Shell history | any command with secret in arg | Persisted to `~/.bash_history` |\n| Temp files | `echo $SECRET > /tmp/key` | World-readable by default, persists on disk |\n| Env var dumps | `env` or `printenv` | Prints every variable including secrets |\n| Error messages | stack traces that include config | Framework errors may echo config values |\n\n## Core Rules\n\n### Rule 1 — Never pass secrets as command arguments\n\nProcess argument lists are public on POSIX systems.\n\n```bash\n# BAD — visible in ps aux and shell history\ncurl -H \"Authorization: Bearer $TOKEN\" https://api.example.com/data\nnpx some-cli --api-key \"$STRIPE_KEY\" deploy\ndocker build --build-arg SECRET_KEY=\"$SECRET_KEY\" .\n\n# GOOD — reference the env var; the shell expands it but it never touches args\n# curl reads Authorization from env via a config file\ncurl -K <(echo \"header = \\\"Authorization: Bearer $TOKEN\\\"\") https://api.example.com/data\n\n# Or use a tool that reads directly from env\nSTRIPE_KEY=\"$STRIPE_KEY\" npx some-cli deploy\n```\n\n### Rule 2 — Reference env vars; never echo or print their values\n\nChecking that a variable is set does not require printing it.\n\n```bash\n# BAD — emits the secret value\necho \"Using key: $ANTHROPIC_API_KEY\"\necho $STRIPE_SECRET_KEY\nprintenv AWS_SECRET_ACCESS_KEY\n\n# GOOD — check presence without revealing value\nif [ -z \"$ANTHROPIC_API_KEY\" ]; then\n  echo \"ANTHROPIC_API_KEY is not set\" >&2\n  exit 1\nfi\necho \"ANTHROPIC_API_KEY is set (${#ANTHROPIC_API_KEY} chars)\"\n\n# Check length only — confirms the var exists and is non-trivial\necho \"Key length: $(printenv ANTHROPIC_API_KEY | wc -c) chars\"\n```\n\n### Rule 3 — Use stdin patterns for password-style inputs\n\nMany CLIs accept secrets via stdin to avoid argument exposure.\n\n```bash\n# BAD — password in args\ndocker login --username user --password \"$DOCKER_PASSWORD\" registry.example.com\ngh auth login --with-token \"$GH_TOKEN\"\n\n# GOOD — pipe through stdin\necho \"$DOCKER_PASSWORD\" | docker login --username user --password-stdin registry.example.com\necho \"$GH_TOKEN\" | gh auth login --with-token\n\n# GOOD — process substitution (bash/zsh only)\ngit clone https://user:$(cat <(printenv GIT_PASSWORD))@github.com/org/repo.git\n```\n\n### Rule 4 — Suppress shell history for sensitive sessions\n\n```bash\n# Prevent the current session's commands from being saved\nunset HISTFILE\n\n# Or prefix commands with a space (requires HISTCONTROL=ignorespace in bashrc)\n export SECRET_KEY=\"actual-value-here\"   # leading space — not saved to history\n\n# For a block of sensitive operations\n(\n  unset HISTFILE\n  # ... sensitive commands here ...\n)\n```\n\n### Rule 5 — Secure temporary credential files\n\nWhen a tool absolutely requires a file on disk:\n\n```bash\n# BAD — world-readable temp file, never cleaned up\necho \"$PRIVATE_KEY\" > /tmp/key.pem\nsome-tool --key /tmp/key.pem\n\n# GOOD — restricted permissions, guaranteed cleanup\nKEY_FILE=$(mktemp)\nchmod 600 \"$KEY_FILE\"\ntrap \"rm -f '$KEY_FILE'\" EXIT\necho \"$PRIVATE_KEY\" > \"$KEY_FILE\"\nsome-tool --key \"$KEY_FILE\"\n# trap fires on exit — file always removed\n```\n\n### Rule 6 — Log outcomes, never secret values\n\nExecution traces and logs should record *what happened*, not *which credential was used*.\n\n```bash\n# BAD — logs the token\necho \"Calling API with token: $API_TOKEN\"\ncurl -v -H \"Authorization: Bearer $API_TOKEN\" https://api.example.com\n\n# GOOD — log the outcome\necho \"Calling API...\" >&2\nRESPONSE=$(curl -s -o /dev/stdout -w \"%{http_code}\" \\\n  -H \"Authorization: Bearer $API_TOKEN\" https://api.example.com)\nSTATUS=\"${RESPONSE: -3}\"\nBODY=\"${RESPONSE:0:${#RESPONSE}-3}\"\necho \"API response: HTTP $STATUS\"\n```\n\n### Rule 7 — Never hardcode secrets in scripts or skill content\n\nScripts that are committed to version control, shared, or published as skills must\ncontain zero hardcoded credentials. Use env var references everywhere.\n\n```bash\n# BAD — hardcoded in script (never do this)\nSTRIPE_KEY=\"sk_live_<your-key-here>\"\ncurl -u \"$STRIPE_KEY:\" https://api.stripe.com/v1/charges\n\n# GOOD — always from environment\n: \"${STRIPE_SECRET_KEY:?STRIPE_SECRET_KEY must be set}\"\ncurl -u \"$STRIPE_SECRET_KEY:\" https://api.stripe.com/v1/charges\n```\n\n## Tool-Specific Patterns\n\n### curl\n\n```bash\n# Use a netrc file (chmod 600) instead of --user flag\necho \"machine api.example.com login token password $API_KEY\" > ~/.netrc\nchmod 600 ~/.netrc\ncurl --netrc https://api.example.com/endpoint\n\n# Use --config with process substitution to avoid the header in args\ncurl -K <(echo \"header = \\\"Authorization: Bearer $TOKEN\\\"\") https://api.example.com\n```\n\n### git\n\n```bash\n# Use a credential helper — never embed tokens in remote URLs\ngit config --global credential.helper store   # or 'osxkeychain' / 'manager'\necho \"https://token:$GH_TOKEN@github.com\" | git credential approve\n\n# For CI: use GIT_ASKPASS\nexport GIT_ASKPASS=echo\nexport GIT_PASSWORD=\"$GH_TOKEN\"\ngit clone https://user@github.com/org/repo.git\n```\n\n### Docker\n\n```bash\n# Pass secrets at runtime, not build time\n# BAD — baked into the image layer\ndocker build --build-arg API_KEY=\"$API_KEY\" .\n\n# GOOD — mount a secret at build time (BuildKit, never in image layers)\nDOCKER_BUILDKIT=1 docker build --secret id=api_key,env=API_KEY .\n# In Dockerfile: RUN --mount=type=secret,id=api_key ...\n\n# GOOD — inject at runtime only\ndocker run -e API_KEY=\"$API_KEY\" myimage\n```\n\n### GitHub Actions / CI\n\n```yaml\n# Secrets are masked in logs automatically when referenced via secrets context\n- name: Call API\n  env:\n    API_KEY: ${{ secrets.API_KEY }}\n  run: |\n    # $API_KEY is masked in output — GitHub replaces it with ***\n    curl -H \"Authorization: Bearer $API_KEY\" https://api.example.com\n\n# Add extra masking for derived values\n- run: |\n    DERIVED=\"${API_KEY:0:8}-suffix\"\n    echo \"::add-mask::$DERIVED\"\n    echo \"Derived prefix set\"\n```\n\n### Node.js / Python\n\n```javascript\n// Node — read from process.env, never log the value\nconst apiKey = process.env.ANTHROPIC_API_KEY\nif (!apiKey) throw new Error('ANTHROPIC_API_KEY is required')\nconsole.log(`API key configured (${apiKey.length} chars)`)  // length only\n```\n\n```python\n# Python — use os.environ, validate early, never print\nimport os\napi_key = os.environ[\"ANTHROPIC_API_KEY\"]  # raises KeyError if missing\nprint(f\"API key configured ({len(api_key)} chars)\")  # length only\n```\n\n## When Operating as an Agent in a CLI Environment\n\nIf you are an AI agent executing shell commands, follow these additional rules:\n\n1. **Assume traces are visible.** Verification traces, stdout captures, and debug logs\n   may be read by other users. Treat every line of output as potentially public.\n\n2. **Prefer `-z` checks over echo.** When verifying env vars exist, use\n   `[ -n \"$VAR\" ]` or `[ -z \"$VAR\" ]` — never `echo $VAR`.\n\n3. **Report key length, not key value.** In trace output, confirm a credential is\n   present by its length: `\"API key: ${#ANTHROPIC_API_KEY} chars set\"`.\n\n4. **Never confirm the value matches.** Don't log \"key starts with sk-ant-\" —\n   that's half the secret. Log \"key format validated\" or \"key accepted by API\".\n\n5. **Treat AbortError / timeout as secret-safe.** If a network call fails, log the\n   HTTP status and error message, not the headers that were sent.\n\n6. **Scope secrets to the narrowest environment.** Set env vars inside subshells\n   `(SECRET=x command)` rather than exporting to the whole session.\n\n## Checklist Before Running Sensitive Commands\n\n- [ ] Is the secret referenced as `$VAR_NAME` rather than its literal value?\n- [ ] Will the command appear in `ps aux` with the secret visible?\n- [ ] Does any `echo` or `print` statement emit the actual secret value?\n- [ ] Are temp files created with `chmod 600` and a `trap ... EXIT` cleanup?\n- [ ] Does the log output contain only outcomes (HTTP status, boolean pass/fail)?\n- [ ] Will the shell history record the secret? (use `unset HISTFILE` if needed)\n\n## Attribution\n\nPart of the SkillSlap security toolkit. Maintained by the community.\nPublished at skillslap.com — install via MCP or `skillslap install secure-cli-execution`.\n"}}