T09 · Insecure Skill Coding Practices
- Location
SKILL.md:42- Finding
Shell Command Injection in Recommended Memory Indexing Integration
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 42–50
Vulnerability Type: Shell command injection through dynamically interpolated input
Risk Level: Hightypescript // Pseudocode example async function writeMemory(entry: MemoryEntry) { // 1. Write the memory file await writeFile(`memory/${entry.date}.md`, entry.content); // 2. Automatically add the index const tags = extractTags(entry.content); const subject = extractSubject(entry.content); await exec(`./scripts/auto-index-memory.sh memory/${entry.date}.md '${subject}' '${tags}'`); }Technical Analysis
The recommended integration constructs a shell command by directly interpolating
entry.date,subject, andtags. These values are derived from a memory entry or its content and must therefore be treated as untrusted.The single quotes around
subjectandtagsdo not prevent injection. An attacker can include a single quote to terminate the quoted argument and then append shell operators and commands. The interpolatedentry.datevalue is not quoted at all, making it directly susceptible to whitespace, command substitution, or shell metacharacters.Because
exec()invokes a shell, the resulting string is parsed as shell syntax rather than passed exclusively as literal arguments to the indexing script.Attack Path
- An attacker supplies or influences memory content, its extracted subject or tags, or the
entry.datefield. - The malicious value contains a quote or another shell metacharacter followed by an operating-system command.
extractSubject()orextractTags()preserves the malicious sequence, or the crafted date is used directly.- The application interpolates that value into the command passed to
exec(). - The shell interprets the injected sequence as executable syntax.
- The injected command runs with the operating-system privileges of the Agent or process executing the Skill ...[truncated 848 chars]
- An attacker supplies or influences memory content, its extracted subject or tags, or the
- Remediation
View remediation
Remediation Suggestions
-
Do not construct shell commands by concatenating or interpolating untrusted data.
-
Replace shell-based
exec()with an argument-vector API such asexecFile()orspawn()with shell processing disabled:typescript import { execFile } from "node:child_process"; import { promisify } from "node:util"; import path from "node:path"; const execFileAsync = promisify(execFile); const datePattern = /^\d{4}-\d{2}-\d{2}$/; if (!datePattern.test(entry.date)) { throw new Error("Invalid memory date"); } const memoryRoot = path.resolve("memory"); const filePath = path.resolve(memoryRoot, `${entry.date}.md`); if (!filePath.startsWith(`${memoryRoot}${path.sep}`)) { throw new Error("Memory path escapes the permitted directory"); } await execFileAsync( "./scripts/auto-index-memory.sh", [filePath, subject, tags], { shell: false } ); -
Strictly validate
entry.dateagainst the expected date format and ensure the resolved file remains inside the designatedmemory/directory. -
Apply explicit length and character constraints to extracted subjects and tags.
-
Treat memory content and all values derived from it as untrusted input.
-
Run the indexing process with least privilege and restrict its filesystem access to the files required for indexing.
-
Add security tests containing quotes, command substitutions, newlines, semicolons, pipes, and redirection operators to verify that all values remain literal arguments.
-
