T08 · Insecure Dependencies
- Location
templates/hooks/subagent-stop.js:67- Finding
Lifecycle Hook May Download and Execute Unpinned npm Packages
- Content
View full analysis
l.includes('error TS')).slice(0, 10).join('\n'); return lines ? `**tsc:**\n\`\`\`\n${lines}\n\`\`\`` : null; } function runEslint(files) { const r = tryCmd('npx', ['eslint', ...files], { timeout: 60000, cwd: PROJECT_ROOT }); if (r.ok || !r.output.trim()) return null; return `**eslint:**\n\`\`\`\n${truncate(r.output, 500)}\n\`\`\``; } ``` ```javascript function runVitest(files) { if (files.length === 0) return null; const r = tryCmd('npx', ['vitest', 'run', ...files], { timeout: 90000, cwd: PROJECT_ROOT }); if (r.ok) return null; if (r.output.includes('FAIL') || r.output.includes('Error')) { return `**vitest:**\n\`\`\`\n${truncate(r.output, 800)}\n\`\`\``; } return null; } ``` ### Technical Analysis The `SubagentStop` lifecycle hook automatically invokes `npx` for TypeScript, ESLint, and Vitest checks. It does not use `--no-install`, verify that the executable comes from the repository's locked dependencies, or validate the resolved package and version. When the expected executable is not locally installed, `npx` can attempt package resolution through the configured npm registry. This creates a supply-chain execution channel inside an automatic lifecycle hook. The short command name `tsc` is especially ambiguous because package resolution may not necessarily select the intended TypeScript compiler unless TypeScript is already installed and locked locally. Any ...[truncated 1226 chars]- Remediation
View remediation
