Back to skill

Security audit

Nutrition Claw

Security checks for vulnerabilities and agentic risk

Overview

The skill is a local nutrition tracker, but it needs Review because it stores sensitive health data and has a verified bug that can read or overwrite JSON files outside its intended log folder.

Review this before installing on a shared machine or using it with untrusted agent-supplied dates. It stores personal nutrition and body-goal data locally in plain files under ~/.nutrition-claw, may create extra feedback/education history, installs npm dependencies without a reviewed lockfile, and should validate dates before it is trusted for automated use.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
src/lib/storage.ts:44
Finding

Path Traversal Through Unvalidated Date Values Permits JSON File Access Outside the Log Directory

Content
View full analysis
(logPath(date), []); } export function writeDayLog(date: string, log: DayLog): void { writeJson(logPath(date), log); } ``` The caller only verifies that the date and time values are present: ```ts function requireDate(args: Args): string { const d = args['date'] as string | undefined; if (!d) { err('--date YYYY-MM-DD is required'); process.exit(1); } return d; } function requireTime(args: Args): string { const t = args['time'] as string | undefined; if (!t) { err('--time HH:MM is required'); process.exit(1); } return t; } ``` A meal operation subsequently uses the unvalidated date: ```ts async function mealAdd(args: Args): Promise { const date = requireDate(args); const time = requireTime(args); const name = args['name'] as string | undefined; if (!name) { err('--name is required'); process.exit(1); } const id = nanoid(8); const meal: Meal = { id, time, name, ingredients: [] }; const log = readDayLog(date); log.push(meal); writeDayLog(date, log); await upsertVector(mealVecId(id), name, { type: 'meal', mealId: id, date }); print({ id }); } ``` ### Technical Analysis The command documentation specifies dates in `YYYY-MM-DD` format, but `requireDate()` only checks whether a value is present. It does not validate the date syntax, reject path separators, or verify that the resolved path remains under `LOGS_DIR`. `logPath()` concatenates the attacker-controlled value with `.json` and passes it to `path.join()`. A value containing `../` ...[truncated 2200 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/lib/storage.ts:17
Finding

Sensitive Health Records Are Created Without Explicit Restrictive Filesystem Permissions

Content
View full analysis
l.trim()).filter(Boolean) : []; // Remove existing entry for this food if present (re-insertion at bottom = most recent) const filtered = lines.filter(l => l !== foodName); filtered.push(foodName); // Keep only the last EDUCATION_MAX_LINES const trimmed = filtered.slice(-EDUCATION_MAX_LINES); writeFileSync(EDUCATION_FILE, trimmed.join('\n') + '\n'); } ``` ### Technical Analysis The application stores potentially sensitive personal information, including nutrition goals, body-derived targets, meal history, food habits, and semantic indexes. However, it relies entirely on the process umask when creating directories and files. On systems with a permissive umask, newly created files may be readable by group members or other local users. Existing files with overly broad modes also remain broad because overwriting a file does not automatically tighten its permissions. This is a local confidentiality weakness rather than remote code execution. Exploitability depends on the host's umask, filesystem permission model, and whether other users or processes can access t ...[truncated 1167 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
package.json:44
Finding

Unlocked Ranged Dependencies Make Installations Non-Reproducible and Increase Supply-Chain Exposure

Content
View full analysis
npm install ``` No package lockfile is present in the supplied project structure. ### Technical Analysis Caret ranges allow future compatible releases to be selected at installation time, while `"latest"` is explicitly mutable. Without a committed lockfile, separate installations can resolve different transitive dependency graphs from the code that was reviewed. This does not establish that any listed dependency is currently malicious or vulnerable. The weakness is that the reviewed source does not fully determine the code that will later be installed and executed. npm dependencies may include lifecycle scripts, native components, model-loading code, and substantial transitive dependency trees. The project also uses local embedding and transformer packages that may download model assets as part of runtime behavior. That model download is disclosed by the documentation, but its integrity should still be controlled. ### Attack Path 1. A direct or transitive dependency publishes a compromised release that satisfies an existing version range, or the mutable `"latest"` development dependency changes. 2. A user follows the documented `npm install` procedure. 3. npm resolves the newer package because no reviewed lockfile fixes the exact artifact graph. 4. Any dependency lifecycle script ...[truncated 902 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (26)

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 27)May include surrounding context.

Or configure npm once to always resolve the @pita scope from GitHub Packages:

bash
echo "@pita:registry=https://npm.pkg.github.com" >> ~/.npmrc
npm install -g @pita/nutrition-claw

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

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

./scripts/sync-skill.sh

text

This copies `SKILL.md` to `~/.openclaw/workspace/skills/nutrition/SKILL.md` so the openclaw agent always sees the latest version. **Do not skip this step — an outdated skill file will cause the agent to give wrong guidance.**

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

--date and --time are always required for meal add. Meal nutrition totals are computed from their ingredients — never stored separately.

bash
# Create a meal — returns the meal id
nutrition-claw meal add --name "Lunch" --date 2026-03-15 --time 13:00
# id: D-lfLPOP

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly instructs the agent to create and maintain a persistent file at ~/.nutrition-claw/feedback-diary.txt, but the user-facing description does not clearly disclose this extra write beyond core meal/food storage. Hidden or under-disclosed persistence is dangerous because it causes the agent to retain behavioral metadata across sessions, which can surprise users and expand the local privacy footprint.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The diary stores a persistent line-by-line history of dates, normalized food names, and topical interaction metadata that is not necessary for basic nutrition logging. This creates an additional behavioral profile of the user's eating inputs and agent responses, increasing privacy risk if the local machine is shared, compromised, or backed up to third-party services.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The workflow requires the agent to read, rewrite, purge, and append to a persistent diary on every ingredient add or update, normalizing user inputs and continuously mutating local state. Repeated autonomous file operations increase the chance of unintended data retention, corruption, or privacy leakage, especially because they occur as a side effect of ordinary nutrition actions rather than a clearly separated consented feature.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

In flag-based mode, the command persists data via writeGoals(goals) immediately and only prints the resulting object afterward. While interactive mode includes an explicit confirmation before saving, the non-interactive path lacks a comparable warning, prompt, or comment disclosing that user-provided health-related settings will be written to storage.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code adds, updates, and deletes meal data and ingredient records via writeDayLog, and also deletes/rebuilds vector entries, which are persistent state changes. While the command names imply CRUD behavior, there is no inline user-facing warning, confirmation prompt, or explanatory comment/docstring disclosing that these operations modify or remove stored data.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · src/index.ts (reported line 29)May include surrounding context.

ts
goals set [nutrients...]          Update individual goal values
  goals delete [--nutrient <key>]   Delete one or all goals

  meal add --name <n> --date <d> --time <t>   Create meal, returns id
  meal list --date <d>                         List meals with totals
  meal update <id> --name <n>                  Rename meal
  meal delete <id>                             Delete meal + ingredients

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code synchronously writes goals, food library, daily logs, and education history to files under the user's home directory, which affects user data persistence. While there are comments describing some functions, there is no confirmation prompt, user-facing log/print, or explicit disclosure here that personal nutrition-related data is being stored on disk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

This markdown file documents commands that append to ~/.npmrc and later explains that the tool stores nutrition goals, foods, and logs under ~/.nutrition-claw/. While these behaviors are described, the README does not present them as a user-facing warning about persistent changes to local configuration and storage of sensitive personal data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
72% confidence
Finding

These instructions require the assistant to always provide a specific style of response rather than offering the user a choice in how guidance is delivered. While not a spoken-language locale restriction, it is a natural-language policy concern because it forces a fixed interaction mode without opt-in or preference selection.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
90% confidence
Finding

Using a caret range for @inquirer/prompts allows newer minor/patch releases to be installed without explicit review, which weakens supply-chain integrity and reproducibility. In an agent skill package, that can change runtime behavior across environments and could expose consumers to a compromised upstream release.

Content

Scanner excerpt · package.json (reported line 49)May include surrounding context.

json
"clawhub:publish": "npm run build && ./scripts/sync-skill.sh && clawhub publish ~/.openclaw/workspace/skills/nutrition-claw --version $npm_package_version"
  },
  "dependencies": {
    "@inquirer/prompts": "^7.5.2",
    "@themaximalist/embeddings.js": "^0.1.3",
    "@xenova/transformers": "^2.17.2",
    "debug": "^4.4.3",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The dependency @themaximalist/embeddings.js is specified with a caret range, so installations may pull different package contents over time. That creates supply-chain risk and makes it harder to verify exactly what code executes in this local-first CLI skill.

Content

Scanner excerpt · package.json (reported line 50)May include surrounding context.

json
},
  "dependencies": {
    "@inquirer/prompts": "^7.5.2",
    "@themaximalist/embeddings.js": "^0.1.3",
    "@xenova/transformers": "^2.17.2",
    "debug": "^4.4.3",
    "nanoid": "^5.1.5",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The @xenova/transformers dependency is not pinned to a single exact version, allowing unreviewed upstream updates into builds. Because this package is involved in model-related functionality, unexpected updates could materially alter behavior or introduce vulnerable transitive code.

Content

Scanner excerpt · package.json (reported line 51)May include surrounding context.

json
"dependencies": {
    "@inquirer/prompts": "^7.5.2",
    "@themaximalist/embeddings.js": "^0.1.3",
    "@xenova/transformers": "^2.17.2",
    "debug": "^4.4.3",
    "nanoid": "^5.1.5",
    "vectra": "^0.12.3",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The debug package is declared with a caret range, so the actual installed version may vary and could include a compromised or vulnerable release. This is more concerning here because there are known advisories affecting some debug versions, but the manifest does not constrain resolution tightly enough to verify safety.

Content

Scanner excerpt · package.json (reported line 52)May include surrounding context.

json
"@inquirer/prompts": "^7.5.2",
    "@themaximalist/embeddings.js": "^0.1.3",
    "@xenova/transformers": "^2.17.2",
    "debug": "^4.4.3",
    "nanoid": "^5.1.5",
    "vectra": "^0.12.3",
    "yaml": "^2.7.1"

Unverifiable Dependency: debug has 4 known advisory(ies) (CVE-2025-59144 (debug@4.4.2 contains malware after npm account takeover); CVE-2017-20165 (debug Inefficient Regular Expression Complexity vulnerability); CVE-2017-16137 (Regular Expression Denial of Service in debug) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
87% confidence
Finding

The manifest does not pin debug to an exact version, and known advisories exist for some releases, including a reported malware-compromise event and ReDoS issues. Because the resolved version cannot be verified from this file alone, consumers may install an affected version depending on lockfile state and timing.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The nanoid dependency is specified with a caret range, which permits silent version drift and reduces assurance about which implementation is executed. Since nanoid often underpins identifier generation, vulnerable or behavior-changing releases can affect reliability and, in some cases, security properties.

Content

Scanner excerpt · package.json (reported line 53)May include surrounding context.

json
"@themaximalist/embeddings.js": "^0.1.3",
    "@xenova/transformers": "^2.17.2",
    "debug": "^4.4.3",
    "nanoid": "^5.1.5",
    "vectra": "^0.12.3",
    "yaml": "^2.7.1"
  },

Unverifiable Dependency: nanoid has 5 known advisory(ies) (CVE-2026-67214 (nanoid: non-secure generators can loop indefinitely with negative size); CVE-2026-67213 (nanoid: custom generators can loop indefinitely when size is zero); CVE-2024-55565 (Predictable results in nanoid generation when given non-integer values) +2 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding

The nanoid dependency is version-ranged and known advisories exist for some releases, so the package manifest alone cannot prove that installs are safe. If an affected version is resolved, identifier generation could become unreliable or predictable in edge cases, depending on how the library is used.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The vectra package is unpinned and may resolve to different versions over time, creating a supply-chain and reproducibility weakness. In a package used by agents, nondeterministic dependency resolution can make security review and incident response more difficult.

Content

Scanner excerpt · package.json (reported line 54)May include surrounding context.

json
"@xenova/transformers": "^2.17.2",
    "debug": "^4.4.3",
    "nanoid": "^5.1.5",
    "vectra": "^0.12.3",
    "yaml": "^2.7.1"
  },
  "devDependencies": {

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The yaml dependency is not pinned exactly, so installations may pull in versions with different security properties. This matters because YAML parsers are historically high-risk components, and known advisories exist for some yaml releases, making unverifiable version drift a real concern.

Content

Scanner excerpt · package.json (reported line 55)May include surrounding context.

json
"debug": "^4.4.3",
    "nanoid": "^5.1.5",
    "vectra": "^0.12.3",
    "yaml": "^2.7.1"
  },
  "devDependencies": {
    "@types/bun": "latest",

Unverifiable Dependency: yaml has 2 known advisory(ies) (CVE-2026-33532 (yaml is vulnerable to Stack Overflow via deeply nested YAML collections); CVE-2023-2251 (Uncaught Exception in yaml)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

Known vulnerabilities affect some yaml releases, but the dependency is not pinned exactly, so the actual security posture cannot be determined from the manifest. Since YAML parsing libraries can be exposed to denial-of-service or parser-crash issues, unresolved version ambiguity is a meaningful supply-chain risk.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

The devDependency uses the floating tag "latest", which makes builds non-reproducible and can silently introduce malicious or breaking upstream releases. Even though this is a development-only package, compromised developer environments or CI pipelines can still be affected during install or build.

Content

Scanner excerpt · package.json (reported line 58)May include surrounding context.

json
"yaml": "^2.7.1"
  },
  "devDependencies": {
    "@types/bun": "latest",
    "typescript": "^5.8.2"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
88% confidence
Finding

The TypeScript devDependency uses a caret range, which allows uncontrolled minor/patch updates in developer and CI environments. While lower risk than a runtime dependency, this can still affect build integrity, reproducibility, and exposure to toolchain compromise.

Content

Scanner excerpt · package.json (reported line 59)May include surrounding context.

json
},
  "devDependencies": {
    "@types/bun": "latest",
    "typescript": "^5.8.2"
  }
}

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The inline documentation states a clear intent limitation: "Food library is for packaged/labelled products only." However, the implementation only requires a name and optional nutrient fields, with no validation or checks that the item is packaged or label-derived. This is an intent-code divergence because the comment asserts a narrower scope than the code actually enforces.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.