T01 · Skill Instruction Hijacking
Error
- Location
- src/templates.ts:1
- Finding
- Attacker-Controlled Content Is Embedded into Generated Skill Instructions Without Escaping<![CDATA[ ## Vulnerability Details **File Location**: `src/templates.ts:1-24` **Vulnerability Type**: Generated skill instruction and YAML injection **Risk Level**: High ### Complete Code Snippet ```ts export function generateSkillMd(params: { name: string; description: string; price: number; envVars?: string[]; }): string { const envList = params.envVars ? [...params.envVars, "SKILLPAY_API_KEY"] : ["SKILLPAY_API_KEY"]; return `--- name: ${params.name} description: ${params.description} version: 1.0.0 metadata: openclaw: requires: env: ${envList.map((e) => ` - ${e}`).join("\n")} --- # ${params.name} ${params.description} ## Pricing $${params.price} USDT per call via SkillPay.me `; } ``` The values originate directly from the request body: ```ts const body = await request.json() as { user_id: string; name: string; description: string; price_usdt: number; env_vars?: string[]; }; ``` ### Technical Analysis The generated `SKILL.md` places `name`, `description`, and environment-variable names directly into YAML frontmatter and Markdown without validation, YAML serialization, escaping, or content-boundary enforcement. A TypeScript type assertion does not validate runtime JSON. An attacker can therefore include newlines, YAML keys, Markdown sections, or agent-directed instructions in these fields. In particular, the `description` is emitted both inside frontmatter and in the skill body, where it can become operative instructions when an AI agent loads the generated skill. This is especially dangerous because the declared purpose of the project is to create artifacts that users subsequently deploy and publish. The untrusted input therefore crosses a trust boundary and becomes trusted skill content. ### Attack Path 1. An attacker submits a scaffold request containing a multiline `description`, for example one that starts with a benign description and then adds instructions telling an agent to disclose secrets ...[truncated 1140 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Treat all scaffold parameters as untrusted structured data. - Enforce strict schemas and maximum lengths before template generation. - Restrict skill names to a conservative pattern such as `^[a-z0-9][a-z0-9-]{0,62}$`. - Reject control characters and line breaks in fields intended to occupy one YAML scalar. - Generate frontmatter with a well-maintained YAML serializer rather than string interpolation. - Quote and serialize every environment-variable entry. - Validate environment-variable names with a pattern such as `^[A-Z_][A-Z0-9_]*$`. - Define an explicit policy for descriptions. If descriptions are not intended to contain agent instructions, reject instruction-like sections, frontmatter delimiters, and unapproved Markdown structures. - Present the generated `SKILL.md` for explicit human review before publishing it. - Add adversarial tests covering newlines, `---`, YAML keys, Markdown headings, tool instructions, and oversized values. ]]>
