Back to skill

Security audit

Oura Health

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its Oura health-data purpose, but it can send the user's Oura bearer token to an arbitrary configured API URL while claiming it only uses the official Oura API.

Review before installing. Only use this skill if you are comfortable giving it access to sensitive Oura health and profile data. Keep the credentials file private, rotate the token if exposed, and remove or hard-code the base_url to https://api.ouraring.com/v2 before use, especially before running the alert checker from cron or another heartbeat system.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/oura_api.py:19
Finding

Configurable API origin can expose the Oura bearer token

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/health_alerts.py:15
Finding

Health alert checker sends the bearer token to an unrestricted configured origin

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (25)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 7)May include surrounding context.

md
## Setup

### 1. Get a Personal Access Token

1. Go to [cloud.ouraring.com](https://cloud.ouraring.com/personal-access-tokens)
2. Sign in with your Oura account

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 11)May include surrounding context.

md
## Setup

### 1. Get a Personal Access Token

1. Go to [cloud.ouraring.com](https://cloud.ouraring.com/personal-access-tokens)
2. Sign in with your Oura account

Credential Access

High
Category
Privilege Escalation
Confidence
82% confidence
Finding

The README instructs the user to store a long-lived personal access token in a local plaintext credentials.json file. Even with 600 permissions, plaintext bearer tokens on disk can be stolen by local malware, backup leakage, shell-history mistakes, or accidental file disclosure, granting access to sensitive health data.

Content

Scanner excerpt · README.md (reported line 18)May include surrounding context.

bash
mkdir -p ~/.config/oura
cat > ~/.config/oura/credentials.json << 'EOF'
{
  "personal_access_token": "YOUR_TOKEN_HERE",
  "base_url": "https://api.ouraring.com/v2"

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 82)May include surrounding context.

md
# Oura Ring Skill

Query Oura Ring health data via the Oura API v2. Requires a personal access token at `~/.config/oura/credentials.json`.

## Setup

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 86)May include surrounding context.

md
# Oura Ring Skill

Query Oura Ring health data via the Oura API v2. Requires a personal access token at `~/.config/oura/credentials.json`.

## Setup

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
# Oura Ring Skill

Query Oura Ring health data via the Oura API v2. Requires a personal access token at `~/.config/oura/credentials.json`.

## Setup

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/oura_api.py (reported line 55)May include surrounding context.

python
# Oura Ring Skill

Query Oura Ring health data via the Oura API v2. Requires a personal access token at `~/.config/oura/credentials.json`.

## Setup

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 24)May include surrounding context.

md
import urllib.parse
from datetime import datetime, timedelta

CREDS_PATH = os.path.expanduser("~/.config/oura/credentials.json")


def load_credentials():

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
import urllib.parse
from datetime import datetime, timedelta

CREDS_PATH = os.path.expanduser("~/.config/oura/credentials.json")


def load_credentials():

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
import urllib.parse
from datetime import datetime, timedelta

CREDS_PATH = os.path.expanduser("~/.config/oura/credentials.json")


def load_credentials():

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/health_alerts.py (reported line 12)May include surrounding context.

python
import urllib.parse
from datetime import datetime, timedelta

CREDS_PATH = os.path.expanduser("~/.config/oura/credentials.json")


def load_credentials():

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/oura_api.py (reported line 14)May include surrounding context.

python
import urllib.parse
from datetime import datetime, timedelta

CREDS_PATH = os.path.expanduser("~/.config/oura/credentials.json")


def load_credentials():

Session Persistence

Medium
Category
Rogue Agent
Confidence
74% confidence
Finding

The setup encourages creation of a persistent personal access token for ongoing use. Long-lived session material increases the blast radius of local compromise because an attacker who obtains the token may access historical and current health data until the token is revoked or rotated.

Content

Scanner excerpt · README.md (reported line 11)May include surrounding context.

md
1. Go to [cloud.ouraring.com](https://cloud.ouraring.com/personal-access-tokens)
2. Sign in with your Oura account
3. Click **Create New Personal Access Token**
4. Copy the token

### 2. Save Credentials

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 · README.md (reported line 24)May include surrounding context.

"base_url": "https://api.ouraring.com/v2" } EOF chmod 600 ~/.config/oura/credentials.json

text

### 3. Verify

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill documents network access to the Oura API but does not declare any explicit tool scope or permissions boundary. This creates a transparency and governance gap: users and the platform cannot easily see that the skill will transmit data off-device, which is especially important for health data.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The trigger list includes broad phrases such as 'heart rate', 'readiness', 'oura', and 'health briefing', which can match normal conversation and invoke the skill unexpectedly. Because the skill accesses sensitive health information, unintended invocation can expose private data or cause queries to be sent without clear user intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The description explains capabilities but does not prominently warn that the skill accesses highly sensitive personal health and profile data, including age, email, biological sex, sleep, HRV, and readiness metrics. Without a clear warning and consent model, users may disclose or retrieve protected personal information without understanding the privacy implications.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script reads a personal access token from a credentials file under the user's home directory, which is sensitive credential access. Although the code comment explains silent exit behavior when credentials are missing, it does not disclose to the user that credentials will be read and used for authenticated API access.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · README.md (reported line 21)May include surrounding context.

md
with open(CREDS_PATH) as f:
            creds = json.load(f)
        token = creds.get("personal_access_token")
        base = creds.get("base_url", "https://api.ouraring.com/v2").rstrip("/")
        if not token:
            return None, None
        return token, base

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
with open(CREDS_PATH) as f:
            creds = json.load(f)
        token = creds.get("personal_access_token")
        base = creds.get("base_url", "https://api.ouraring.com/v2").rstrip("/")
        if not token:
            return None, None
        return token, base

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/health_alerts.py (reported line 20)May include surrounding context.

python
with open(CREDS_PATH) as f:
            creds = json.load(f)
        token = creds.get("personal_access_token")
        base = creds.get("base_url", "https://api.ouraring.com/v2").rstrip("/")
        if not token:
            return None, None
        return token, base

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code sends authenticated requests to the Oura API using a personal access token and retrieves sensitive health data, but the operation has no confirmation prompt, no logging/print disclosure, and no inline warning beyond a brief module docstring. For a code file handling privacy-sensitive network activity, the absence of any visible user disclosure matches the missing-warning criteria.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The client allows the API base URL to be overridden from a local credentials file, then attaches the bearer token to every request. If that file is modified to point to an attacker-controlled host, the script will transmit the personal access token and potentially health-query metadata to that host, enabling credential theft and unauthorized access to the user's Oura data.

Content

Scanner excerpt · scripts/oura_api.py (reported line 24)May include surrounding context.

python
with open(CREDS_PATH) as f:
            creds = json.load(f)
        token = creds.get("personal_access_token")
        base = creds.get("base_url", "https://api.ouraring.com/v2").rstrip("/")
        if not token:
            die("No personal_access_token in credentials file")
        return token, base

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The example credentials format instructs users to store a configurable base_url alongside the personal access token, reinforcing the redirectable endpoint design. In the context of a health-data skill, this increases the chance that sensitive bearer tokens and downstream requests are sent to a non-Oura server if the config is tampered with or misconfigured.

Content

Scanner excerpt · scripts/oura_api.py (reported line 30)May include surrounding context.

python
return token, base
    except FileNotFoundError:
        die(f"Credentials file not found: {CREDS_PATH}\n"
            f"Create it with: {{\"personal_access_token\": \"...\", \"base_url\": \"https://api.ouraring.com/v2\"}}")
    except json.JSONDecodeError:
        die(f"Invalid JSON in {CREDS_PATH}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script performs authenticated HTTP requests to the Oura API and retrieves personal and health-related information, including email, age, sex, sleep, heart rate, and readiness data. While network access is central to the tool's purpose, the file lacks any explicit user-facing warning, comment, or help text disclosing that sensitive personal data will be sent to and fetched from a third-party service.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.