T09 · Insecure Skill Coding Practices
- Location
lib/whoop-fetcher.sh:8- Finding
Credential File Is Executed as Arbitrary Shell Code
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears purpose-aligned, but it handles sensitive health data and API credentials with several weak controls that should be reviewed before installation.
Install only if you are comfortable giving the skill access to WHOOP recovery, sleep, workout, profile, and body data. Prefer the OAuth flow over the manual credentials.env file, restrict permissions on any credential files, avoid printing access tokens, and use only trusted HTTPS LLM endpoints because AI analysis may transmit personal health data and goals to that provider.
lib/whoop-fetcher.sh:8Credential File Is Executed as Arbitrary Shell Code
lib/llm.py:203LLM API Keys Are Persisted Without Enforcing Restrictive Permissions
lib/llm.py:232Unvalidated LLM Endpoint Can Receive API Credentials and Sensitive Health Data
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- `llm_config.json`: LLM API keys (user-configured via chat)
- `whoop-tokens.json`: NOT stored here - WHOOP tokens stored at `~/.clawdbot/whoop-tokens.json`
**Note**: WHOOP OAuth tokens are automatically stored by `scripts/whoop_auth.py` at `~/.clawdbot/whoop-tokens.json`. The WHOOP fetcher script (`lib/whoop-fetcher.sh`) also uses `~/.clawdbot/whoop-credentials.env`.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- `llm_config.json`: LLM API keys (user-configured via chat)
- `whoop-tokens.json`: NOT stored here - WHOOP tokens stored at `~/.clawdbot/whoop-tokens.json`
**Note**: WHOOP OAuth tokens are automatically stored by `scripts/whoop_auth.py` at `~/.clawdbot/whoop-tokens.json`. The WHOOP fetcher script (`lib/whoop-fetcher.sh`) also uses `~/.clawdbot/whoop-credentials.env`.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- `llm_config.json`: LLM API keys (user-configured via chat)
- `whoop-tokens.json`: NOT stored here - WHOOP tokens stored at `~/.clawdbot/whoop-tokens.json`
**Note**: WHOOP OAuth tokens are automatically stored by `scripts/whoop_auth.py` at `~/.clawdbot/whoop-tokens.json`. The WHOOP fetcher script (`lib/whoop-fetcher.sh`) also uses `~/.clawdbot/whoop-credentials.env`.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- `llm_config.json`: LLM API keys (user-configured via chat)
- `whoop-tokens.json`: NOT stored here - WHOOP tokens stored at `~/.clawdbot/whoop-tokens.json`
**Note**: WHOOP OAuth tokens are automatically stored by `scripts/whoop_auth.py` at `~/.clawdbot/whoop-tokens.json`. The WHOOP fetcher script (`lib/whoop-fetcher.sh`) also uses `~/.clawdbot/whoop-credentials.env`.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- `llm_config.json`: LLM API keys (user-configured via chat)
- `whoop-tokens.json`: NOT stored here - WHOOP tokens stored at `~/.clawdbot/whoop-tokens.json`
**Note**: WHOOP OAuth tokens are automatically stored by `scripts/whoop_auth.py` at `~/.clawdbot/whoop-tokens.json`. The WHOOP fetcher script (`lib/whoop-fetcher.sh`) also uses `~/.clawdbot/whoop-credentials.env`.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- `llm_config.json`: LLM API keys (user-configured via chat)
- `whoop-tokens.json`: NOT stored here - WHOOP tokens stored at `~/.clawdbot/whoop-tokens.json`
**Note**: WHOOP OAuth tokens are automatically stored by `scripts/whoop_auth.py` at `~/.clawdbot/whoop-tokens.json`. The WHOOP fetcher script (`lib/whoop-fetcher.sh`) also uses `~/.clawdbot/whoop-credentials.env`.
The guide directs users to create a credentials file containing client ID, client secret, and refresh token in plaintext under the home directory. This materially increases credential exposure risk because these values provide durable access and are often harvested from dotfiles, backups, malware scans, or accidental source-control inclusion.
创建 ~/.clawdbot/whoop-credentials.env:
WHOOP_CLIENT_ID="your_client_id"
User-provided fitness goals, limitations, injuries, weight targets, and related profile details are incorporated into prompts and sent to the LLM without visible disclosure. In this skill context, the data is health-adjacent and personally revealing, so undisclosed transfer to a model backend materially increases privacy risk and could expose sensitive user information to third-party processing.
The analysis prompt includes WHOOP-derived recovery and HRV data and sends it to an LLM without any visible disclosure or consent. This is dangerous because wearable health metrics are sensitive personal data, and transmitting them to an external model provider can expand exposure, create third-party retention risk, and potentially violate user expectations or privacy requirements.
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.
# Get access token
get_access_token() {
local response=$(curl -s -X POST "https://api.prod.whoop.com/oauth/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=$WHOOP_REFRESH_TOKEN" \
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# First-time login (opens browser)
python3 whoop_auth.py login --client-id ID --client-secret SECRET
# Get current access token (auto-refreshes if expired)
python3 whoop_auth.py token
# Check auth status
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# First-time login (opens browser)
python3 whoop_auth.py login --client-id ID --client-secret SECRET
# Get current access token (auto-refreshes if expired)
python3 whoop_auth.py token
# Check auth status
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# First-time login (opens browser)
python3 whoop_auth.py login --client-id ID --client-secret SECRET
# Get current access token (auto-refreshes if expired)
python3 whoop_auth.py token
# Check auth status
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# First-time login (opens browser)
python3 whoop_auth.py login --client-id ID --client-secret SECRET
# Get current access token (auto-refreshes if expired)
python3 whoop_auth.py token
# Check auth status
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# First-time login (opens browser)
python3 whoop_auth.py login --client-id ID --client-secret SECRET
# Get current access token (auto-refreshes if expired)
python3 whoop_auth.py token
# Check auth status
The function returns the raw access token and the CLI exposes a token command that prints it, which can leak credentials to terminal scrollback, shell capture, process supervisors, or other tooling that logs stdout. In an agent skill context, surfaced tokens are especially sensitive because upstream orchestration or chat transcripts may capture command output.
access_token = tokens.get("access_token")
if not access_token:
print("No access token found. Run: whoop_auth.py login", file=sys.stderr)
sys.exit(1)
return access_token
The command intentionally prints the current access token, enabling easy disclosure of a bearer credential to anyone or anything with access to stdout or captured logs. In automation/agent environments, stdout is often centrally logged, making this context more dangerous than an interactive local-only CLI.
login_p.add_argument("--client-id", required=True)
login_p.add_argument("--client-secret", required=True)
sub.add_parser("token", help="Print current access token")
sub.add_parser("status", help="Show auth status")
sub.add_parser("logout", help="Delete stored tokens")
The file states that all time processing uses Beijing time (UTC+8), which is a locale constraint expressed in natural language. Under SQP-3, forcing a specific locale without user opt-in or clear region-specific justification is a policy violation.
This markdown file applies to SQP-2, and it states that user health data is sent to an LLM API for AI analysis. While the data flow is mentioned, there is no clear warning calling out the privacy sensitivity of health data or advising users to review third-party handling before enabling the feature.
The feature description states that all time processing uniformly uses Beijing time (UTC+8). This is a locale policy concern because it imposes a specific timezone on all users without any opt-in, choice, or clear justification that the skill is limited to a Beijing-region use case.
The README explicitly documents that API keys and OAuth tokens are stored on disk, but provides no warning about their sensitivity, file permission requirements, or handling risks. This can normalize insecure credential storage and increase the chance that secrets are left in readable locations, backed up unintentionally, or exposed through logs, support bundles, or local compromise.
This JSON data uses Chinese-only values such as exercise types, completion status, feedback, and notes throughout the file. Under the policy for natural-language violations, forcing a specific language without offering user choice or documenting a justified locale constraint is a reportable issue.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
curl -X POST https://api.prod.whoop.com/oauth/oauth2/token \
-d "grant_type=authorization_code" \
-d "code=刚才获得的code" \
-d "client_id=你的CLIENT_ID" \
The guide instructs users to place OAuth client credentials and a long-lived refresh token into a plaintext local env file without any warning about file permissions, secure storage, or secret rotation. If that file is exposed through weak filesystem permissions, backups, logs, or accidental commits, an attacker could reuse the refresh token and client secret to obtain access tokens and impersonate the user or application.
Natural-language content in the module docstring and generated check-in messages is written exclusively in Chinese, with no indication that the user can choose another language. This creates a language-policy concern because the skill imposes a specific locale rather than offering user choice or documenting a justified regional constraint.
No suspicious patterns detected.