Back to skill

Security audit

Mealie Recipe Manager

Security checks for vulnerabilities and agentic risk

Overview

This Mealie skill is coherent, but it needs review because it can delete Mealie data and may send its API token over unencrypted HTTP.

Review before installing. Use only an HTTPS Mealie URL, create a dedicated least-privilege Mealie API token, store the .env file with restrictive permissions, and be careful with delete commands because the script executes them immediately without confirmation.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mealie.js:74
Finding
Bearer API Token Can Be Transmitted over Unencrypted HTTP<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mealie.js`, lines 74–96 **Vulnerability Type**: Plaintext transmission of authentication credentials **Risk Level**: Medium ### Vulnerable Code ```js // Parse URL const urlObj = new URL(MEALIE_URL); const isHttps = urlObj.protocol === 'https:'; const httpModule = isHttps ? require('https') : require('http'); // API request helper async function api(method, endpoint, body = null) { return new Promise((resolve, reject) => { const path = endpoint.startsWith('/api') ? endpoint : `/api${endpoint}`; const options = { hostname: urlObj.hostname, port: urlObj.port || (isHttps ? 443 : 80), path: path, method: method, headers: { 'Authorization': `Bearer ${API_TOKEN}`, 'Content-Type': 'application/json' } }; ``` ### Technical Analysis The client selects the plaintext Node.js HTTP module whenever `MEALIE_URL` does not use the `https:` protocol. It nevertheless includes `MEALIE_API_TOKEN` as a bearer credential in every API request. Bearer tokens provide access based solely on possession. When sent over HTTP, neither the authorization header nor the request and response bodies receive transport encryption or server authentication. An attacker able to observe or manipulate traffic between the client and the Mealie server can therefore capture the token, inspect private Mealie data, modify traffic, or impersonate the server. The access to `~/.openclaw/.env` and the skill-level `.env` is consistent with the declared authentication functionality and parsing is limited to `MEALIE_URL` and `MEALIE_API_TOKEN`. The security issue is not the documented credential-file access itself, but the possibility of transmitting the loaded secret over an insecure transport. ### Attack Path 1. The user or deployment configures `MEALIE_URL` with an `http://` endpoint. 2. The Skill reads `MEALIE_API_TOKEN` from the process environment, the skill-level `.env`, or ...[truncated 1401 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce HTTPS before creating any request: ```js const urlObj = new URL(MEALIE_URL); if (urlObj.protocol !== 'https:') { throw new Error('MEALIE_URL must use HTTPS'); } const httpModule = require('https'); ``` 2. If plaintext HTTP is required for local development, make it an explicit opt-in rather than the default fallback. Restrict that exception to verified loopback addresses such as `127.0.0.1`, `::1`, or `localhost`, and display a prominent warning. 3. Reject unsupported URL schemes instead of treating every non-HTTPS scheme as HTTP. 4. Configure the Mealie deployment with a valid TLS certificate and verify certificates using Node.js defaults. Do not disable TLS certificate validation. 5. Use a dedicated, least-privilege Mealie API token for this Skill. Limit its permissions to the operations actually required by the user. 6. Revoke and replace any token that may previously have been transmitted over HTTP. 7. Store the skill-level `.env` with restrictive filesystem permissions and prefer it over a shared agent-level environment file where practical. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Credential Access

High
Category
Privilege Escalation
Content
return;
  }
  
  // Try skill-level .env first
  const skillEnvPath = path.join(__dirname, '..', '.env');
  if (fs.existsSync(skillEnvPath)) {
    const content = fs.readFileSync(skillEnvPath, 'utf8');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return;
  }
  
  // Try skill-level .env first
  const skillEnvPath = path.join(__dirname, '..', '.env');
  if (fs.existsSync(skillEnvPath)) {
    const content = fs.readFileSync(skillEnvPath, 'utf8');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
return;
  }
  
  // Try skill-level .env first
  const skillEnvPath = path.join(__dirname, '..', '.env');
  if (fs.existsSync(skillEnvPath)) {
    const content = fs.readFileSync(skillEnvPath, 'utf8');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
}
  
  // Try skill-level .env first
  const skillEnvPath = path.join(__dirname, '..', '.env');
  if (fs.existsSync(skillEnvPath)) {
    const content = fs.readFileSync(skillEnvPath, 'utf8');
    content.split('\n').forEach(line => {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
}
  
  // Try skill-level .env first
  const skillEnvPath = path.join(__dirname, '..', '.env');
  if (fs.existsSync(skillEnvPath)) {
    const content = fs.readFileSync(skillEnvPath, 'utf8');
    content.split('\n').forEach(line => {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
}
  
  // Try skill-level .env first
  const skillEnvPath = path.join(__dirname, '..', '.env');
  if (fs.existsSync(skillEnvPath)) {
    const content = fs.readFileSync(skillEnvPath, 'utf8');
    content.split('\n').forEach(line => {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Session Persistence

Medium
Category
Rogue Agent
Content
## Environment Variables

Set these in your agent's `.env` (`~/.openclaw/.env`) or create a skill-level `.env` at `~/.openclaw/skills/mealie/.env`:

- `MEALIE_URL` — Your Mealie instance URL (e.g., `https://recipes.example.com`)
- `MEALIE_API_TOKEN` — Your API token (create at `/user/profile/api-tokens` in Mealie)
Confidence
77% confidence
Finding
The skill instructs users to store a long-lived API token in global or skill-level `.env` files under the user’s home directory, creating persistent local credential storage. If the host is shared, backups are exposed, filesystem permissions are weak, or another tool/process can read those files, the token could be stolen and used to access or modify the Mealie instance.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill advertises destructive and state-changing operations such as deleting recipes, deleting items, and modifying meal plans without any warning, confirmation guidance, or caution about irreversible changes. In an agent setting, this increases the risk of accidental data loss or unintended modification of a user’s Mealie instance, especially because the skill uses an authenticated API token with write access.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The delete-recipe command performs an irreversible destructive action immediately from CLI input without any confirmation prompt, dry-run mode, or safety flag. In an agent context, accidental invocation, prompt confusion, or misuse could delete user data with little friction.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The delete-item command removes shopping-list items immediately based only on supplied identifiers and provides no confirmation or warning. In an automation or agent-driven workflow, this increases the chance of unintended data loss from mistakes or manipulated inputs.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest describes this skill as interacting with Mealie for recipes, shopping lists, and meal plans. The code also exposes commands for household statistics, tags, and categories, which are additional Mealie capabilities not reflected in that stated scope. These are not necessary implementation details for the described functions and broaden the skill’s behavior beyond what the manifest claims.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The code explicitly formats dates using toLocaleDateString('en-US'), which imposes a specific locale regardless of the user's preferences or environment. This is a natural-language/locale policy issue because the skill does not offer a locale choice or document why US English formatting is required.

Static analysis

No suspicious patterns detected.