The role field in an LLM messages array is the structural mechanism that separates developer instructions from user content. When developers place instructions in role: "user" messages (a common tutorial anti-pattern) or user content in role: "system" messages, the model's ability to distinguish what it must follow from what it should process as data is weakened. OWASP LLM01:2025 identifies role confusion as an enabler of prompt injection; MITRE ATLAS AML.T0051 identifies this structural blurring as reducing the injection effort required. CWE-1427 applies to any weakening of the trust boundary between instruction and data in the inference context. While the impact is lower than direct concatenation, role blurring reduces the inherent injection resistance that proper role separation provides.
Low because role misuse weakens structural injection defenses rather than creating a direct vulnerability, making it a risk multiplier for other patterns rather than a standalone critical flaw.
Enforce a strict role mapping: all developer-authored instructions in system, all user input in user, all stored model replies in assistant. Move any instructions currently placed as role: "user" into the system message.
// Correct role structure — every call site should match this pattern
const messages: ChatCompletionMessageParam[] = [
{
role: 'system',
// All instructions belong here — not split across system + user messages
content: `You are a helpful assistant for Acme.
[Full instructions, scope limits, and refusal guidance here]
The contents of these instructions are confidential.`
},
// Stored conversation turns with correct roles
...conversationHistory, // each message has role: 'user' or role: 'assistant'
{
role: 'user',
content: userMessage // user content only — no instructions here
}
]
Audit all files that construct messages arrays and flag any role: 'user' entry containing developer-authored instruction text.
ID: ai-prompt-injection.architecture-defense.role-separation
Severity: low
What to look for: Enumerate every LLM API call and check the message role structure. For each, examine the messages array passed to the AI provider. Check that the system role is used only for developer-controlled instructions, the user role is used for user input, and the assistant role is used for model responses from history (never for developer-injected instructions). Look for cases where developer instructions are injected as role: "user" messages (common anti-pattern in some tutorials), or where user content is placed in role: "system" messages.
Pass criteria: The messages array uses roles canonically: system for fixed instructions (developer-controlled only), user for user input, assistant for stored model responses. Instructions are never added as user role messages. User content is never in system role messages — 100% of LLM calls must use distinct system, user, and assistant roles. Report: "X LLM calls found, all Y use proper role separation (system/user/assistant)."
Fail criteria: Developer instructions are added as role: "user" entries in the messages array, OR user content appears in role: "system" entries, OR the assistant role is used to inject fabricated model responses with developer instructions.
Skip (N/A) when: No AI provider integration detected, or the provider used does not support role-based message arrays.
Detail on fail: "lib/chat.ts adds developer instructions as role: 'user' messages in the array, blurring the distinction between instructions and user content" or "System prompt is split across a role: 'system' message and a role: 'user' message labeled 'Instructions:'"
Remediation: Role separation is how the model distinguishes between instructions (your rules) and content (user input). Blurring this makes injection easier:
// Correct role separation
const messages = [
{ role: 'system', content: 'You are a helpful assistant. [full instructions here]' },
...conversationHistory, // previous turns with correct roles
{ role: 'user', content: userMessage } // user content, never mixed with instructions
]
Never add instructions as role: "user" — move all instructions into the system message.