Back to skill

Security audit

Code Runner

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real code runner, but it executes supplied snippets on the host with unsafe invocation guidance and no enforced isolation.

Install or use this only in a disposable sandbox, container, or VM with no sensitive files, credentials, or network access unless you explicitly need them. Do not let it run code from untrusted users automatically, and avoid the documented echo interpolation pattern; feed code through a safe stdin mechanism such as a quoted heredoc or an argument-array API. Prefer pinned, local or container-local language tools over global installs.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:72
Finding
Shell Injection Through the Recommended Standard-Input Invocation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:72-75` **Vulnerability Type**: Shell command injection through unsafe code interpolation **Risk Level**: High ### Vulnerable Code ```bash **Recommended Method (stdin):** ```bash echo "<code>" | node scripts/run-code.cjs <languageId> ``` ``` ### Technical Analysis The recommended command places potentially untrusted source code inside a double-quoted shell argument. Double quotes do not prevent shell evaluation of command substitutions, variable expansion, or backticks. For example, source text containing `$(command)` is evaluated by the caller's shell before `echo` sends data to the runner. Consequently, malicious shell syntax can execute outside `run-code.cjs`. This bypasses the runner's timeout, output handling, temporary-file cleanup, and language-selection logic. The documentation's assertion that this method avoids escaping issues is therefore unsafe. ### Attack Path 1. An attacker supplies a code snippet containing shell substitution syntax such as `$(malicious-command)`. 2. An AI Agent follows the documented invocation and interpolates the snippet into `echo "<code>"`. 3. The Agent's shell evaluates the substitution before starting or feeding the code runner. 4. The substituted command executes directly with the Agent process's operating-system privileges. 5. Only the resulting text is passed to `run-code.cjs`, potentially concealing the prior shell-side execution. ### Impact Assessment Successful exploitation permits arbitrary command execution as the account running the Agent. The attacker can access any files, environment variables, credentials, network resources, and processes available to that account. Because execution occurs outside the runner, its timeout and cleanup behavior do not constrain the injected command. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Do not interpolate source code into an `echo` command or any shell command string. - Invoke the Node.js process through an argument-array API such as `spawn()` with `shell: false`, and write the exact source bytes to the child process's stdin. - If a shell example is unavoidable, use a strongly quoted heredoc with a fixed delimiter: ```bash node scripts/run-code.cjs javascript <<'CODE_RUNNER_EOF' console.log("Example"); CODE_RUNNER_EOF ``` - Clearly state that dynamically inserting untrusted content into shell command text is prohibited. - Add regression tests containing `$()`, backticks, dollar signs, backslashes, quotes, and multiline content to verify that input reaches the runner unchanged. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/run-code.cjs:134
Finding
Untrusted Code Executes Without Mandatory Isolation or Least-Privilege Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run-code.cjs:134-140, 173-226`; related warning without enforcement at `SKILL.md:151-161` **Vulnerability Type**: Unrestricted arbitrary code execution with ambient Agent privileges **Risk Level**: High ### Vulnerable Code ```js function executeCommand(command, timeout = DEFAULT_TIMEOUT) { return new Promise((resolve, reject) => { const startTime = Date.now(); const child = exec(command, { timeout: timeout, maxBuffer: 10 * 1024 * 1024, // 10MB buffer encoding: 'utf8' }, (error, stdout, stderr) => { ``` ```js async function runCode(languageId, code, options = {}) { const lang = languageId.toLowerCase(); const config = languageConfig[lang]; if (!config) { const supported = Object.keys(languageConfig).join(', '); throw new Error(`Unsupported language: ${languageId}\n\nSupported languages: ${supported}`); } const timeout = options.timeout || DEFAULT_TIMEOUT; let tempFile = null; let outputFile = null; try { // Handle compiled languages if (config.compile) { const tmpDir = os.tmpdir(); // Special handling for Java if (lang === 'java') { const className = extractJavaClassName(code); tempFile = createTempFile(code, config.ext, className); const dir = path.dirname(tempFile); // Compile const compileCmd = config.compileCmd(tempFile, null, dir); await executeCommand(compileCmd, timeout); // Run const runCmd = config.runCmd(null, dir, className); const result = await executeCommand(runCmd, timeout); // Cleanup class file cleanupFiles(path.join(dir, `${className}.class`)); ...[truncated 2802 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Execute every untrusted snippet in a mandatory ephemeral sandbox or container rather than directly on the host. - Run the sandbox as a dedicated unprivileged user with no access to the Agent's home directory, credentials, sockets, or project files unless explicitly required. - Mount only a fresh working directory, make the root filesystem read-only, and use `noexec`, `nosuid`, and `nodev` mount options where appropriate. - Disable network access by default and expose it only through an explicit, narrowly scoped policy. - Clear inherited environment variables and provide a minimal allowlisted environment. - Apply CPU, memory, process-count, file-size, open-file, and wall-clock limits. - Use syscall filtering and platform isolation controls such as seccomp, AppArmor, SELinux, namespaces, or equivalent mechanisms. - Terminate the entire sandbox or process group on timeout, not only the immediate shell process. - Require explicit user authorization before enabling sensitive capabilities such as network access or host filesystem mounts. - Use `spawn()` or `execFile()` with `shell: false` for host-side process construction, even when the runtime commands are internally configured. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run-code.cjs:111
Finding
Predictable Files in a Shared Temporary Directory Enable Symlink and Race Attacks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run-code.cjs:111-118, 193-194` **Vulnerability Type**: Unsafe temporary-file creation **Risk Level**: Medium ### Vulnerable Code ```js function createTempFile(code, ext, customName = null) { const tmpDir = os.tmpdir(); const fileName = customName || `code_runner_${Date.now()}`; const filePath = path.join(tmpDir, `${fileName}.${ext}`); fs.writeFileSync(filePath, code, 'utf8'); return filePath; } ``` ```js tempFile = createTempFile(code, config.ext); outputFile = path.join(tmpDir, `code_runner_${Date.now}${process.platform === 'win32' ? '.exe' : ''}`); ``` Java also supplies an extracted class name as the custom temporary filename: ```js const className = extractJavaClassName(code); tempFile = createTempFile(code, config.ext, className); ``` ### Technical Analysis Temporary source and executable paths are created directly under the shared operating-system temporary directory. Default names are derived from `Date.now()`, which is predictable, while Java source filenames are derived from an attacker-visible class name. `fs.writeFileSync()` is called without exclusive creation or explicit symlink protection. An attacker with access to the same temporary directory may predict or race the filename and pre-create a symbolic link. The write operation can then follow that link and overwrite another file writable by the runner. Compiled output paths are similarly predictable and are passed to a compiler without first creating a private working directory. This can support artifact substitution or race conditions between compilation, execution, and cleanup. ### Attack Path 1. A local attacker monitors runner activity or predicts the timestamp-based filename. 2. The attacker creates a symbolic link or conflicting file at the anticipated path under the shared temporary directory. 3. The runner calls `fs.writeFileSync()` on that path or instructs a compiler to generate the predictabl ...[truncated 743 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create a unique private directory for every run using `fs.mkdtempSync()` with a fixed prefix. - Set restrictive directory permissions, such as mode `0700`, and place all source, compiler output, class files, and auxiliary artifacts inside that directory. - Generate filenames with cryptographically secure random values rather than timestamps or user-derived class names. - Use exclusive file creation, such as the `wx` flag, and fail if the target already exists. - Validate filesystem objects with `lstat()` and reject symbolic links where platform APIs permit. - For Java, keep the required class filename inside the newly created private directory rather than directly under the shared temporary root. - Remove the complete private directory recursively after execution instead of deleting a small set of individually predicted paths. - Ensure cleanup failures are logged securely rather than silently ignored. ]]>

T08 · Insecure Dependencies

Warning
Location
references/LANGUAGES.md:13
Finding
Documentation Recommends Unpinned Global Third-Party Installations<![CDATA[ ## Vulnerability Details **File Location**: `references/LANGUAGES.md:13-16, 244-247, 256-259` **Vulnerability Type**: Unpinned global dependency installation **Risk Level**: Medium ### Vulnerable Code ```markdown ### TypeScript - **Executor**: `ts-node` - **Extension**: `.ts` - **Install**: `npm install -g ts-node typescript` ``` ```markdown ### C# Script - **Executor**: `dotnet script` - **Extension**: `.csx` - **Install**: `dotnet tool install -g dotnet-script` ``` ```markdown ### CoffeeScript - **Executor**: `coffee` - **Extension**: `.coffee` - **Install**: `npm install -g coffeescript` ``` ### Technical Analysis The installation commands resolve mutable current releases from external package registries and install them globally. No reviewed versions, lockfile, integrity hashes, trusted registry restrictions, or provenance verification are specified. Global installation expands the impact of a compromised package because its executable remains available to future sessions and other projects. Package installation can also execute lifecycle or installation logic with the privileges of the user following the instructions. This finding concerns unsafe supply-chain guidance. The audited project itself does not contain evidence that these named packages are malicious. ### Attack Path 1. A user follows the installation instructions to enable a supported language. 2. The package manager resolves the latest available package and transitive dependencies from its configured registry. 3. A compromised upstream release, registry account, dependency, or substituted registry response is downloaded. 4. Installation-time code may execute with the user's privileges. 5. The globally installed executable is subsequently invoked by `run-code.cjs`, allowing the compromised component to affect code-runner sessions and potentially other projects. ### Impact Assessment A compromised dependency can execute code as the installing user during installation or lat ...[truncated 217 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin every recommended dependency to a reviewed exact version. - Prefer project-local or container-local installations over global installations. - Supply lockfiles and verify registry-provided integrity metadata. - Use trusted registry configuration and package provenance or signature verification where available. - Run installations in an isolated, unprivileged build environment with limited network and filesystem access. - Regularly scan pinned dependencies and their transitive dependency graphs for known vulnerabilities. - Document a controlled upgrade and review process rather than implicitly selecting the newest release. - For runtime images, preinstall and verify required tools during a reproducible image-build process. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (12)

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
echo "<code>" | node scripts/run-code.cjs <languageId>
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Vague Triggers

Medium
Confidence
88% confidence
Finding
The skill description and usage guidance are broad enough to trigger on many generic requests about testing or checking code behavior, which can cause over-invocation of a high-risk capability: arbitrary code execution. In this context, overly permissive matching is more dangerous than usual because the skill enables direct execution across many languages and the instructions include ready-to-use command lines for running untrusted code.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This skill is explicitly designed to execute arbitrary user-supplied code across many languages by writing it to disk and invoking interpreters/compilers with child_process. In an agent skill context, the absence of any safety gate, sandboxing, or explicit warning means the skill can be used to run destructive host commands, access local files, make network calls, or abuse available credentials, so the risk is real rather than merely informational.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/run-code.cjs:140