Back to skill

Security audit

Feishu Chat Importer

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but it imports external chat text directly into persistent agent memory where future agent features may reuse it.

Review imported Feishu exports before running the non-dry-run import, especially if chats include messages from untrusted people. Prefer a backup or quarantine workflow and treat the resulting memory files as untrusted historical data until reviewed.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Findings (1)

T02 · Agent Memory Poisoning

Warning
Location
scripts/parse_feishu.py:111
Finding

Untrusted Feishu Messages Are Written to Persistent Agent Memory

Content
View full analysis

Vulnerability Details

File Location: scripts/parse_feishu.py:111-119; persistent write at scripts/batch.py:102-120
Vulnerability Type: T02: Agent Memory Poisoning
Risk Level: Medium

Complete Code Snippet

python
# scripts/parse_feishu.py:111-119
for chat_id, chat_msgs in by_chat.items():
    lines.append(f"\n### Chat: {chat_id}")
    for msg in chat_msgs:
        role_label = "user" if msg["role"] == "user" else "assistant"
        ts = msg["timestamp"][11:]  # just HH:MM:SS
        content = msg["content"]
        # Truncate very long messages
        if len(content) > 500:
            content = content[:500] + "..."
        lines.append(f"- **{role_label}** [{ts}]: {content}")
python
# scripts/batch.py:102-120
if not already_in_episodic:
    entry_content = entry["content"]
    # If file exists, append; if not, create with header
    if mem_file.exists():
        existing = mem_file.read_text(encoding="utf-8")
        # Avoid double headers if already has episodic content
        entry_content = "\n\n---\n\n" + entry_content
    else:
        entry_content = (
            f"# Episodic Memory — {entry['date']}\n\n"
            + "> Imported from Feishu chat history via feishu-chat-importer\n\n"
            + entry_content
        )

    with open(mem_file, "a", encoding="utf-8") as f:
        f.write(entry_content)

Technical Analysis

Feishu message content is externally controlled input. The parser inserts that content directly into Markdown episodic entries without applying trust classification, instruction neutralization, or a structural boundary that downstream agent components are required to treat exclusively as quoted data.

The batch process then appends the generated entry to persistent files under memory/episodic/. According to the skill documentation, the Dreaming system processes these files and makes their contents available through Imported Insights and Memory Palace views. Consequently, prompt-inject ...[truncated 1832 chars]

Remediation
View remediation

Remediation Suggestions

  1. Treat every imported message as untrusted data and attach explicit provenance and trust metadata to each entry.
  2. Store imported conversations in a structured data format or dedicated quarantine area rather than directly in agent-consumed episodic Markdown.
  3. Require downstream memory processors to place imported text inside a strict quoted-data boundary and explicitly instruct the model never to follow commands found inside that boundary.
  4. Escape Markdown control syntax and normalize content before storage. This reduces structural manipulation, although escaping alone does not prevent semantic prompt injection.
  5. Add prompt-injection detection and flag messages containing instruction-oriented patterns for manual review.
  6. Require explicit operator approval before promoting imported content into persistent agent memory.
  7. Apply least privilege to downstream processing: memory summarization should not have access to sensitive tools, credentials, or side-effecting operations.
  8. Preserve source identifiers, sender identifiers, timestamps, and an untrusted_external_content label so later consumers can enforce policy.
  9. Consider storing only deterministic summaries produced in an isolated, tool-free process, while retaining the original text outside active agent context.
  10. Add security tests demonstrating that instructions embedded in Feishu messages remain quoted as historical conversation and are never followed by downstream components.
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

A description-behavior mismatch is security-relevant because operators may approve the skill based on the documented behavior while the implementation performs materially different actions and output handling. For a memory-import skill, writing somewhere other than the stated episodic location or emitting different artifact formats can bypass expected review, validation, retention, or downstream safety assumptions.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill declares executable capabilities involving environment access, filesystem reads/writes, and shell execution but does not declare an explicit tool scope or permissions boundary. In an agent ecosystem, this weakens reviewability and can allow broader-than-expected operations, especially since the skill imports user data and writes into memory state.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This markdown file describes importing Feishu chat exports and explicitly states that it writes daily summaries to memory/episodic/, but it does not include a user-facing caution about modifying persistent memory data. Because the skill affects stored user/agent data, the documentation should clearly warn users before they run the import.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/batch.py (reported line 27)May include surrounding context.

python
def parse_export(json_path: Path) -> list[dict]:
    """Run the feishu parser and yield episodic entry dicts."""
    parser_script = SCRIPTS_DIR / "parse_feishu.py"
    proc = subprocess.run(
        ["python", str(parser_script), str(json_path)],
        capture_output=True, text=True
    )

Tainted flow: 'mem_file' from os.environ.get (line 98, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
88% confidence
Finding

The output path is derived from OPENCLAW_WORKSPACE, an environment variable that is trusted without validation, and the script then creates directories and appends files under that location. If an attacker can influence the environment in which this importer runs, they can redirect writes outside the intended workspace and cause unauthorized file creation or modification, especially because imported chat content is written verbatim.

Content

Scanner excerpt · scripts/batch.py (reported line 120)May include surrounding context.

python
+ entry_content
                        )

                    with open(mem_file, "a", encoding="utf-8") as f:
                        f.write(entry_content)
                    written_entries += 1
                    print(f"    Wrote episodic entry to {mem_file.name}")