T09 · Insecure Skill Coding Practices
Error
- Location
- src/runtime/chunked-render.mjs:120
- Finding
- Untrusted Composition Files Execute with Full Node.js Process Privileges<![CDATA[ ## Vulnerability Details **File Location**: `src/runtime/chunked-render.mjs:120-142` **Vulnerability Type**: Arbitrary JavaScript execution without isolation **Risk Level**: High ### Vulnerable Code ```javascript let patternCode = readFileSync(input, 'utf8').replace(/^\/\/ @\w+.*/gm, '').trim(); patternCode = stripVizMethods(patternCode); let pattern; try { const lines = patternCode.split('\n'); let lastExprStart = -1; let depth = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (!line || line.startsWith('//')) continue; if (depth === 0 && /^(stack|note|s|n|seq|cat|sequence|arrange|slowcat|fastcat)\s*\(/.test(line)) { lastExprStart = i; } for (const ch of line) { if (ch === '(') depth++; if (ch === ')') depth--; } } if (lastExprStart >= 0) { const setup = lines.slice(0, lastExprStart).join('\n'); const expr = lines.slice(lastExprStart).join('\n'); const fn = new Function(setup + '\nreturn ' + expr); pattern = fn(); } else { try { pattern = new Function(patternCode)(); } catch { pattern = new Function('return ' + patternCode)(); } } } ``` ### Technical Analysis The renderer reads an arbitrary JavaScript composition and evaluates it through the `Function` constructor in the main renderer process. `new Function()` is not a sandbox. Evaluated code can access globally available Node.js objects and APIs and executes with the same operating-system identity and privileges as the renderer. The renderer does not apply the partial environment and `child_process` restrictions found in `offline-render-v2.mjs`. The visualization-method stripping routine is a compatibility transformation, not a security control, and does not prevent compositions from reading files, modifying files, accessing environment variables, or making network requests. The documentation warns users to trust or review compositions, but this warning does not create an enforceable privilege boundary. ...[truncated 1316 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Execute every composition in a separate, disposable sandbox rather than in the primary renderer process. 2. Use a container or equivalent operating-system isolation with: - No inherited secrets or credentials. - Network access disabled by default. - A read-only root filesystem. - A narrowly scoped writable output directory. - Explicit CPU, memory, process, and execution-time limits. - A non-privileged user and no additional Linux capabilities. 3. Replace unrestricted JavaScript compositions with a declarative composition format or a validated abstract syntax tree containing only approved Strudel operations. 4. If JavaScript support must remain, statically reject imports, dynamic imports, `Function`, `eval`, filesystem APIs, process APIs, network APIs, and constructor-based escapes. Static filtering should supplement, not replace, process isolation. 5. Do not pass the parent process environment into the renderer. 6. Treat AI-generated and externally supplied compositions as untrusted by default. 7. Make the safer isolated renderer the default entry point and add automated tests proving that compositions cannot read secrets, write outside the output directory, spawn processes, or access the network. ]]>
