CVE-2026-54447 Overview
CVE-2026-54447 affects garminconnect, a Python 3 API wrapper for Garmin Connect used to retrieve statistics and manage activities. Versions prior to 0.3.5 create the OAuth token directory and garmin_tokens.json without explicit owner-only permissions. A permissive umask such as 022 leaves the directory at mode 0755 and the token file at mode 0644. On shared Linux or macOS hosts, any unprivileged local user can read the file and extract di_refresh_token, gaining persistent access to the victim's Garmin Connect account. The issue is classified as [CWE-732] Incorrect Permission Assignment for Critical Resource and is fixed in version 0.3.5.
Critical Impact
A local unprivileged user on a shared host can steal di_refresh_token from garmin_tokens.json and obtain persistent access to the victim's Garmin Connect account, including health, fitness, activity, and device data.
Affected Products
- garminconnect Python package versions prior to 0.3.5
- The Garmin.login tokenstore path (garminconnect/client.pyClient.dump)
- Pre-existing garmin_tokens.json files on Linux and macOS hosts using permissive umasks
Discovery Timeline
- 2026-09-14 - CVE-2026-54447 published to the National Vulnerability Database (NVD)
- 2026-09-14 - Last updated in NVD database
Technical Details for CVE-2026-54447
Vulnerability Analysis
The garminconnect library persists OAuth tokens to disk to support session reuse across script invocations. The Client.dump function in garminconnect/client.py writes the token directory and garmin_tokens.json file using default filesystem creation semantics. When the process runs under a common umask of 022, the directory is created with mode 0755 and the JSON file with mode 0644, making both world-readable.
The stored file contains di_refresh_token, a long-lived credential that authorizes access to the Garmin Connect account. Any other local user on the host can read the file, extract the refresh token, and use it to obtain access tokens outside the victim's session. Because the credential is a refresh token, access persists until the token is revoked or rotated.
Root Cause
The root cause is missing explicit permission enforcement when writing sensitive credential material to disk [CWE-732]. Client.dump relies on the process umask rather than calling os.chmod or opening files with restrictive modes such as 0o600. Systems configured with common defaults produce group- and world-readable artifacts. Additionally, any token file created before upgrading to 0.3.5 retains its original loose permissions until it is rewritten or manually restricted.
Attack Vector
Exploitation requires local access with low privileges and no user interaction. An attacker with a shell account on the same Linux or macOS host enumerates predictable token store locations, reads garmin_tokens.json, and exfiltrates di_refresh_token. The stolen token is then used from any network location to authenticate to Garmin Connect and access health, fitness, activity, and device data associated with the victim's account.
# Patch excerpt: garminconnect/client.py
import http.cookiejar
import json
import logging
+import os
import random
import re
import time
Source: GitHub commit 8256b57
The patch imports os to enable explicit permission enforcement on the token directory and file, ensuring owner-only access regardless of the process umask.
Detection Methods for CVE-2026-54447
Indicators of Compromise
- Presence of garmin_tokens.json with mode 0644 or broader on shared multi-user hosts
- Token store parent directory created with mode 0755 and readable by non-owner users
- Unexpected Garmin Connect API sessions originating from IP addresses not associated with the account owner
- Access logs showing non-owner users reading files under the OAuth token directory
Detection Strategies
- Audit filesystem permissions on all garmin_tokens.json files using find with -perm predicates to identify world- or group-readable instances.
- Inventory installed garminconnect package versions across Python environments and flag any release earlier than 0.3.5.
- Correlate Garmin Connect account access logs against known user devices and geolocations to identify refresh-token misuse.
Monitoring Recommendations
- Enable Linux audit rules (auditd) to log open and read syscalls against known token store paths by non-owner UIDs.
- Monitor macOS Unified Log for filesystem access to user home directory token stores by other local accounts.
- Track Python dependency manifests in CI to alert on garminconnect < 0.3.5 being installed or pinned.
How to Mitigate CVE-2026-54447
Immediate Actions Required
- Upgrade garminconnect to version 0.3.5 or later in every Python environment that uses the library.
- Rotate Garmin Connect credentials and revoke existing sessions to invalidate any di_refresh_token values that may have been exposed.
- Manually restrict permissions on existing token stores by running chmod 700 on the directory and chmod 600 on garmin_tokens.json.
- Remove garmin_tokens.json from any shared or world-readable location and regenerate it under the upgraded library.
Patch Information
The fix is available in garminconnectrelease 0.3.5. The upstream commit 8256b57 writes the token store with owner-only permissions and adds a test_token_permissions regression test. Full advisory details are published as GHSA-wjhr-76vg-2hvc.
Workarounds
- Set a restrictive umask such as umask 077 before invoking scripts that call Client.dump so that newly created token files inherit owner-only modes.
- Store token files inside a dedicated directory with mode 0700 owned exclusively by the runtime user.
- Avoid running garminconnect on shared multi-user hosts; prefer isolated per-user systems or containerized environments.
# Restrict permissions on existing token store
chmod 700 ~/.garminconnect
chmod 600 ~/.garminconnect/garmin_tokens.json
# Enforce restrictive umask before running scripts that use Client.dump
umask 077
python3 my_garmin_script.py
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

