T09 · Insecure Skill Coding Practices
- Location
scripts/fitbit_setup.py:53- Finding
Sensitive credentials, OAuth tokens, and health records are stored without restrictive file permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Fitbit health skill is mostly coherent, but it exposes sensitive health data and stored tokens to unrestricted Python execution and stores credentials in plaintext without clear safeguards.
Review carefully before installing. Use only if you are comfortable storing Fitbit credentials, OAuth tokens, synced health records, and optional health-condition notes in local plaintext files, and avoid using the custom Python analysis feature unless you fully trust the exact code being executed. Prefer a version that uses secure secret storage, owner-only file permissions, explicit confirmations for health-data access, and fixed analysis commands instead of unrestricted exec.
scripts/fitbit_setup.py:53Sensitive credentials, OAuth tokens, and health records are stored without restrictive file permissions
scripts/fitbit_analyze.py:159Unrestricted agent-directed Python execution exposes health data and host process privileges
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def exchange_code(code: str) -> dict:
"""Exchange authorization code for tokens."""
resp = httpx.post(
TOKEN_URL,
data={
"client_id": CLIENT_ID,
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def refresh_token(token_data: dict) -> dict:
"""Refresh an expired access token."""
resp = httpx.post(
TOKEN_URL,
data={
"grant_type": "refresh_token",
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
cd ~/.openclaw/workspace/skills/personal-health-agent
cp .env.example .env
# Edit .env with your Client ID and Secret
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
cd ~/.openclaw/workspace/skills/personal-health-agent
cp .env.example .env
# Edit .env with your Client ID and Secret
/health_setup and follow the prompts.Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
```bash
cd ~/.openclaw/workspace/skills/personal-health-agent
cp .env.example .env
# Edit .env with your Client ID and Secret
```
3. **Authorize:** Tell your OpenClaw agent `/health_setup` and follow the prompts.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
```bash
cd ~/.openclaw/workspace/skills/personal-health-agent
cp .env.example .env
# Edit .env with your Client ID and Secret
```
3. **Authorize:** Tell your OpenClaw agent `/health_setup` and follow the prompts.
The skill handles Fitbit client credentials, OAuth callback data, and detailed health telemetry, but the description and setup flow do not provide clear privacy, retention, or handling warnings at the point of collection. Given the sensitivity of both health data and authentication artifacts, users may disclose secrets without understanding storage, logging, and exposure risks.
The skill instructs the agent to ask the user to paste the Fitbit Client ID, Client Secret, and the full OAuth callback URL directly into conversation, then reuse them in shell commands. This exposes sensitive credentials and authorization artifacts to chat logs, prompt history, and potentially other tools or operators, creating a straightforward path to account compromise and unauthorized access to health data.
The skill explicitly instructs the agent to execute arbitrary Python supplied as a string via fitbit_analyze.py, which creates a broad code-execution surface beyond normal health analytics. If a user or downstream prompt can influence that code, it could read local files, exfiltrate health data or secrets, or perform unintended system actions under the agent's privileges.
Although the skill is presented as a Fitbit health analysis assistant, it exposes a general arbitrary Python execution surface over personal health data. The mismatch in expected capability increases the chance that users or downstream agents will pass untrusted prompts/code, leading to full compromise of confidentiality and integrity within the local execution context.
The code executes arbitrary Python without any confirmation prompt, trust check, or warning at the point of execution, despite exposing sensitive Fitbit and profile data in the namespace. In this health-assistant context, silent execution materially raises the chance of unnoticed exfiltration or destructive actions because users may expect benign data analysis rather than unrestricted code execution.
The script directly executes attacker-controlled Python via exec(code, namespace) with full Python semantics and access to imported modules, filesystem paths, and sensitive Fitbit/profile data. This enables arbitrary code execution, local file access, data exfiltration, and potentially shell command execution through Python's standard capabilities.
stdout = io.StringIO()
try:
with contextlib.redirect_stdout(stdout):
exec(code, namespace)
output = stdout.getvalue()
if output:
print(output)
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def refresh_token(token_data: dict) -> dict:
"""Refresh an expired access token."""
resp = httpx.post(
TOKEN_URL,
data={
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def main():
if not CLIENT_ID or not CLIENT_SECRET:
print("ERROR: Set FITBIT_CLIENT_ID and FITBIT_CLIENT_SECRET in .env")
print(f" .env location: {BASE_DIR / '.env'}")
sys.exit(1)
# Non-interactive: exchange a redirect URL
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).
ng_hr"].mean()
std = hr_valid["resting_hr"].std()
if std > 0:
recent = hr_valid.tail(3)
for _, row in recent.iterrows():
z = (row["resting_hr"] - mean) / std
if z > 2:
insights.append(make_insight(
"anomaly", "high",
"❤️🔥 Elevated resting heart rate",
f"On {row['date'].strftime('%b %d')}, your resting HR was {row['resting_hr']:.0f} bpm — "
f"above your baseline of {mean:.0f} bpm. Could indicate stress, poor sleep, illness, or dehydration.",
{"date": str(row["date"].date()), "resting_hr": round(row["resting_hr"]),
"avg_hr": round(mean), "z_score": round(z, 2)},
"Monitor over the next few days. If consistently elevated, consider: Are you stressed? Getting enough sl
This code path saves Fitbit client credentials into plaintext .env storage, creating a credential exposure risk if the repository, filesystem, backups, or logs are accessible to others. In a health-agent skill, that risk is more serious because the stored credentials enable access to a healthcare-adjacent data integration handling sensitive wellness data.
def save_credentials(client_id, client_secret):
"""Save credentials to .env file."""
lines = []
if ENV_FILE.exists():
for line in ENV_FILE.read_text().splitlines():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
BASE_DIR = Path(__file__).parent.parent
load_dotenv(BASE_DIR / ".env")
DATA_DIR = BASE_DIR / "data" / "user_default"
TOKEN_FILE = BASE_DIR / "data" / "fitbit_token.json"
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
BASE_DIR = Path(__file__).parent.parent
load_dotenv(BASE_DIR / ".env")
DATA_DIR = BASE_DIR / "data" / "user_default"
TOKEN_FILE = BASE_DIR / "data" / "fitbit_token.json"
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
BASE_DIR = Path(__file__).parent.parent
load_dotenv(BASE_DIR / ".env")
DATA_DIR = BASE_DIR / "data" / "user_default"
TOKEN_FILE = BASE_DIR / "data" / "fitbit_token.json"
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
BASE_DIR = Path(__file__).parent.parent
load_dotenv(BASE_DIR / ".env")
DATA_DIR = BASE_DIR / "data" / "user_default"
TOKEN_FILE = BASE_DIR / "data" / "fitbit_token.json"
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from dotenv import load_dotenv
BASE_DIR = Path(__file__).parent.parent
load_dotenv(BASE_DIR / ".env")
DATA_DIR = BASE_DIR / "data" / "user_default"
TOKEN_FILE = BASE_DIR / "data" / "fitbit_token.json"
The usage examples are broad, natural-language requests such as asking about sleep, heart rate trends, or weekly summaries. In an agent ecosystem, this can cause the skill to activate on ordinary conversation without clear user intent, increasing the chance that sensitive health data is accessed or summarized unexpectedly.
The README says the skill works with 'any model — Claude, GPT, Gemini' while also claiming 'Your health data stays local on your machine' and that the agent 'processes everything locally.' If a cloud-hosted model is used, prompts or derived health data may leave the machine, so the privacy claim is misleading and can cause users to disclose sensitive medical information under false assumptions.
The skill invokes local scripts, reads and writes data during setup/sync/onboarding, and interacts with Fitbit authorization flows, yet it declares no explicit tool/permission scope. That mismatch weakens least-privilege controls and makes it harder for the host to constrain file, environment, and network access for a skill handling sensitive health data and credentials.
The setup text says the flow should begin when the user says /health_setup or asks to connect their Fitbit, which broadens activation beyond an explicit command. In a sensitive skill, vague semantic triggering can cause the agent to enter credential-collection and authorization flows unexpectedly, increasing the chance of accidental invocation or prompt-confusion-driven misuse.