T02 · Agent Memory Poisoning
Error
- Location
- hooks/inject.py:26
- Finding
- Unsigned Soul Tokens Enable Persistent Prompt Injection<![CDATA[ ## Vulnerability Details **File Location**: `hooks/inject.py:26-50`, `lib/memory_manager.py:215-230`, `lib/prompt_builder.py:90-113` **Vulnerability Type**: Unauthenticated persistent state injection **Risk Level**: High ### Complete Code Snippet ```python def parse_token(token: str) -> Optional[Dict[str, Any]]: """ Parse an injection token supporting Base64-encoded JSON. """ if not token or not token.strip(): return None raw = token.strip() try: decoded = base64.b64decode(raw, validate=True).decode("utf-8") data = json.loads(decoded) if isinstance(data, dict): return data except Exception: pass try: data = json.loads(raw) if isinstance(data, dict): return data except Exception: pass return {"token": raw} ``` ```python def inject_soul(self, token_data: Dict) -> None: """Inject a Pro soul profile into persistent state.""" state = self._load_state() state["injected_token"] = token_data.get("token") if "mbti" in token_data: state["agent_mbti"] = token_data["mbti"] if "preferences" in token_data: state["user_preferences"] = token_data["preferences"] if "interaction_patterns" in token_data: state["interaction_patterns"] = token_data["interaction_patterns"] if "adaptation_level" in token_data: state["adaptation_level"] = token_data["adaptation_level"] if "learnings" in token_data: state["learnings"] = token_data["learnings"] state["evolution_stage"] = 2 self._save_state(state) ``` ```python def build_persona_prompt(self) -> str: """Build a persona prompt from the current MBTI and preferences.""" mbti = self.mm.get_mbti() if not self._normalize_mbti(mbti): return "" mbti_template = self._load_mbti_template(mbti) if not mbti_template: return "" preferences = self.mm.get_user_preferences() pref_prompt = "" ...[truncated 2662 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace arbitrary JSON tokens with signed tokens using an appropriate asymmetric signature algorithm. 2. Verify the signature, expected issuer, intended audience, expiration time, issued-at time, and a replay-resistant nonce before accepting claims. 3. Remove the fallback that treats every nonempty opaque string as a successful token. 4. Define and enforce a strict schema: - Restrict `mbti` to the 16 supported values. - Require `preferences` and `learnings` to be arrays of bounded strings. - Restrict preferences to approved identifiers rather than free-form instructions. - Restrict `adaptation_level` to an integer from 0 through 100. - Validate interaction-pattern keys and numeric ranges. - Reject unknown fields. 5. Impose maximum token, array, and string sizes. 6. Keep untrusted profile data in a clearly delimited data channel instead of interpolating it into a system-level prompt. 7. Apply instruction-neutral serialization and explicitly tell the model that profile values are data, not instructions. 8. Require explicit authorization before overwriting an existing profile and provide a safe rollback mechanism. ]]>
