Back to skill

Security audit

Whoop Guru

Security checks for vulnerabilities and agentic risk

Overview

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.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
lib/whoop-fetcher.sh:8
Finding

Credential File Is Executed as Arbitrary Shell Code

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
lib/llm.py:203
Finding

LLM API Keys Are Persisted Without Enforcing Restrictive Permissions

Content
View full analysis
bool: """保存用户配置""" os.makedirs(os.path.dirname(CONFIG_FILE), exist_ok=True) all_configs = {} if os.path.exists(CONFIG_FILE): try: with open(CONFIG_FILE, 'r') as f: all_configs = json.load(f) except Exception: pass all_configs[self.user_id] = { "provider": self.config.provider, "api_key": self.config.api_key, "api_url": self.config.api_url, "model": self.config.model, "temperature": self.config.temperature, "max_tokens": self.config.max_tokens } try: with open(CONFIG_FILE, 'w') as f: json.dump(all_configs, f, ensure_ascii=False, indent=2) return True except Exception: return False ``` ### Technical Analysis The LLM API key is written in plaintext to `data/config/llm_config.json`. Unlike WHOOP token storage in `scripts/whoop_auth.py`, this code does not apply mode `0600`, validate ownership, use an operating-system credential store, or write through a securely created temporary file. The resulting permissions depend on the process umask and installation directory. In a shared or permissively configured environment, another local account may be able to read the API key. Keeping the secret inside the project tree also increases the risk that it will be copied into archives, support bundles, deployments, or source-control commits. The committed configuration observed during the audit was empty (`{}`), and no bundled API key was found. The vulnerability affects keys saved by users at runtime. ### Attack Path 1. A user configures an LLM provider and enters an API key. 2. `_save_config()` writes the key to `data/config/llm_config.json`. 3. The file inherits default ...[truncated 809 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
lib/llm.py:232
Finding

Unvalidated LLM Endpoint Can Receive API Credentials and Sensitive Health Data

Content
View full analysis
bool: """ 配置用户LLM设置 Args: provider: 提供商ID api_key: API密钥 model: 模型名称 (可选) api_url: API地址 (可选) Returns: 是否配置成功 """ if provider not in PROVIDERS: return False self.config.provider = provider self.config.api_key = api_key self.config.api_url = api_url or PROVIDERS[provider]["api_url"] self.config.model = model or PROVIDERS[provider]["default_model"] return self._save_config() ``` ```python def _chat_custom(self, messages, temperature, max_tokens): """自定义API""" return self._chat_openai(messages, temperature, max_tokens) def _post_request(self, url, headers, data): """通用的POST请求""" try: response = requests.post(url, headers=headers, json=data, timeout=60) result = response.json() ``` The transmitted request includes the credential and prompt: ```python headers = { "Authorization": f"Bearer {self.config.api_key}", "Content-Type": "application/json" } data = { "model": self.config.model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens } return self._post_request(self.config.api_url, headers, data) ``` ### Technical Analysis The configuration method accepts an arbitrary `api_url` for any recognized provider, including the explicitly supported custom provider. No validation requires HTTPS, checks the destination hostname, blocks loopback or private-network destinations, or asks for confirmation when the endpoint differs from the provider's official API. The req ...[truncated 1972 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (159)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · CLAWHUB.md (reported line 120)May include surrounding context.

md
- `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`.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 153)May include surrounding context.

md
- `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`.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 357)May include surrounding context.

md
- `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`.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · _meta.json (reported line 44)May include surrounding context.

json
- `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`.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · data/config/README.md (reported line 7)May include surrounding context.

md
- `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`.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · lib/whoop-fetcher.sh (reported line 8)May include surrounding context.

sh
- `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`.

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

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.

Content

Scanner excerpt · docs/whoop_api_guide.md (reported line 54)May include surrounding context.

6. 配置凭证

创建 ~/.clawdbot/whoop-credentials.env:

bash
WHOOP_CLIENT_ID="your_client_id"

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
94% confidence
Finding

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.

Content

No source excerpt is available for this finding.

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · lib/whoop-fetcher.sh (reported line 34)May include surrounding context.

sh
# 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" \

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · lib/whoop-fetcher.sh (reported line 32)May include surrounding context.

sh
# 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

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · lib/whoop-fetcher.sh (reported line 51)May include surrounding context.

sh
# 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

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/whoop_auth.py (reported line 9)May include surrounding context.

python
# 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

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/whoop_auth.py (reported line 88)May include surrounding context.

python
# 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

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/whoop_auth.py (reported line 123)May include surrounding context.

python
# 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

Credential Access

High
Category
Privilege Escalation
Confidence
85% confidence
Finding

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.

Content

Scanner excerpt · scripts/whoop_auth.py (reported line 137)May include surrounding context.

python
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

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

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.

Content

Scanner excerpt · scripts/whoop_auth.py (reported line 284)May include surrounding context.

python
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")

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

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.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · docs/whoop_api_guide.md (reported line 32)May include surrounding context.

4. 用 code 换 token

bash
curl -X POST https://api.prod.whoop.com/oauth/oauth2/token \
  -d "grant_type=authorization_code" \
  -d "code=刚才获得的code" \
  -d "client_id=你的CLIENT_ID" \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.