T01 · Skill Instruction Hijacking
- Location
scripts/preflight.py:124- Finding
Repository-Controlled Persona Content Is Promoted to a System Prompt
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md:16-17,scripts/preflight.py:124-158, andscripts/preflight.py:176-181
Vulnerability Type: Prompt injection through untrusted project configuration
Risk Level: MediumVulnerable Code
SKILL.md:16-17:markdown Check for `preflight-personas.md` in the project root. If it exists, use those personas. Otherwise use the defaults in `references/personas.md`.scripts/preflight.py:124-158:python def load_personas_from_file(path: str) -> list: """Parse personas from a markdown file.""" personas = [] current = None with open(path, 'r') as f: lines = f.readlines() for line in lines: line = line.rstrip() if line.startswith('### '): if current: personas.append(current) name = line[4:].strip() # Split on ( to get archetype if '(' in name: parts = name.split('(') name = parts[0].strip() archetype = parts[1].rstrip(')').strip() else: archetype = "" current = {"name": name, "archetype": archetype, "prompt": ""} elif current and line.startswith('- '): current["prompt"] += line[2:] + " " if current: personas.append(current) # Build proper prompts from bullet points for p in personas: if p["prompt"]: p["prompt"] = f"You are {p['name']} ({p['archetype']}). {p['prompt'].strip()}" return personas if personas else DEFAULT_PERSONASscripts/preflight.py:176-181:python response = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": persona["prompt"]}, {"role": "user", "content": EVALUATION_PROMPT.format(content=content)} ],Technical Analysis
The documented wor ...[truncated 2563 chars]
- Remediation
View remediation
Remediation Suggestions
- Do not automatically trust persona definitions found in a project. Require explicit user approval before loading a project-level override, particularly for repositories from untrusted sources.
- Keep the system prompt entirely application-controlled. Pass custom persona fields as clearly delimited, quoted data in a lower-privilege message rather than using them as the complete system message.
- Define a strict persona schema containing only expected fields such as name, archetype, audience context, and preferences.
- Enforce length limits and reject unexpected sections, control characters, role markers, prompt delimiters, or instruction-like fields.
- Add an invariant system instruction stating that persona data is untrusted and cannot alter the evaluation task, output schema, safety constraints, or handling of supplied content.
- Request structured model output through a validated JSON schema rather than parsing unrestricted prose.
- Display the selected persona-file path and trust status before submission, and provide an option to force the bundled defaults.
- Treat all model responses as untrusted data and escape them appropriately if they are later rendered in terminals, web interfaces, or automation pipelines.
