T09 · Insecure Skill Coding Practices
- Location
scripts/create_log.py:7- Finding
Hard-Coded Hosted API Endpoint Can Disclose Tokens and Fitness Data
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md:10-12,scripts/create_log.py:7-20,scripts/view_logs.py:8-20
Vulnerability Type: Hard-coded external destination inconsistent with documented self-hosting support
Risk Level: MediumThe Skill documentation states that users may use either
https://wger.de/api/v2/or a self-hosted URL:markdown ## Setup (One-Time) - API Base: https://wger.de/api/v2/ (or self-hosted URL). - Auth: Token from wger dashboard (User > API). Set env WGER_TOKEN=your_key or pass in commands.However,
scripts/create_log.pyunconditionally transmits the configured token and workout data to the public wger endpoint:python BASE_URL = 'https://wger.de/api/v2/' TOKEN = os.getenv('WGER_TOKEN') if not TOKEN: print("Error: Set WGER_TOKEN env var") sys.exit(1) def create_log(date, workout_id, exercises): headers = {'Authorization': f'Token {TOKEN}', 'Content-Type': 'application/json'} data = { 'date': date, 'workout': workout_id, 'exercises': exercises # List of dicts: [{'reps': 10, 'weight': 135, 'exercise': exercise_id}] } response = requests.post(f'{BASE_URL}workoutlog/', json=data, headers=headers)scripts/view_logs.pyhas the same fixed destination:python BASE_URL = 'https://wger.de/api/v2/' TOKEN = os.getenv('WGER_TOKEN') # Set env WGER_TOKEN=your_key if not TOKEN: print("Error: Set WGER_TOKEN env var") sys.exit(1) def view_logs(limit=7, workout_id=None): headers = {'Authorization': f'Token {TOKEN}', 'Content-Type': 'application/json'} url = f'{BASE_URL}workoutlog/?limit={limit}&format=json' if workout_id: url += f'&workout={workout_id}' response = requests.get(url, headers=headers)Technical Analysis
External API access is required for the declared hosted-mode functionality, so the network behavior is not inherent ...[truncated 2123 chars]
- Remediation
View remediation
Remediation Suggestions
- Read the API destination from an explicit environment variable, such as
WGER_BASE_URL, rather than hard-coding the public service:python BASE_URL = os.environ.get("WGER_BASE_URL", "https://wger.de/api/v2/").rstrip("/") + "/" - Clearly document whether the public endpoint is the default. For privacy-sensitive operations, consider requiring the user to set the endpoint explicitly.
- Parse and validate the URL before attaching credentials. Allow only
httpsfor remote hosts; permit plain HTTP only for explicitly selected loopback development endpoints. - Display the destination hostname before the first authenticated request or require confirmation when transmitting fitness data to a non-loopback service.
- Keep credentials scoped to one endpoint and advise users not to reuse tokens between hosted and self-hosted installations.
- Add automated tests confirming that both scripts honor
WGER_BASE_URLand never silently fall back towger.dewhen a self-hosted endpoint is configured. - Set explicit request timeouts and use a shared, validated API client so all workflows apply the same destination and credential controls.
- Read the API destination from an explicit environment variable, such as
