Back to skill

Security audit

Ai Coach

Security checks for vulnerabilities and agentic risk

Overview

This AI training coach has a coherent purpose, but it handles health data and account credentials with several under-scoped and under-disclosed storage and sharing behaviors.

Review before installing. This skill may be useful if you want a Chinese-language triathlon coach connected to TrainingPeaks and Garmin, but you should not use it as-is with real credentials unless the Garmin password storage is replaced with a secure credential store, the IMA API keys are removed and replaced with your own configured credentials, and the Garmin token/cache paths are moved to a private per-user directory with clear deletion controls.

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

Error
Location
scripts/send_daily_plan.py:21
Finding

Hard-Coded IMA API Credentials Exposed in Source Code

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/garmin_sync.py:29
Finding

Garmin Account Password Stored in Plaintext and Accepted Through Command-Line Arguments

Content
View full analysis
") else: save_credentials(sys.argv[2], sys.argv[3]) print(f"Garmin account saved: {sys.argv[2]}") ``` The saved plaintext values are later used for authentication: ```python creds = load_credentials() if not creds: print("Configure a Garmin account first") return None client = garth.Client(domain='garmin.com') for attempt in range(max_retries): try: client.login(creds["email"], creds["password"]) ``` ### Technical Analysis The implementation serializes the Garmin email address and password directly into `scripts/data/credentials.json`. It does not encrypt the password, use an operating-system credential service, or explicitly set file mode `0600`. The file's actual permissions therefore depend on the process umask and surrounding directory permissions. In a permissive environment, another local account or process may be able to read it. The file may also be unintentionally included in source archives, backups, support bundles, or repository commits because it is stored beneath the Skill directory. Passing the password as `sys.argv[3]` creates an additional disclosure channel. Depending on the environment, command-line arguments can temporarily appear in process inspection tools and may remain i ...[truncated 1673 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/plan_engine.py:82
Finding

Garmin Authentication and Health Data Use Predictable or Cross-Skill Storage Paths

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (37)

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

The presence and use of a dedicated credentials.json file indicates the skill persists third-party account secrets locally for later authentication. While the filename alone is not malicious, in this code it corresponds to active credential collection and reuse, which materially increases exposure if the workstation, repository, or data directory is accessed by an attacker.

Content

Scanner excerpt · scripts/garmin_sync.py (reported line 17)May include surrounding context.

python
PYTHON = Path.home() / ".miniconda3/bin/python3"

DATA_DIR = Path(__file__).parent / "data"
CREDENTIALS_FILE = DATA_DIR / "credentials.json"
ACTIVITIES_FILE = DATA_DIR / "activities.json"

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script contains hardcoded IMA API credentials directly in source code, which makes secret exposure likely through source control, logs, backups, or redistribution of the skill. Anyone obtaining these credentials could impersonate the application to access the external note API, create or modify notes, or abuse the associated account until the keys are rotated.

Content

No source excerpt is available for this finding.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · scripts/tp_sync.py (reported line 25)May include surrounding context.

python
start_date = (datetime.now() - timedelta(days=90)).strftime("%Y-%m-%d")
    
    cmd = f"{PYTHON} {TP_SCRIPT} workouts {start_date} {end_date} --json"
    result = os.popen(cmd).read()
    
    output_file = AI_COACH_DIR / "data" / "tp_workouts.json"
    output_file.parent.mkdir(exist_ok=True)

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · scripts/tp_sync.py (reported line 38)May include surrounding context.

python
start_date = (datetime.now() - timedelta(days=90)).strftime("%Y-%m-%d")
    
    cmd = f"{PYTHON} {TP_SCRIPT} workouts {start_date} {end_date} --json"
    result = os.popen(cmd).read()
    
    output_file = AI_COACH_DIR / "data" / "tp_workouts.json"
    output_file.parent.mkdir(exist_ok=True)

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

Duplicate finding of the same issue: the documentation directs users to save reusable session credentials to disk or environment variables. Even with chmod 600, a persistent session cookie remains a sensitive bearer credential that can be replayed if exfiltrated.

Content

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

方式A(推荐):写入文件

bash
mkdir -p ~/.trainingpeaks
echo "你的cookie值" > ~/.trainingpeaks/cookie
chmod 600 ~/.trainingpeaks/cookie

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

Duplicate finding of the same issue: the documentation directs users to save reusable session credentials to disk or environment variables. Even with chmod 600, a persistent session cookie remains a sensitive bearer credential that can be replayed if exfiltrated.

Content

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

方式A(推荐):写入文件

bash
mkdir -p ~/.trainingpeaks
echo "你的cookie值" > ~/.trainingpeaks/cookie
chmod 600 ~/.trainingpeaks/cookie

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

bash
mkdir -p ~/.trainingpeaks
echo "你的cookie值" > ~/.trainingpeaks/cookie
chmod 600 ~/.trainingpeaks/cookie

方式B:设置环境变量

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

bash
mkdir -p ~/.trainingpeaks
echo "你的cookie值" > ~/.trainingpeaks/cookie
chmod 600 ~/.trainingpeaks/cookie

方式B:设置环境变量

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill instructs users to place Garmin credentials in user_config.json or environment variables, but unlike the TrainingPeaks cookie section it does not provide an equivalent explicit warning about credential sensitivity, storage risks, or safe handling. This increases the chance of plaintext secret exposure through local files, shell history, backups, logs, or accidental repository commits.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

Natural-language policy review applies to all file types. The manifest description forces a single language presentation and does not indicate that users can choose their preferred language or that the locale restriction is intentional and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This manifest/config file uses Chinese field comments and values throughout, which can impose a specific language context without any opt-in or documented regional justification. Under the language/locale policy, forcing a single language may be a natural-language policy violation when no user choice is provided.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file’s comments, docstring, and all user-facing formatted content are written in Chinese, including prompts shown to the user. Under the policy, forcing a specific language without opt-in is a natural-language locale violation unless the skill clearly offers language choice or documents a justified regional constraint, which this file does not.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script stores Garmin credentials directly in a local JSON file in plaintext, which exposes the username and password to any local user, malware, backup system, or accidental file disclosure. In a skill context, this is more dangerous because the tool is explicitly designed to collect and reuse live account credentials for a third-party service.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Throughout the file, user-facing strings, labels, and generated plan text are fixed in Chinese, with no mechanism for user language selection or opt-in. This is a natural-language policy issue because it forces a specific language/locale on users rather than offering a choice or documenting a justified locale constraint.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/plan_engine.py (reported line 51)May include surrounding context.

python
"""同步 TrainingPeaks 数据"""
    try:
        python_path = os.path.expanduser("~/.miniconda3/bin/python3")
        result = subprocess.run(
            [python_path, str(TP_SKILL_DIR / "scripts/tp.py"), "workouts", 
             (datetime.now() - timedelta(days=90)).strftime("%Y-%m-%d"),
             datetime.now().strftime("%Y-%m-%d"), "--json"],

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/trainer.py (reported line 62)May include surrounding context.

python
"""同步 TrainingPeaks 数据"""
    try:
        python_path = os.path.expanduser("~/.miniconda3/bin/python3")
        result = subprocess.run(
            [python_path, str(TP_SKILL_DIR / "scripts/tp.py"), "workouts", 
             (datetime.now() - timedelta(days=90)).strftime("%Y-%m-%d"),
             datetime.now().strftime("%Y-%m-%d"), "--json"],

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/plan_engine.py (reported line 68)May include surrounding context.

python
"""获取体能状态"""
    try:
        python_path = os.path.expanduser("~/.miniconda3/bin/python3")
        result = subprocess.run(
            [python_path, str(TP_SKILL_DIR / "scripts/tp.py"), "fitness", "--json"],
            capture_output=True, text=True, timeout=30
        )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/trainer.py (reported line 79)May include surrounding context.

python
"""获取体能状态"""
    try:
        python_path = os.path.expanduser("~/.miniconda3/bin/python3")
        result = subprocess.run(
            [python_path, str(TP_SKILL_DIR / "scripts/tp.py"), "fitness", "--json"],
            capture_output=True, text=True, timeout=30
        )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The module persists Garmin-derived health data to a local cache file in a workspace path without any disclosure, retention policy, or file-permission hardening. Health metrics such as sleep, stress, and readiness are sensitive personal data, and storing them locally can expose them to other local processes or users if the environment is shared or compromised.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The docstring states the function will never actively re-login, but the companion fetch routine performs authenticated Garmin access using credentials and a token. This mismatch is dangerous because it can mislead reviewers or users about the module's actual data-access behavior, reducing informed consent and increasing the chance of undisclosed sensitive-data retrieval.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The planning module contains authenticated access logic for a Garmin account, including reading credentials from config/environment and loading a token from /tmp. Embedding credential-handling and account access in a planning component expands the module's privilege boundary and increases the risk of unintended access to sensitive health data if this function is invoked or reused elsewhere.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code reads Garmin credentials from user_config.json or environment variables and uses them for account access without any visible disclosure, consent check, or trust-boundary notice. In a skill context, silent use of personal credentials to retrieve health information is privacy-sensitive and can surprise users or operators, especially because the module's main purpose is plan generation rather than authentication.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The function sends generated plan content and titles to an external IMA service over the network without any visible consent, disclosure, or data-classification check. If the plan contains personal training details, schedules, names, or other sensitive content from configuration, this creates a privacy and data-governance risk because user data is exported off-system automatically.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
80% confidence
Finding

The top-level docstring says the script 'automatically syncs TrainingPeaks training data and updates AI coach configuration,' which suggests two effects: syncing and config update. However, sync_workouts and sync_fitness also persist raw data files under AI_COACH_DIR/data (L027-L030, L040-L042), an additional side effect not reflected in the documentation. This is an intent/documentation mismatch rather than a mere implementation detail because it expands where and how user data is stored.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script stores raw workout data to local disk without any consent prompt, minimization, or access-control handling. Training history can contain sensitive health and behavioral information, so silent persistence increases privacy risk, especially on shared systems or when the skill runs automatically.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.