T01 · Skill Instruction Hijacking
Warning
- Location
- email.agent.ts:30
- Finding
- User-Controlled Instructions Are Elevated to a System Message## Vulnerability Details **File Location**: `email.agent.ts`, lines 30–39 **Vulnerability Type**: Prompt injection through privileged instruction placement **Risk Level**: Medium ```typescript async sendEmail(dto: SendEmailDto) { const { email, name, subject = '', body = '', instructions = '' } = dto; const result = await this.agent.invoke({ messages: [ { role: "system", content: message.EMAIL_PROMPT(instructions), }, { role: "user", content: message.EMAIL_USER_MESSAGE(email, name, subject, body), }, ], }); ``` ### Technical Analysis The caller-controlled `dto.instructions` value is passed to `message.EMAIL_PROMPT` and the resulting content is assigned the privileged `system` role. Consequently, untrusted instructions may be interpreted with greater authority than the email data supplied in the `user` message. An attacker could submit instructions that attempt to override the intended email-composition rules, alter recipients or message content, conceal relevant information from the approver, or induce unintended `EmailTool` calls. The implementation of `message.EMAIL_PROMPT` was outside the supplied audit scope, so any internal escaping or delimitation could not be verified. The configured human-in-the-loop middleware requires an approve, edit, or reject decision for `EmailTool`, which reduces the likelihood of automatic delivery. It does not eliminate the underlying prompt-injection condition because manipulated output or tool arguments may still be presented to an approving user. ### Attack Path 1. An attacker or untrusted caller provides a crafted value in `dto.instructions`. 2. `sendEmail` passes that value to `message.EMAIL_PROMPT`. 3. The generated prompt is assigned the `system` role. 4. The model interprets the attacker-controlled text as privileged instructions and may compose altered content or request an unintended `Em ...[truncated 851 chars]
- Remediation
- ## Remediation Suggestions - Use a fixed, application-controlled system prompt. Place caller-provided composition preferences in a clearly delimited user-role message rather than interpolating them into system-role content. - Treat model-generated tool arguments as untrusted. Validate and authorize the recipient, subject, body, attachments, and other delivery options inside `EmailTool`. - Bind the permitted recipient and other security-sensitive fields to validated application data instead of allowing the model to redefine them. - Apply schema validation, length limits, and control-character filtering to every `SendEmailDto` field. - Display a structured approval view showing the final recipient, subject, body, attachments, and sender identity. Highlight differences between requested and model-generated values. - Verify with integration tests that `humanInTheLoopMiddleware` reliably intercepts the exact registered tool name and that rejection prevents all delivery side effects. - Ensure authorization and recipient policy checks remain mandatory even when a human approves the request.
