T01 · Skill Instruction Hijacking
Error
- Location
- package.json:8
- Finding
- Automatic Installation of a Mutable Remote Skill Manifest<![CDATA[ ## Vulnerability Details **File Location**: `package.json:8`; `bin/install.js:8-9, 14-18, 60-77, 126-131, 143-204` **Vulnerability Type**: Remote instruction retrieval and persistent Skill installation **Risk Level**: Critical ### Vulnerable Code ```json "scripts": { "postinstall": "node bin/install.js" } ``` ```js const RAW_BASE = 'https://raw.githubusercontent.com/aawp-ai/aawp/main/skills/aawp'; const FALLBACK = 'https://aawp.ai/skill'; function validateSkillMd(content, sourceUrl) { // Must be a valid SKILL.md with expected markers if (!content.startsWith('---')) throw new Error('Downloaded content is not a valid SKILL.md (missing YAML frontmatter)'); if (!content.includes('name: aawp')) throw new Error('Downloaded SKILL.md does not match expected skill identity'); if (!content.includes('aawp.ai')) throw new Error('Downloaded SKILL.md failed content integrity check'); return true; } async function downloadSkillMd() { const primaryUrl = `${RAW_BASE}/SKILL.md`; const fallbackUrl = `${FALLBACK}/SKILL.md`; let content, sourceUrl; try { content = await fetchText(primaryUrl); sourceUrl = primaryUrl; } catch { content = await fetchText(fallbackUrl); sourceUrl = fallbackUrl; } info(`Source: ${dim(sourceUrl)}`); validateSkillMd(content, sourceUrl); return content; } function installToDir(baseDir, skillMd) { const dest = path.join(baseDir, SKILL_NAME); fs.mkdirSync(dest, { recursive: true }); fs.writeFileSync(path.join(dest, 'SKILL.md'), skillMd, 'utf8'); return dest; } ``` ### Technical Analysis The npm `postinstall` lifecycle hook automatically runs the installer. Rather than installing the reviewed, bundled `SKILL.md`, the installer downloads a new manifest from either the mutable GitHub `main` branch or an independently controlled website. The downloaded manifest is authenticated only by checking for three ordinary text markers. These checks do not establish integrity or publisher authent ...[truncated 2287 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove all network retrieval from the npm `postinstall` lifecycle. 2. Install the reviewed `SKILL.md` bundled inside the published package. 3. Do not modify AI-client directories automatically during dependency installation; require an explicit installer command and informed user confirmation. 4. If remote manifest retrieval is unavoidable: - Pin an immutable release commit or content-addressed artifact; - Embed the expected SHA-256 digest in the npm package; - Verify a digital signature against a public key bundled through a separate trust channel; - Reject redirects and unexpected origins; - Fail closed when verification cannot be completed. 5. Display the destination and verified artifact identity before writing any file. 6. Provide a dry-run mode and request confirmation separately for every client destination. 7. Treat changes to the manifest as new reviewed releases rather than silently serving them from a mutable branch. ]]>
