T09 · Insecure Skill Coding Practices
- Location
SKILL.md:25- Finding
JavaScript Code Injection Through Unvalidated Water Intake Value
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, line 25
Vulnerability Type: User-controlled JavaScript code injection
Risk Level: HighVulnerable Code
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')"The Skill instructs the agent to replace
CUPSwith a number extracted from user input. The value is inserted directly into JavaScript source passed tonode -e. The instructions do not require strict numeric parsing, range validation, safe quoting, or passing the value as a command-line argument.Technical Analysis
Because
CUPSappears as executable JavaScript rather than as a data value, verbatim or insufficiently validated substitution can alter the syntax and behavior of the generated program. A malicious input that is accepted as the replacement value could close or extend the intended expression and introduce arbitrary JavaScript.This is source-code injection rather than ordinary JSON manipulation. Once the constructed command is invoked, Node.js evaluates the injected content with the same operating-system identity, environment, filesystem access, and process-launching capabilities available to the agent.
Exploitation depends on the invoking agent substituting attacker-controlled content instead of enforcing a strict numeric grammar. The Skill itself supplies no safeguard that would prevent this behavior.
Attack Path
- An attacker submits a water-record request whose purported cup count contains JavaScript syntax rather than a plain numeric value.
- The agent follows the instruction to replace
CUPSand inserts the attacker-controlled content into thenode -esource. - The agent invokes the generated shell command.
- Node.js evaluates the injecte ...[truncated 593 chars]
- Remediation
View remediation
Remediation Suggestions
Do not construct executable JavaScript by replacing tokens with user input.
- Pass the cup count as a separate process argument or through a controlled environment variable.
- Parse it inside the fixed program with
Number()or a similarly strict conversion. - Require the complete input to match an explicitly permitted numeric format.
- Reject non-finite, negative, fractional, or unreasonably large values according to the application's requirements.
- Keep the JavaScript source constant so user input is handled only as data.
- Pass
baseDiras a separate data argument and resolve the path against an approved storage directory rather than embedding it in source code. - Consider using a dedicated script file and a structured invocation API that does not involve a shell.
A safe implementation should validate the argument before modifying data, for example:
js const cups = Number(process.argv[1]); if (!Number.isInteger(cups) || cups < 0 || cups > 100) { throw new Error('Invalid cup count'); }The invoking process should provide the value as an argument without interpolating it into the JavaScript program.
