T09 · Insecure Skill Coding Practices
- Location
SKILL.md:68- Finding
User-Controlled ENS Name Injection in Node.js Fallback Command
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 68–80
Vulnerability Type: Command and JavaScript code injection through unsafe input interpolation
Risk Level: HighVulnerable Code
bash node --input-type=module -e " import { createPublicClient, http } from 'viem'; import { mainnet } from 'viem/chains'; import { normalize } from 'viem/ens'; const c = createPublicClient({ chain: mainnet, transport: http('https://eth.llamarpc.com') }); const addr = await c.getEnsAddress({ name: normalize('REPLACE_NAME') }); console.log(JSON.stringify({ address: addr })); "The subsequent instruction states:
text Replace `REPLACE_NAME` with the actual ENS name.Technical Analysis
The skill instructs the Agent to replace
REPLACE_NAMEdirectly with an ENS name supplied through user input. The value is embedded inside a JavaScript string literal contained in a shell command, but the instructions do not require strict ENS validation, escaping, or separation of code from data.A crafted input containing quote characters and JavaScript syntax could terminate the
normalize('...')string and inject statements into the program passed tonode -e. Depending on how the Agent performs substitution and executes the resulting command, shell metacharacters or command substitutions could also be interpreted by the surrounding shell.The skill's name-detection rule is broad—any token matching
*.ethis considered likely to be an ENS name—and does not establish a restrictive validation boundary before the fallback command is generated. Althoughnormalize()validates its argument at runtime, it cannot protect against injection that alters the JavaScript source beforenormalize()is called.Attack Path
- An attacker submits a crafted value that appears to end in
.ethbut contains quote characters and executable JavaScript or shell syntax. - The Agent classifies the value as an ENS n ...[truncated 1415 chars]
- An attacker submits a crafted value that appears to end in
- Remediation
View remediation
Remediation Suggestions
-
Do not interpolate user input into executable source code. Keep the JavaScript program static and pass the ENS name as a separate process argument. Read it through
process.argvinside the program. -
Avoid shell execution. Invoke Node.js through an argument-array API such as
spawnorexecFilewithshell: false. This prevents shell metacharacters and command substitutions from being evaluated. -
Validate before execution. Apply ENS-compatible normalization and reject invalid input before performing resolution. Enforce a reasonable maximum length and reject control characters, quotes, newlines, shell metacharacters, and malformed labels. Validation should support legitimate internationalized ENS names through the canonical ENS normalization implementation rather than an improvised ASCII-only expression.
-
Use a fixed script or safe argument pattern. For example, the static program can read the name from an argument:
javascript import { createPublicClient, http } from 'viem'; import { mainnet } from 'viem/chains'; import { normalize } from 'viem/ens'; const rawName = process.argv[2]; if (!rawName) throw new Error('ENS name is required'); const name = normalize(rawName); const client = createPublicClient({ chain: mainnet, transport: http('https://eth.llamarpc.com'), }); const address = await client.getEnsAddress({ name }); console.log(JSON.stringify({ address }));Invoke it with the ENS name as a distinct argument and without a shell.
-
Fail closed. If normalization or validation fails, report that the ENS name is invalid and do not attempt to repair, guess, or execute it.
-
Reduce execution privileges. Run the resolution process in a restricted environment without wallet secrets, unnecessary filesystem access, or broad network permissions.
-
