Back to skill

Security audit

Healthcheck Litiao

Security checks for vulnerabilities and agentic risk

Overview

This health tracker is not malicious, but it needs Review because it puts user-provided cup values into executable Node commands.

Install only if you are comfortable with a local JSON health log and you trust the agent to strictly substitute numeric cup counts. The skill should be hardened to pass values as arguments, validate positive numeric ranges, and ask before deleting records.

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

Error
Location
SKILL.md:24
Finding

JavaScript Code Injection Through Unvalidated Water Intake Input

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 24-27
Vulnerability Type: User-controlled input interpolated into executable JavaScript
Risk Level: High

Vulnerable Code

markdown
When user says "uống X cốc" or "uống nước X cốc":

```bash
node -e "const fs=require('fs');const f='{baseDir}/health-data.json';let d={water:[],sleep:[]};try{d=JSON.parse(fs.readFileSync(f))}catch(e){}d.water.push({time:new Date().toISOString(),cups:CUPS});fs.writeFileSync(f,JSON.stringify(d));console.log('Da ghi: '+CUPS+' coc')"

Replace CUPS with number from user input.

text

### Technical Analysis

The Skill directs the agent to replace the `CUPS` placeholder with a value extracted from user input. The value is embedded directly into source code supplied to `node -e`, without strict numeric parsing, range validation, escaping, or separation through process arguments.

If the agent accepts a crafted value rather than a valid numeric literal, the value could terminate or alter the intended JavaScript expression and inject additional JavaScript statements. Depending on shell metacharacters and quoting, unsafe interpolation may also affect the surrounding shell command.

The vulnerability violates the boundary between data and executable code: user-provided data becomes part of dynamically constructed JavaScript source.

### Attack Path

1. An attacker submits a water-tracking request containing a crafted value in place of the expected cup count.
2. The agent extracts that value and substitutes it directly for `CUPS`.
3. The resulting text becomes part of the `node -e` JavaScript program.
4. When the command is executed, the injected JavaScript runs with the same operating-system privileges as the agent process.
5. The injected code could access or modify files available to that process or invoke further local commands through Node.js APIs.

### Impact Assessment

Successful exploitation could provide arbitr
...[truncated 579 chars]
Remediation
View remediation

Remediation Suggestions

  • Never insert the user-provided cup count into JavaScript source code.
  • Parse the value using a fixed program and pass it as a separate process argument, for example through process.argv.
  • Convert the argument with Number() and reject it unless Number.isFinite() succeeds.
  • Enforce an appropriate numeric range, such as a positive value with a reasonable maximum.
  • Reject unexpected characters instead of attempting to escape executable input.
  • Pass {baseDir} as a separately validated argument rather than interpolating it into executable source.
  • Consider replacing the inline node -e command with a fixed, reviewed script that treats all external values strictly as data.
  • Write updates atomically and apply restrictive permissions to the health-data file where supported.

A safer fixed script should follow this pattern:

javascript
const cups = Number(process.argv[2]);
if (!Number.isFinite(cups) || cups <= 0 || cups > 100) {
  throw new Error('Invalid cup count');
}

data.water.push({
  time: new Date().toISOString(),
  cups
});

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:63
Finding

JavaScript Code Injection Through Unvalidated Record Update Value

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 63-66
Vulnerability Type: User-controlled update value interpolated into executable JavaScript
Risk Level: High

Vulnerable Code

markdown
To update last water entry:

```bash
node -e "const fs=require('fs');const f='{baseDir}/health-data.json';let d=JSON.parse(fs.readFileSync(f));d.water[d.water.length-1].cups=NEW_CUPS;fs.writeFileSync(f,JSON.stringify(d));console.log('Updated')"
text

### Technical Analysis

The update command places the `NEW_CUPS` placeholder directly inside JavaScript passed to `node -e`. The documented workflow provides no numeric conversion, validation, escaping, or argument boundary for the replacement value.

If a crafted update value is substituted for `NEW_CUPS`, it can alter the assignment expression and potentially append arbitrary JavaScript statements. Because Node.js exposes filesystem and process-execution APIs, successful injection is not limited to modifying the intended health record.

The command also assumes that the JSON file, `water` array, and final array element are present and valid. Those reliability issues can cause failures, but the security-critical issue is the placement of an externally supplied value into executable source.

### Attack Path

1. An attacker asks the agent to update the last water record and supplies a crafted replacement value.
2. The agent substitutes that value directly for `NEW_CUPS`.
3. The crafted value changes the structure of the JavaScript assignment or introduces additional statements.
4. The agent executes the generated `node -e` command.
5. The injected statements execute with the privileges of the agent process and can use Node.js filesystem or child-process functionality.

### Impact Assessment

Successful exploitation could lead to arbitrary local code execution under the current agent account. Potential effects include unauthorized file reads and writes, deleti
...[truncated 350 chars]
Remediation
View remediation

Remediation Suggestions

  • Replace the dynamically generated node -e source with a fixed script.
  • Supply the new cup count as a separate command-line argument or through another structured data channel.
  • Convert the value with Number() and require Number.isFinite(value).
  • Enforce a positive, reasonable numeric range before modifying the record.
  • Validate that parsed data is an object, that water is an array, and that at least one entry exists.
  • Do not rely on escaping user-controlled values for executable source; preserve a strict separation between code and data.
  • Use atomic file replacement to reduce the likelihood of partial writes or corruption.

A hardened update should use logic equivalent to:

javascript
const newCups = Number(process.argv[2]);
if (!Number.isFinite(newCups) || newCups <= 0 || newCups > 100) {
  throw new Error('Invalid cup count');
}
if (!Array.isArray(data.water) || data.water.length === 0) {
  throw new Error('No water record to update');
}

data.water[data.water.length - 1].cups = newCups;
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (4)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This markdown skill describes commands that create, update, and delete entries in {baseDir}/health-data.json, but it does not clearly warn users that invoking the skill will persist and alter personal health-related data on disk. Although the file path is documented later, there is no user-facing caution near the overview or command descriptions about local data storage and record modification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The activation instructions are written as fixed Vietnamese phrases like uống X cốc, đi ngủ, and thức dậy, but the document does not state that the skill is intentionally Vietnamese-only or provide an alternative language option. This creates a language-policy concern because the skill appears to require a specific language without user opt-in or justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The stats command is triggered by Vietnamese phrases only (thống kê, xem thống kê), continuing a pattern of locale-specific instructions without explaining that the skill is region- or language-specific. Under the stated policy, forcing a specific language without opt-in or documented justification should be flagged.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill documents a delete operation that immediately removes the last water record with no warning, confirmation, or recovery path. In a health-tracking context this can cause unintended loss of personal records, though the impact is limited to local integrity/availability of the user's own data.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.