Back to skill

Security audit

garmin-ultimate-frisbee-analysis

Security checks for vulnerabilities and agentic risk

Overview

The skill appears purpose-aligned, but it handles Garmin credentials and sensitive health/location data with several unsafe defaults that users should review before installing.

Review before installing. Use a virtual environment, pin dependencies, avoid putting your Garmin password in shell profiles or command-line arguments, protect or delete generated HTML/JSON/FIT/GPX files, and treat dashboards as containing sensitive health and location data. Prefer offline/bundled chart assets or integrity-checked scripts before viewing private reports.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/frisbee_chart.py:297
Finding
Unescaped Garmin and User-Controlled Values Permit Script Injection in Generated Dashboards<![CDATA[ ## Vulnerability Details **File Location**: `scripts/frisbee_chart.py:297-315, 331-357, 462-469`; `scripts/frisbee_compare.py:370-394, 439-455` **Vulnerability Type**: Persistent/local HTML and JavaScript injection **Risk Level**: High ### Vulnerable Code ```python def _build_activity_table_html(activities): if not activities: return "<p style='text-align:center;opacity:0.6'>No activities found in this date range.</p>" rows = "" for i, a in enumerate(activities, 1): dur = int((a.get("duration_seconds") or 0) // 60) hrr_count = len(a.get("hrr", [])) hrr_note = f"✓ {hrr_count} pts" if hrr_count else "—" rows += f""" <tr> <td>Game {i}</td> <td>{a.get('date', '—')}</td> <td>{a.get('activity_name', '—')}</td> <td>{a.get('activity_type', '—')}</td> <td>{dur} min</td> <td>{a.get('avg_hr') or '—'}</td> <td>{a.get('max_hr') or '—'}</td> <td>{a.get('calories') or '—'}</td> <td>{hrr_note}</td> </tr>""" ``` ```python def generate_tournament_html(data): name = data.get("name", "Tournament") start = data.get("start_date", "") end = data.get("end_date", "") title = f"{name} | {start} → {end}" ... charts_json = json.dumps({"stats": stats, "charts": charts}) return f"""<!DOCTYPE html> <html lang="en"> <head> ... <title>{title}</title> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> ... <body> <div class="container"> <h1>🥏 {name}</h1> <div class="subtitle">{start} → {end} &nbsp;·&nbsp; Generated {generated}</div> ... {activity_table} ``` The comparison dashboard contains the same class of issue: ```python table_rows = "" for a in all_relevant: table_rows += f"""<tr> <td>{a['date']}</td> <td>{a['name'][:20]}</td> <td>{a.get('category','—')}</td> <td>{a['dur ...[truncated 2149 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply `html.escape(value, quote=True)` to every value inserted into HTML markup, including activity names, activity types, dates, tournament names, and titles. 2. Do not place ordinary `json.dumps()` output directly in an executable script block. 3. Store serialized data in a `<script type="application/json">` element and read it using `textContent`, while escaping `<`, `>`, `&`, U+2028, and U+2029. 4. Alternatively, serialize data to a separate local JSON file and parse it as data rather than executable source. 5. Add a restrictive Content Security Policy that disallows inline event handlers and limits outbound connections. 6. Add regression tests using values containing `<script>`, `</script>`, `<img onerror=...>`, quotes, ampersands, and Unicode line separators. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unbounded Dependency Versions Create a Supply-Chain Execution Risk<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-3` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```text garminconnect>=0.2.19 fitparse>=3.2.0 gpxpy>=1.3.5 ``` The documentation incorrectly describes these constraints as pinned: ```markdown # Install dependencies (versions pinned in requirements.txt) pip3 install -r requirements.txt ``` ### Technical Analysis The `>=` operator permits pip to install any future release of each package. Consequently, installations performed after the Skill was reviewed may execute dependency code that was not part of the audited artifact. Python package installation can run package build logic, and the installed packages subsequently execute in the same process as the Skill. In particular, `garminconnect` operates in a process that handles the user's Garmin password, reusable session tokens, and sensitive health data. The absence of package hashes also means the installer does not cryptographically verify that it received the exact reviewed distributions. ### Attack Path 1. A dependency account, package index, release process, or future package version is compromised. 2. The attacker publishes a malicious version satisfying the broad `>=` constraint. 3. The user installs or reinstalls the Skill. 4. Pip resolves the malicious release and executes its installation logic or imports it during normal Skill operation. 5. The dependency accesses the process environment, Garmin credentials, stored session tokens, health information, and files available to the installing user. ### Impact Assessment A malicious dependency would execute native Python code with the privileges of the user running installation or the Skill. It could steal Garmin credentials and session tokens, access health and GPS data, alter generated results, or modify any files writable by that user. If installation is run with elevated privileges, the impact could extend to the bro ...[truncated 18 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace minimum-version ranges with exact, reviewed versions using `==`. 2. Generate a lock file that includes all transitive dependencies. 3. Require package hashes by using `pip install --require-hashes -r requirements.txt`. 4. Build and test upgrades through a controlled dependency-review process. 5. Correct the documentation so its claim of pinned versions matches the implementation. 6. Consider installing from a trusted internal package mirror or verified wheel repository. ]]>

T08 · Insecure Dependencies

Warning
Location
install.sh:18
Finding
Installer Bypasses Python Environment Protections and May Modify Shared Packages<![CDATA[ ## Vulnerability Details **File Location**: `install.sh:18-30` **Vulnerability Type**: Unsafe dependency installation and shared-environment modification **Risk Level**: Medium ### Vulnerable Code ```bash # Install Python dependencies echo echo "📦 Installing Python dependencies..." if pip3 install --user -r requirements.txt 2>/dev/null; then echo "✓ Dependencies installed (--user)" elif pip3 install --break-system-packages -r requirements.txt 2>/dev/null; then echo "✓ Dependencies installed (--break-system-packages)" elif pip3 install -r requirements.txt 2>/dev/null; then echo "✓ Dependencies installed (system-wide)" else echo "❌ Failed to install Python dependencies" echo " Try manually: pip3 install --user -r requirements.txt" exit 1 fi ``` ### Technical Analysis If the per-user installation fails, the script automatically invokes pip with `--break-system-packages`. This option deliberately bypasses protections for externally managed Python installations. It then makes another installation attempt without either user isolation or a virtual environment. These fallbacks can overwrite or conflict with packages used by unrelated applications. Redirecting standard error to `/dev/null` also conceals the reason for fallback execution and suppresses security-relevant warnings. This behavior is not necessary for Garmin analytics; an isolated virtual environment provides all required functionality without changing a shared interpreter. ### Attack Path 1. The user runs `install.sh`. 2. The initial `--user` installation fails because of environment policy, dependency conflicts, or local configuration. 3. Without obtaining explicit confirmation, the script retries with `--break-system-packages`. 4. Pip modifies an externally managed or shared Python environment if the current account has sufficient permission. 5. Updated or conflicting packages affect other Python applications, or a compromised dependency gains access to the br ...[truncated 404 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a dedicated virtual environment, for example with `python3 -m venv .venv`. 2. Install dependencies only through `.venv/bin/python -m pip`. 3. Remove the `--break-system-packages` and unscoped system-wide fallbacks. 4. Stop suppressing pip error output; preserve warnings and failure diagnostics. 5. Require explicit user action before changing any environment outside the project. 6. Combine environment isolation with exact dependency versions and verified hashes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/garmin_auth.py:108
Finding
Credential Guidance and CLI Options Expose the Garmin Password<![CDATA[ ## Vulnerability Details **File Location**: `scripts/garmin_auth.py:108-138`; `SKILL.md:42-46`; `install.sh:38-45` **Vulnerability Type**: Plaintext credential exposure **Risk Level**: Medium ### Vulnerable Code ```python # Login command login_parser = subparsers.add_parser("login", help="Login to Garmin Connect") login_parser.add_argument("--email", help="Garmin account email (or set GARMIN_EMAIL env var)") login_parser.add_argument("--password", help="Garmin account password (or set GARMIN_PASSWORD env var)") args = parser.parse_args() if args.command == "login": email = args.email password = args.password # Priority: CLI args > environment variables if not email or not password: email = email or os.getenv("GARMIN_EMAIL") password = password or os.getenv("GARMIN_PASSWORD") if not email or not password: print("❌ Email and password required", file=sys.stderr) print("Set via:", file=sys.stderr) print(" 1. CLI: --email and --password", file=sys.stderr) print(" 2. Env vars: GARMIN_EMAIL and GARMIN_PASSWORD", file=sys.stderr) sys.exit(1) ``` The setup instructions recommend persistent plaintext storage: ```bash export GARMIN_EMAIL="your-email@example.com" export GARMIN_PASSWORD="your-password" ``` The installer similarly instructs users to add the values to `~/.zshrc` or `~/.bashrc`. ### Technical Analysis Supplying a password through `--password` places it in the process argument vector. Depending on operating-system policy, process arguments may be visible through process inspection, diagnostics, audit logs, crash reports, or monitoring systems. The command can also remain in shell history. Persisting `GARMIN_PASSWORD` in a shell startup file leaves a reusable account password in plaintext on disk. Such files are commonly included in workstation backups, support bundles, dotfile repositories, and accidental disclosures. Exporting it globally also makes the secret av ...[truncated 1078 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--password` argument. 2. Prompt interactively using `getpass.getpass()` so the password is not echoed or stored in shell history. 3. Support an operating-system credential store such as Keychain, Secret Service, or an appropriate Python keyring backend. 4. Do not recommend adding account passwords to shell startup files. 5. If environment-variable input must remain available for automation, recommend short-lived injection scoped only to the authentication command. 6. Clear password references as soon as authentication completes and avoid propagating the secret to child processes. 7. Update README and Skill documentation to explain the residual risks of each credential input method. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/garmin_activity_files.py:31
Finding
Sensitive Activity Files Are Written to Predictable Shared Temporary Paths<![CDATA[ ## Vulnerability Details **File Location**: `scripts/garmin_activity_files.py:31-49` **Vulnerability Type**: Unsafe temporary-file creation and sensitive-data persistence **Risk Level**: Medium ### Vulnerable Code ```python def download_activity_file(client, activity_id, file_format="fit", output_dir="/tmp"): """Download activity FIT or GPX file.""" try: output_path = f"{output_dir}/activity_{activity_id}.{file_format.lower()}" if file_format.lower() == "fit": data = client.download_activity(activity_id, dl_fmt=client.ActivityDownloadFormat.ORIGINAL) elif file_format.lower() == "gpx": data = client.download_activity(activity_id, dl_fmt=client.ActivityDownloadFormat.GPX) elif file_format.lower() == "tcx": data = client.download_activity(activity_id, dl_fmt=client.ActivityDownloadFormat.TCX) else: return {"error": f"Unsupported format: {file_format}"} with open(output_path, 'wb') as f: f.write(data) return {"file": output_path, "activity_id": activity_id, "format": file_format} except Exception as e: return {"error": str(e), "activity_id": activity_id} ``` ### Technical Analysis The default output directory is the globally shared `/tmp` directory, and the filename is deterministically derived from the activity ID. The file is created using ordinary `open(..., "wb")`, which follows symbolic links and truncates an existing target. Permissions depend on the process umask rather than being explicitly restricted. FIT, GPX, and TCX files may contain precise geographic routes, timestamps, heart-rate measurements, cadence, elevation, and other health or performance data. The function does not remove the downloaded file after analysis, so sensitive information can remain in `/tmp` beyond the Skill run. ### Attack Path 1. A local attacker predicts or learns the Garmin activity ID. 2. The attacker creates `/tmp/activity_ ...[truncated 775 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use `tempfile.TemporaryDirectory()` to create a unique private directory for each operation. 2. Set the directory mode to `0700` and activity-file mode to `0600`. 3. Create files atomically with exclusive creation and no symbolic-link following, such as `O_CREAT | O_EXCL | O_NOFOLLOW` where supported. 4. Delete temporary activity files immediately after parsing by using `try/finally` or a context manager. 5. When the user explicitly requests persistent output, validate the destination directory and refuse symbolic-link targets. 6. Avoid predictable filenames in shared directories. ]]>

T03 · Remote Payload Retrieval and Execution

Warning
Location
scripts/frisbee_chart.py:837
Finding
Generated Health Dashboards Execute Remotely Hosted JavaScript Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/frisbee_chart.py:837-838`; also `scripts/frisbee_chart.py:357`, `scripts/frisbee_compare.py:393-394`, and `scripts/garmin_chart.py:29` **Vulnerability Type**: Remote payload retrieval in sensitive local dashboards **Risk Level**: Medium ### Vulnerable Code ```html <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@3.0.1/dist/chartjs-plugin-annotation.min.js"></script> ``` The general health dashboard uses the same pattern: ```html <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> ``` ### Technical Analysis Each generated dashboard retrieves and executes JavaScript from jsDelivr when opened. Although package versions are specified in the URL, the files are not protected by Subresource Integrity hashes. The effective executable dashboard payload therefore depends on remote CDN content that is outside the audited project. The external script executes in a page containing sensitive health and activity data. It can read the document, inspect embedded chart payloads, alter the displayed analysis, and initiate network communication. This external JavaScript is needed only for chart rendering, not for Garmin authentication or data retrieval. Bundling a reviewed local copy would provide the same declared functionality without runtime remote-code dependency. ### Attack Path 1. The npm package, package-owner account, CDN infrastructure, DNS path, or delivery process is compromised. 2. Modified JavaScript is served at one of the referenced URLs. 3. The user opens a generated Garmin dashboard while connected to the network. 4. The browser downloads and executes the modified code in the dashboard. 5. The code reads embedded heart-rate, HRV, sleep, Body Battery, activity, or tournament information. 6. It manipulates the analysis or attempts to transmit the info ...[truncated 407 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bundle reviewed Chart.js and plugin files inside the Skill package and load them from local paths. 2. If remote loading is unavoidable, add verified `integrity` attributes and `crossorigin="anonymous"` to every external script. 3. Recalculate and review integrity hashes only during controlled dependency upgrades. 4. Add a restrictive Content Security Policy limiting `script-src` and `connect-src`. 5. Prefer dashboards that function fully offline so viewing private health data does not require contact with a third-party CDN. 6. Document all runtime network destinations and explain that remote scripts execute with access to dashboard data. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (20)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**Session tokens**
- Stored in `~/.clawdbot/garmin/` with directory permissions `700` (owner read/write/execute only)
- Managed by the `garth` library (part of `garminconnect`); format is an opaque token bundle, not plaintext password
- To revoke: `rm -rf ~/.clawdbot/garmin/` — next login will re-authenticate

**Network**
- Credentials are used only to authenticate with Garmin Connect and are not transmitted to any third-party service
Confidence
90% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**Session tokens**
- Stored in `~/.clawdbot/garmin/` with directory permissions `700` (owner read/write/execute only)
- Managed by the `garth` library (part of `garminconnect`); format is an opaque token bundle, not plaintext password
- To revoke: `rm -rf ~/.clawdbot/garmin/` — next login will re-authenticate

**Network**
- Credentials are used only to authenticate with Garmin Connect and are not transmitted to any third-party service
Confidence
85% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**Session tokens**
- Stored in `~/.clawdbot/garmin/` with directory permissions `700` (owner read/write/execute only)
- Managed by the `garth` library (part of `garminconnect`); format is an opaque token bundle, not plaintext password
- To revoke: `rm -rf ~/.clawdbot/garmin/` — next login will re-authenticate

**Network**
- Credentials are used only to authenticate with Garmin Connect and are not transmitted to any third-party service
Confidence
90% 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).

Credential Access

High
Category
Privilege Escalation
Content
# Install and setup
npm install
pip3 install garminconnect fitparse gpxpy
cp .env.example .env
# Edit .env with your credentials

# Authenticate
Confidence
84% confidence
Finding
The documentation tells users to copy .env.example to .env and place credentials there, which normalizes storing secrets in a local plaintext environment file without any accompanying protections or caveats. While common in development workflows, this can expose credentials through accidental source control commits, backups, shared machines, or weak filesystem hygiene.

Credential Access

High
Category
Privilege Escalation
Content
npm install
pip3 install garminconnect fitparse gpxpy
cp .env.example .env
# Edit .env with your credentials

# Authenticate
npm run auth
Confidence
80% confidence
Finding
The instruction to edit the .env file with credentials reinforces direct placement of secrets into a local file, again without warning about exposure risks or secure alternatives. In context, this is a legitimate installation guide, but the absence of security guidance makes credential mishandling more likely.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The document provides concrete instructions for saving and restoring OAuth tokens and logging in with email/password, but it does not warn that these credentials and tokens are sensitive secrets that must be stored securely and never hardcoded, logged, or shared. In a skill/reference context, users may copy these examples directly into insecure scripts or agent memory, increasing the risk of account compromise and unauthorized access to health and activity data.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This documentation encourages collection and analysis of highly sensitive health and location-derived activity data, including heart rate, HRV, sleep, GPS, and tournament timelines, without any privacy notice, consent guidance, retention guidance, or warning about downstream exposure. In an agent skill context, normalizing these operations without safeguards increases the risk that users or downstream tools will export, share, or persist sensitive biometric data in ways they do not fully understand.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The examples explicitly write HTML dashboards containing activity and health analytics to ~/Desktop without warning that sensitive data will be stored in a local file. That can expose private information to other local users, backups, sync services, screenshots, or accidental sharing, especially because the dashboards may include identifiable fitness, recovery, and timeline data.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The setup instructions direct users to create a .env file with credentials and run an authentication flow, but they do not include any warning about safeguarding secrets, avoiding accidental commits, or the risks of local token storage. In a setup guide for a tool that handles account authentication, omission of basic credential-handling guidance can lead to credential exposure or insecure storage practices by users.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This code makes API calls to retrieve sensitive biometric and activity information, including HRV and heart rate, which are privacy-relevant data types. While the script logs progress, it does not disclose to the user that it will access and analyze health data, and the module docstring/CLI help also omit that warning.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script writes a detailed HTML report containing personal fitness and health metrics to disk and automatically opens it in a browser, but does not clearly warn the user that a persistent local artifact is being created. On shared systems or synced home directories, this can unintentionally expose sensitive data to other local users, backup systems, cloud sync providers, or browser history/recent-files mechanisms.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This script aggregates multiple categories of sensitive health data (sleep, HRV, body battery, intraday heart rate) and outputs them as JSON or an HTML dashboard without any explicit privacy warning, consent confirmation, or output protection. In the context of a tournament report, this creates a real risk of unintentionally exposing detailed personal health information through saved files, browser display, or later sharing.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The script explicitly supports passing the Garmin password via a `--password` command-line argument, which can expose secrets through shell history, process listings, audit logs, and job runners. In this context, the script is an authentication helper that handles real account credentials and stores session tokens, so encouraging CLI password entry materially increases the risk of credential disclosure.

Intent-Code Divergence

Medium
Confidence
92% confidence
Finding
The module docstring states that the script fetches health data, but the implementation also exposes profile PII via the `profile` metric, including name and email. This mismatch can mislead users or downstream agents about the scope of collected data, increasing the risk of unintended disclosure of sensitive personal information.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script retrieves sensitive health telemetry and profile information, then prints it directly to stdout as formatted JSON. In agent or tool-execution environments, stdout is commonly logged, captured, or forwarded to other components, so this creates a straightforward privacy exposure path for health data and PII without any warning, consent check, or output minimization.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This script retrieves highly sensitive health and biometric data from a Garmin account and prints it directly to stdout without any privacy notice, consent check, masking, or output minimization. In agent or automation contexts, stdout is often captured by logs, pipelines, chat transcripts, or other downstream systems, which can unintentionally expose protected personal health information.

Unpinned Dependencies

Low
Category
Supply Chain
Content
garminconnect>=0.2.19
fitparse>=3.2.0
gpxpy>=1.3.5
Confidence
94% confidence
Finding
The dependency is specified with only a lower bound, which allows future unreviewed versions to be installed. This weakens build reproducibility and can expose the skill to supply-chain risk or accidental adoption of a vulnerable or breaking release.

Unverifiable Dependency: garminconnect has 2 known advisory(ies) (CVE-2026-54447 (garminconnect Has Insecure Permission Assignment for Garmin OAuth Token Store); CVE-2026-54447 (garminconnect Has Insecure Permission Assignment for Garmin OAuth Token Store)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
89% confidence
Finding
The manifest references garminconnect without pinning a version even though advisories exist for that package, so there is no way to verify whether deployment will install an affected release. Given the cited issue involves insecure permission assignment for an OAuth token store, exploitation could expose or improperly permit access to authentication tokens if a vulnerable version is resolved.

Unpinned Dependencies

Low
Category
Supply Chain
Content
garminconnect>=0.2.19
fitparse>=3.2.0
gpxpy>=1.3.5
Confidence
94% confidence
Finding
The dependency is not pinned to an exact version, so installations may resolve to different releases over time. That increases supply-chain exposure and makes it harder to verify which code is actually running.

Unpinned Dependencies

Low
Category
Supply Chain
Content
garminconnect>=0.2.19
fitparse>=3.2.0
gpxpy>=1.3.5
Confidence
94% confidence
Finding
Using an unpinned package version permits uncontrolled upgrades during installation. In practice this can introduce vulnerable, malicious, or incompatible upstream releases without any change to this repository.

Static analysis

No suspicious patterns detected.