T09 · Insecure Skill Coding Practices
- Location
scripts/send_daily_plan.py:21- Finding
Hard-Coded IMA API Credentials Exposed in Source Code
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
scripts/send_daily_plan.py:21Hard-Coded IMA API Credentials Exposed in Source Code
scripts/garmin_sync.py:29Garmin Account Password Stored in Plaintext and Accepted Through Command-Line Arguments
scripts/plan_engine.py:82Garmin Authentication and Health Data Use Predictable or Cross-Skill Storage Paths
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.
PYTHON = Path.home() / ".miniconda3/bin/python3"
DATA_DIR = Path(__file__).parent / "data"
CREDENTIALS_FILE = DATA_DIR / "credentials.json"
ACTIVITIES_FILE = DATA_DIR / "activities.json"
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.
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.
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() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.
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)
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.
方式A(推荐):写入文件
mkdir -p ~/.trainingpeaks
echo "你的cookie值" > ~/.trainingpeaks/cookie
chmod 600 ~/.trainingpeaks/cookie
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.
方式A(推荐):写入文件
mkdir -p ~/.trainingpeaks
echo "你的cookie值" > ~/.trainingpeaks/cookie
chmod 600 ~/.trainingpeaks/cookie
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
mkdir -p ~/.trainingpeaks
echo "你的cookie值" > ~/.trainingpeaks/cookie
chmod 600 ~/.trainingpeaks/cookie
方式B:设置环境变量
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
mkdir -p ~/.trainingpeaks
echo "你的cookie值" > ~/.trainingpeaks/cookie
chmod 600 ~/.trainingpeaks/cookie
方式B:设置环境变量
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.
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.
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.
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.
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.
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.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
"""同步 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 calls execute external commands. Without careful input validation, this enables command injection.
"""同步 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 calls execute external commands. Without careful input validation, this enables command injection.
"""获取体能状态"""
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 calls execute external commands. Without careful input validation, this enables command injection.
"""获取体能状态"""
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
)
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.
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.
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.
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.
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.
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.
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.
No suspicious patterns detected.