Skill: brycexbt/secret-safe (version 1.0.0)

--- name: secret-safe description: > Secure API key and secrets management for agent skills. Use this skill whenever a task requires authenticating with an external service, reading or writing API keys, tokens, passwords, or credentials of any kind. Also trigger when auditing other skills for credential leaks, when a user asks how to securely pass a secret to a skill, or when reviewing a SKILL.md that handles sensitive values. This skill teaches the agent how to handle secrets WITHOUT ever placing them in the LLM context, prompts, logs, or output artifacts — using OpenClaw's native env injection instead. tags: [security, api-keys, credentials, secrets, audit] version: 1.0.0 --- # Secret-Safe: Secure Credential Handling for Agent Skills > **Why this skill exists**: Snyk researchers found that 7.1% of all ClawHub skills > instruct agents to handle API keys through the LLM context — making every secret > an active exfiltration channel. This skill teaches the correct pattern. --- ## The Core Rule **A secret must never appear in:** - The LLM prompt or system context - Claude's response or reasoning - Logs, session exports, or `.jsonl` history files - File artifacts created by the agent - Error messages echoed back to the user **A secret must only flow through:** - `process.env` (injected by OpenClaw before the agent turn) - The shell environment of a subprocess the agent spawns - A secrets manager CLI (read at subprocess level, not piped back into context) --- ## Pattern 1: Environment Injection (Preferred) This is OpenClaw's native, secure path. Use it for any skill that needs an API key. ### In `SKILL.md` frontmatter ```yaml --- name: my-service-skill description: Interact with MyService API. metadata: {"openclaw": {"requires": {"env": ["MY_SERVICE_API_KEY"]}, "primaryEnv": "MY_SERVICE_API_KEY"}} --- ``` The `requires.env` gate ensures the skill **will not load** if the key isn't present — no silent failures, no prompting the user to paste a key mid-conversation. The `primaryEnv` field links to `skills.entries.<n>.apiKey` in `openclaw.json`, so the user configures it once in their config file, never in chat. ### In skill instructions ```markdown ## Authentication The API key is available as `$MY_SERVICE_API_KEY` in the shell environment. Pass it to CLI tools or curl as an environment variable — never echo it or include it in any output returned to the user. ``` ### Example safe curl invocation (instruct the agent to do this) ```bash # CORRECT — key stays in environment, never in command string visible to LLM MY_SERVICE_API_KEY="$MY_SERVICE_API_KEY" curl -s \ -H "Authorization: Bearer $MY_SERVICE_API_KEY" \ https://api.myservice.com/v1/data ``` **Never instruct the agent to do this:** ```bash # WRONG — key is visible in LLM context, command history, and logs curl -H "[REDACTED_SECRET]" https://api.myservice.com/ ``` --- ## Pattern 2: Secrets Manager Integration For production setups or team environments, read secrets from a manager at subprocess level. ### Supported managers | Manager | CLI | Env var pattern | |---|---|---| | macOS Keychain | `security find-generic-password -w` | N/A | | 1Password CLI | `op read op://vault/item/field` | `OP_SERVICE_ACCOUNT_TOKEN` | | Doppler | `doppler run --` | `DOPPLER_TOKEN` | | HashiCorp Vault | `vault kv get -field=value` | `VAULT_TOKEN` | | Bitwarden CLI | `bw get password item-name` | `BW_SESSION` | ### Safe shell wrapper pattern Create a `scripts/run-with-secret.sh` in your skill: ```bash #!/usr/bin/env bash # Fetches the secret at subprocess level — never echoes to stdout [REDACTED_SECRET] find-generic-password -s "my-service-api-key" -w 2>/dev/null) if [ -z "$SECRET" ]; then echo "ERROR: Secret 'my-service-api-key' not found in keychain." >&2 exit 1 fi export MY_SERVICE_API_KEY="$SECRET" exec "$@" ``` The agent runs `bash {baseDir}/scripts/run-with-secret.sh <actual-command>` — the secret is fetched and injected entirely outside the LLM's view. --- ## Pattern 3: User Setup Flow (first-run) If the user hasn't configured a key yet, guide them through setup **without asking for the key in chat**. ### Correct setup prompt to give the user: ``` To use this skill, add your API key to ~/.openclaw/openclaw.json: skills: entries: my-service: [REDACTED_SECRET]" Or set it as an environment variable before starting OpenClaw: export MY_SERVICE_API_KEY="your-key-here" Do NOT paste your key into this chat — it will be logged. ``` ### Incorrect (never do this): ``` Please share your API key so I can help you set it up. ``` --- ## Auditing Another Skill for Leaks When asked to review a `SKILL.md` for credential safety, check for these patterns: ### 🔴 Critical — Must Fix | Pattern | Why it's dangerous | |---|---| | Instruction to paste key into chat | Key goes into LLM context + session logs | | `echo $API_KEY` or `print(api_key)` in instructions | Output captured in context | | Key interpolated into a string returned to user | Exposed in response artifact | | `cat ~/.env` or reading raw env files | Entire env dumped into context | | Key stored in a file the agent creates | Creates a static credential artifact | | Instructions tell agent to "remember" the key | Key persists across context window | ### 🟡 Warning — Should Fix | Pattern | Risk | |---|---| | No `requires.env` gate in frontmatter | Skill silently fails or user is prompted | | Logging command output without filtering | May capture keys in error messages | | Using `set -x` in shell scripts | Echoes all commands including key values | | Passing key as a positional argument | Visible in `ps aux` on the host | ### 🟢 Safe Patterns - `requires.env` in frontmatter - Key accessed only as `$ENV_VAR` in shell, never echoed - Subprocess scripts that fetch and inject without returning to context - Error messages that say "key not found" without printing the value - Output filtered through `sed`/`grep` before returning to agent --- ## Self-Check Before Publishing a Skill Run through this checklist before putting any skill on ClawHub: - [ ] Does the skill ever ask the user to paste a secret into the conversation? - [ ] Does the skill ever `echo`, `print`, `log`, or return a secret value? - [ ] Does the skill read a `.env` file and dump its contents? - [ ] Does the skill store a secret in a file artifact? - [ ] Are all API key references gated with `requires.env` in frontmatter? - [ ] Do error messages avoid reflecting credential values? - [ ] Does any shell script use `set -x` (which would expose key values)? - [ ] Would running `clawhub audit {skill-name}` pass? If any box is unchecked, do not publish until fixed. --- ## Quick Reference: Safe vs Unsafe Patterns ```markdown # UNSAFE — never write instructions like these: "Ask the user for their OpenAI API key and use it to call the API." "Set the Authorization header to Bearer {user_api_key}." "Store the API key in a variable and use it throughout the session." # SAFE — write instructions like these: "The API key is injected as $OPENAI_API_KEY via environment — use it directly." "Run: OPENAI_API_KEY=$OPENAI_API_KEY curl ..." "If $OPENAI_API_KEY is not set, print an error and exit — do not ask the user." ``` --- ## Reference Files - `references/env-injection-examples.md` — Full worked examples for popular APIs (OpenAI, Anthropic, GitHub, Stripe, Slack) - `references/audit-checklist.md` — Printable audit checklist for skill authors and reviewers

path: references/audit-checklist.md
content: # Skill Security Audit Checklist

Use this checklist when reviewing any skill — yours or a third-party skill from ClawHub —
before enabling it on your OpenClaw instance.

---

## Pre-Install Audit (Third-Party Skills)

Read the full `SKILL.md` before installing. Check each item:

### Credential Handling
- [ ] The skill does NOT ask users to paste secrets into the chat
- [ ] The skill does NOT echo, print, or return any credential value
- [ ] All required credentials are declared via `requires.env` in frontmatter
- [ ] The skill does NOT read `.env` files and dump their contents
- [ ] The skill does NOT store credentials in files it creates

### Shell/Script Safety
- [ ] No `set -x` in bundled scripts (would expose env vars in trace output)
- [ ] No credentials passed as positional arguments (visible in `ps aux`)
- [ ] Scripts use `2>/dev/null` or structured error handling (not raw error echo)
- [ ] Any output piped back to the agent is filtered for sensitive patterns

### Network / Exfiltration
- [ ] The skill only contacts URLs explicitly described in its documentation
- [ ] No unexplained `curl`, `wget`, or `fetch` calls to hardcoded IPs or unusual domains
- [ ] No encoded or obfuscated command strings (base64, eval, etc.)
- [ ] No "prerequisite install" steps that download and execute unsigned binaries

### Filesystem Access
- [ ] The skill only accesses files/paths relevant to its stated purpose
- [ ] The skill does NOT read credential files like `~/.ssh/`, `~/.aws/`, `~/.gnupg/`
- [ ] The skill does NOT access OpenClaw config files (`~/.openclaw/`) unless it's an OpenClaw management skill

### Scope Creep
- [ ] The skill's required permissions match its stated function
  (e.g., a weather skill should NOT need filesystem write access)
- [ ] The skill does NOT have `always: true` in metadata unless that's genuinely needed

---

## Pre-Publish Audit (Your Own Skills)

### Credential Patterns
- [ ] No hardcoded API keys, tokens, or passwords in `SKILL.md` or any bundled files
- [ ] No instructions that ask users for credentials via chat
- [ ] All secret access uses `$ENV_VAR` syntax, not interpolated strings
- [ ] `requires.env` gate in frontmatter for every secret the skill uses

### Output Safety
- [ ] Error messages never reflect credential values
- [ ] Success messages don't include auth tokens from responses (e.g., OAuth access tokens)
- [ ] Log output (if any) strips sensitive fields before writing

### Script Review
- [ ] Ran `grep -r "echo.*KEY\|echo.*TOKEN\|echo.*SECRET\|echo.*PASSWORD" ./` — result clean
- [ ] Ran `grep -r "set -x" ./` — result clean or `set -x` is justified and scoped
- [ ] Ran `grep -rE "curl.*Bearer [a-zA-Z0-9]" ./` to catch hardcoded tokens — result clean

---

## Red Flags That Should Block Installation

Stop immediately and do not install if you see:

🚨 A skill that asks you to run a shell command to "install a prerequisite"
   that isn't from a known package manager (brew, npm, pip, apt)

🚨 A `curl | bash` or `wget | sh` pattern anywhere in the instructions

🚨 Base64-encoded strings being decoded and executed: `echo "..." | base64 -d | bash`

🚨 Instructions to add the skill's author's domain to a trusted list

🚨 A skill with 5 installs and no reviews that promises to do everything a
   popular high-install skill does

🚨 Any mention of sending data to a webhook URL that isn't documented

---

## Quick Scan Commands

Run these from the skill directory before enabling:

```bash
# Check for hardcoded secrets patterns
grep -rEi "(api_key|secret|password|token|bearer)\s*=\s*['\"][a-zA-Z0-9_\-]{10,}" .

# Check for dangerous shell patterns
grep -rE "(curl|wget)\s+.*\|\s*(bash|sh|zsh)" .
grep -r "base64 -d" .
grep -r "eval \$(" .
grep -r "set -x" .

# Check for credential echoing
grep -rEi "echo.*(\$[A-Z_]*KEY|\$[A-Z_]*TOKEN|\$[A-Z_]*SECRET)" .

# Check for file exfiltration patterns
grep -rE "(~/.ssh|~/.aws|~/.gnupg|~/.openclaw)" .
```

All results should be empty or explained by the skill's documented purpose.

---

## Reporting a Suspicious Skill

If you find a skill that fails this audit on ClawHub:

1. Click "Report" on the skill's ClawHub page
2. Use the category "Credential leak" or "Malicious behavior"
3. Paste the specific line(s) that triggered your concern
4. Skills with 3+ unique reports are auto-hidden pending review

You can also open an issue at https://github.com/openclaw/clawhub


path: references/env-injection-examples.md
content: # Env Injection Examples: Common API Integrations

Copy-paste frontmatter and shell patterns for popular services.

---

## OpenAI / Anthropic

```yaml
metadata: {"openclaw": {"requires": {"env": ["OPENAI_API_KEY"]}, "primaryEnv": "OPENAI_API_KEY"}}
```

```bash
# Safe curl
curl -s https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}'
```

```yaml
metadata: {"openclaw": {"requires": {"env": ["ANTHROPIC_API_KEY"]}, "primaryEnv": "ANTHROPIC_API_KEY"}}
```

---

## GitHub

```yaml
metadata: {"openclaw": {"requires": {"env": ["GITHUB_TOKEN"]}, "primaryEnv": "GITHUB_TOKEN"}}
```

```bash
# Using gh CLI — key injected via env, never in command args
GH_TOKEN=$GITHUB_TOKEN gh api /repos/owner/repo/issues
```

---

## Stripe

```yaml
metadata: {"openclaw": {"requires": {"env": ["STRIPE_SECRET_KEY"]}, "primaryEnv": "STRIPE_SECRET_KEY"}}
```

```bash
curl -s https://api.stripe.com/v1/customers \
  -u "$STRIPE_SECRET_KEY:"
# Note: -u USER:PASS format — key goes in the colon-separated user field, not echoed
```

---

## Slack

```yaml
metadata: {"openclaw": {"requires": {"env": ["SLACK_BOT_TOKEN"]}, "primaryEnv": "SLACK_BOT_TOKEN"}}
```

```bash
curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"channel\":\"#general\",\"text\":\"Hello\"}"
```

---

## Google / GCP

```yaml
metadata: {"openclaw": {"requires": {"env": ["GOOGLE_APPLICATION_CREDENTIALS"]}, "primaryEnv": "GOOGLE_APPLICATION_CREDENTIALS"}}
```

```bash
# GCP uses a credentials FILE path, not the key itself
# GOOGLE_APPLICATION_CREDENTIALS should point to the JSON file path
gcloud auth activate-service-account --key-file="$GOOGLE_APPLICATION_CREDENTIALS"
```

> Note: Never read and echo the contents of the credentials file — pass only the file path.

---

## AWS

```yaml
metadata: {"openclaw": {"requires": {"env": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]}, "primaryEnv": "AWS_ACCESS_KEY_ID"}}
```

```bash
# AWS CLI picks up env vars automatically — no need to pass them explicitly
aws s3 ls s3://my-bucket
```

---

## Twilio

```yaml
metadata: {"openclaw": {"requires": {"env": ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"]}, "primaryEnv": "TWILIO_AUTH_TOKEN"}}
```

```bash
curl -s -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Messages.json" \
  --data-urlencode "From=+15551234567" \
  --data-urlencode "To=+15559876543" \
  --data-urlencode "Body=Hello" \
  -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"
```

---

## Multiple Keys Pattern

When a skill needs several keys, list all in `requires.env`:

```yaml
metadata: {"openclaw": {"requires": {"env": ["SERVICE_API_KEY", "SERVICE_WEBHOOK_SECRET"]}, "primaryEnv": "SERVICE_API_KEY"}}
```

In `openclaw.json`, the user adds:
```json
{
  "skills": {
    "entries": {
      "my-skill": {
        "env": {
          "SERVICE_API_KEY": "sk-...",
          "SERVICE_WEBHOOK_SECRET": "whsec-..."
        }
      }
    }
  }
}
```

---

## Python Script Pattern

If your skill uses a Python script, inject the key via env — never via argparse:

```python
import os
import sys

[REDACTED_SECRET]"MY_SERVICE_API_KEY")
if not [REDACTED_SECRET]"ERROR: MY_SERVICE_API_KEY is not set.", file=sys.stderr)
    sys.exit(1)

# Use api_key — never print it, never include in exceptions
```

**Dangerous pattern to avoid:**
```python
# WRONG — key appears in error output which goes back to agent context
try:
    response = call_api(api_key)
except Exception as e:
    print(f"API call failed with key {api_key}: {e}")  # DO NOT DO THIS
```


Security scan verdicts — ClawScan: clean | static analysis: suspicious | VirusTotal: clean | SkillSpector: clean (LOW)