Back to skill

Security audit

WHOOP (Official API)

Security checks for vulnerabilities and agentic risk

Overview

This WHOOP skill has a coherent purpose, but it handles sensitive health data and OAuth tokens with under-scoped defaults and weak local storage practices that deserve careful review before installation.

Install only if you are comfortable granting broad WHOOP read access and storing tokens plus health summaries on this machine. Set WHOOP_SCOPES and WHOOP_TZ deliberately, avoid /tmp for outputs, restrict token/output file permissions, and require explicit confirmation before sending or scheduling WHOOP summaries to external chat channels.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/whoop_oauth_login.py:37
Finding

OAuth Authorization Flow Lacks State Validation and PKCE Binding

Content
View full analysis
str: s = user_input.strip() if s.startswith("http://") or s.startswith("https://"): u = urllib.parse.urlparse(s) q = urllib.parse.parse_qs(u.query) code = (q.get("code") or [None])[0] if code: return code raise SystemExit("Could not find ?code= in the pasted redirect URL") return s ``` ```python q = urllib.parse.parse_qs(parsed.query) code = (q.get("code") or [None])[0] if code: got["code"] = code ``` ```python params = { "client_id": client_id, "redirect_uri": redirect_uri, "response_type": "code", "scope": scopes, } auth_url = OAUTH_AUTH_URL + "?" + urllib.parse.urlencode(params) ``` ```python tok = exchange_code_for_token( code=code, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, ) ``` ```python def exchange_code_for_token(*, code: str, client_id: str, client_secret: str, redirect_uri: str) -> Dict[str, Any]: tok = _post_form( OAUTH_TOKEN_URL, { "grant_type": "authorization_code", "code": code, "client_id": client_id, "client_secret": client_secret, "redirect_uri": redirect_uri, }, ) _annotate_expiry(tok) return tok ``` ### Technical Analysis The authorization request does not include a cryptographically random OAuth `state` parameter. Neither the copy-and-paste callback parser nor the loopback HTTP handler validates that a callback belongs to the authorization request initiated by the current process. The implementation also does not use PKCE. It therefore has no `code_challenge` in the authorization request and no corresp ...[truncated 1767 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/whoop_fetch.py:228
Finding

Sensitive Health Data Is Written Through Predictable and Insufficiently Protected Output Files

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/whoop_token.py:32
Finding

OAuth Tokens Are Initially Written to an Insecure Predictable Temporary File

Content
View full analysis
None: Path(path).parent.mkdir(parents=True, exist_ok=True) def token_path() -> str: return os.environ.get("WHOOP_TOKEN_PATH", DEFAULT_TOKEN_PATH) def load_token(path: Optional[str] = None) -> Dict[str, Any]: p = path or token_path() with open(p, "r", encoding="utf-8") as f: return json.load(f) def save_token(token: Dict[str, Any], path: Optional[str] = None) -> str: p = path or token_path() _mkdirp(p) tmp = p + ".tmp" with open(tmp, "w", encoding="utf-8") as f: json.dump(token, f, indent=2, sort_keys=True) f.write("\n") os.replace(tmp, p) try: os.chmod(p, 0o600) except PermissionError: pass return p ``` ### Technical Analysis The final token path is changed to mode `0600`, but only after credentials have already been written to `p + ".tmp"` and moved into place. The temporary file: - Has a predictable name. - Is opened with umask-dependent permissions. - Is created without exclusive or no-follow flags. - May remain on disk if the process fails before replacement. - Can follow an existing symbolic link. - Is placed in a parent directory whose permissions are not explicitly restricted to `0700`. The token JSON can contain both an access token and a long-lived refresh token. Applying `chmod` only after `os.replace` does not protect the temporary file during creation and writing. ### Attack Path 1. An OAuth login or token refresh invokes `save_token`. 2. In a shared or insufficiently protected configured token directory, a local attacker predicts the `.tmp` filename. 3. The attacker observes the temporary file while it has permissive mode, or pre-creates the path as a symbolic link where filesystem permissions permit. 4. ...[truncated 715 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Note
Location
scripts/whoop_oauth_login.py:27
Finding

Default Workflow Requests and Retrieves More Personal Data Than Required

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (28)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

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

md
2) The script stores tokens at `WHOOP_TOKEN_PATH`. 

If you need to revoke later, use `delete /v2/user/access` (see `references/whoop_api.md`).

## Workflow 2 — Fetch metrics (today / yesterday)

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 · references/openapi.json (reported line 292)May include surrounding context.

json
"post" : {
        "tags" : [ "Partner" ],
        "summary" : "Request a partner client token",
        "description" : "Exchanges partner client credentials for an access token.",
        "operationId" : "requestToken",
        "requestBody" : {
          "content" : {

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 · references/openapi.json (reported line 706)May include surrounding context.

json
"post" : {
        "tags" : [ "Partner" ],
        "summary" : "Request a partner client token",
        "description" : "Exchanges partner client credentials for an access token.",
        "operationId" : "requestToken",
        "requestBody" : {
          "content" : {

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · references/whoop_api.md (reported line 46)May include surrounding context.

md
- Workout collection: `GET /v2/activity/workout`
- Basic profile: `GET /v2/user/profile/basic`
- Body measurements: `GET /v2/user/measurement/body`
- Revoke access: `DELETE /v2/user/access`

## Pagination

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_token.py (reported line 5)May include surrounding context.

python
"""WHOOP token utilities.

- Store tokens in a local JSON file.
- Refresh access tokens when expired.

No third-party deps.
"""

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill advertises very broad trigger phrases such as 'pull WHOOP data' and 'push WHOOP updates to any chat channel', which can cause the integration to activate on ambiguous requests and process sensitive health data without sufficiently explicit user intent. In a health-data context, overbroad invocation increases the chance of accidental retrieval or sharing of private biometric information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The description promotes local token storage and sending WHOOP updates to external chat channels, but it does not clearly warn users that OAuth tokens will be stored locally and that sensitive health data may be copied into third-party messaging platforms. Because WHOOP data includes personal wellness and body metrics, the absence of an upfront privacy warning materially increases the risk of unintended exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This OpenAPI manifest documents endpoints that retrieve sensitive user data such as body measurements, email, sleep, recovery, and workout information, but the file does not include any warning or disclosure about handling personal or health-related data. For markdown and manifest-style descriptions, SQP-2 applies when behaviors affecting user privacy are described without corresponding warnings.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

This markdown file documents OAuth authorization, token exchange, and endpoints for profile, body measurements, sleep, recovery, and workout data, all of which involve sensitive personal data. Under the markdown-file criteria for missing warnings, the description should explicitly warn that the skill may access health/profile data and requires secure handling of OAuth tokens and user consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code performs multiple HTTP requests to the WHOOP API for profile, body measurement, recovery, sleep, cycle, and workout data, then writes the collected raw bundle to the path supplied by the user. Although the module docstring describes the behavior, there is no runtime disclosure, confirmation, or explicit warning that sensitive health data will be retrieved and saved locally.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code silently defaults to a specific locale/timezone, which is a natural-language policy concern because it imposes a regional setting without explicit user choice. The CLI allows --tz, but the default behavior still selects Asia/Shanghai automatically rather than prompting or using a neutral/default-local option.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code assembles and writes profile fields including name and email, along with health-related summary data, to the output file. Although the module docstring explains normalization, there is no confirmation prompt, print/log message, or explicit warning that sensitive user data will be persisted to disk.

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 · references/openapi.json (reported line 7)May include surrounding context.

json
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/openapi.json (reported line 932)May include surrounding context.

json
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/openapi.json (reported line 1420)May include surrounding context.

json
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/openapi.json (reported line 1533)May include surrounding context.

json
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/openapi.json (reported line 1733)May include surrounding context.

json
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/openapi.json (reported line 1734)May include surrounding context.

json
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/openapi.json (reported line 1750)May include surrounding context.

json
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/whoop_api.md (reported line 12)May include surrounding context.

md
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/whoop_api.md (reported line 19)May include surrounding context.

md
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · references/whoop_api.md (reported line 21)May include surrounding context.

md
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · scripts/whoop_fetch.py (reported line 43)May include surrounding context.

python
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · scripts/whoop_token.py (reported line 22)May include surrounding context.

python
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

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 · scripts/whoop_token.py (reported line 23)May include surrounding context.

python
from typing import Any, Dict, Optional


OAUTH_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"
OAUTH_TOKEN_URL = "https://api.prod.whoop.com/oauth/oauth2/token"

DEFAULT_TOKEN_PATH = os.path.expanduser("~/.config/openclaw/whoop/token.json")

Static analysis

No suspicious patterns detected.