T02 · Agent Memory Poisoning
Warning
- Location
- scripts/sync_feishu_contacts.py:62
- Finding
- Untrusted Feishu Directory Data Is Persisted in Agent Prompt Context<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sync_feishu_contacts.py`, lines 62-79 and 100-101 **Vulnerability Type**: Persistent indirect prompt injection through unsanitized contact data **Risk Level**: Medium ### Vulnerable Code ```python for u in data.get("items", []): name = u.get("name", "") open_id = u.get("open_id", "") if name and open_id: users.append({"name": name, "open_id": open_id}) if not data.get("has_more"): break page_token = data.get("page_token") if not users: print("Warning: no users found. Check app permissions (need contact:user.base:readonly).") sys.exit(1) # 4. Update USER.md contacts table TABLE_HEADER = "| 姓名 | open_id |\n|------|---------|" table_rows = "\n".join(f"| {u['name']} | {u['open_id']} |" for u in users) ``` The resulting content is subsequently persisted: ```python with open(user_md_path, "w") as f: f.write(new_content) ``` `SKILL.md`, lines 15-17, establishes that this file is injected into the Agent's system prompt: ```markdown **Fix**: Embed an `open_id → name` lookup table in USER.md. Since workspace files are injected into the system prompt, the agent matches senders instantly — no tool calls. ``` ### Technical Analysis Feishu-provided `name` and `open_id` values are inserted directly into a Markdown table without validation or escaping. In particular, the implementation does not reject newlines, Markdown table separators, control characters, oversized values, or instruction-like content. Because `USER.md` is intended to be loaded into the Agent's persistent prompt context, directory data crosses a trust boundary: externally controlled profile information is converted into persistent high-trust Agent context. If a directory user can control a display name containing formatting or instruction text, the value may break out of the intended table row and introduce content that the Agent could interpret as instructions. This is a persistent indirect prompt- ...[truncated 1897 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Validate directory fields before storing them: - Reject newline, carriage-return, null, and other control characters. - Enforce conservative maximum lengths. - Validate `open_id` against the expected Feishu identifier format. 2. Escape Markdown metacharacters, especially pipe characters and backslashes, before generating table rows. 3. Treat contact records explicitly as untrusted data rather than instructions. 4. Prefer storing the mapping in a structured data file, such as JSON, outside system-prompt context. 5. Expose a narrowly scoped contact-lookup function that returns only the matching record when required. 6. If prompt storage remains necessary, place the data inside a clearly delimited section accompanied by an instruction that its contents are untrusted data and must never be followed as instructions. 7. Review existing generated `USER.md` files for unexpected multiline entries and reload the gateway only after sanitizing them. 8. Add tests using names containing newlines, pipes, backticks, headings, and instruction-like text to verify that table breakout is impossible. ]]>
