Back to skill

Security audit

lark-doc-reviser

Security checks for vulnerabilities and agentic risk

Overview

This skill is for editing Lark documents, but it gives an agent broad authenticated document-changing power while saving full document text locally and treating collaborator comments too much like instructions.

Install only if you are comfortable letting an authenticated lark-cli session read and modify the target Lark document. Treat all document comments as untrusted suggestions, confirm the exact block replacements and comment IDs before any PATCH or resolve step, and delete workspace state files after use, especially for sensitive documents.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
Findings (3)

T01 · Skill Instruction Hijacking

Warning
Location
SKILL.md:17
Finding
Untrusted Lark comments are promoted to actionable Agent instructions<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:17-30`; `scripts/fetch_doc.py:98-105, 189-212` **Vulnerability Type**: Indirect prompt injection through untrusted document comments **Risk Level**: Medium ### Complete Code Snippet ```markdown - `commented_blocks`: blocks that have unresolved comments, each with `elements`, `full_text`, and `comments[]{comment_id, anchor_text, instruction}` - `all_blocks`: full block list (no elements, for structural reference) **Always save to workspace.** The editing process may span multiple sessions. ### Step 2 — Present comments to user Show each entry in `commented_blocks` as: [block_type] full_text → 【anchor_text】 instruction Ask the user to confirm which comments to address, or proceed if the intent is clear. ``` ```python def extract_instruction(reply_list: dict) -> str: try: elements = reply_list["replies"][0]["content"]["elements"] return "".join( e["text_run"]["text"] for e in elements if e.get("type") == "text_run" ).strip() except (KeyError, IndexError): return "" ``` ```python comment_instructions = {} for item in raw_comments: cid = item.get("comment_id", "") instruction = extract_instruction(item.get("reply_list", {})) comment_instructions[cid] = instruction # 4. 合并:找出有评论的 block,附上 comments 列表 commented_map = {} # block_id → enriched block for cid, info in comment_to_block.items(): b = info["block"] bid = b["block_id"] instruction = comment_instructions.get(cid, "") if bid not in commented_map: # 复制 block,加 comments 列表 cb = {k: v for k, v in b.items()} cb["comments"] = [] commented_map[bid] = cb commented_map[bid]["comments"].append({ "comment_id": cid, "anchor_text": info["anchor"], "instruction": instruction, }) ``` ### Technical Analysis Lark document comments are controlled by document collaborators and therefore cross an external trus ...[truncated 1970 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly classify all document bodies and comments as untrusted data rather than Agent instructions. 2. Rename the `instruction` field to a neutral name such as `comment_text` or `requested_edit`. 3. Add a mandatory policy stating that comment content cannot override system, developer, user, or Skill constraints. 4. Restrict comment interpretation to proposed edits to the referenced block. Reject requests involving secrets, unrelated files, external commands, new network destinations, or unrelated tools. 5. Require explicit user approval of the exact target block and replacement text before every PATCH operation. 6. Display comments in clearly delimited data blocks and warn the Agent not to execute commands embedded in them. 7. Validate that each proposed action is limited to the document token and block identifiers already authorized by the user. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_doc.py:217
Finding
Full document contents are collected and persistently stored in plaintext<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:13-20`; `scripts/fetch_doc.py:217-226, 254-258` **Vulnerability Type**: Excessive sensitive-data collection and insecure local retention **Risk Level**: Medium ### Complete Code Snippet ```markdown python3 scripts/fetch_doc.py <doc_url_or_token> --out workspace/<token>_state.json ``` ```markdown This saves full doc state to `workspace/<token>_state.json` and prints a summary to stdout: - `commented_blocks`: blocks that have unresolved comments, each with `elements`, `full_text`, and `comments[]{comment_id, anchor_text, instruction}` - `all_blocks`: full block list (no elements, for structural reference) **Always save to workspace.** The editing process may span multiple sessions. ``` ```python # 5. all_blocks 去掉 elements(太长),只保留 block_id/type/parent/full_text all_blocks = [ {k: v for k, v in b.items() if k != "elements"} for b in slim_blocks ] return { "doc_token": "", # 由 main 填入 "commented_blocks": commented_blocks, "all_blocks": all_blocks, } ``` ```python output = json.dumps(result, ensure_ascii=False, indent=2) if args.out: with open(args.out, "w", encoding="utf-8") as f: f.write(output) print(f"[INFO] 已写入 {args.out}", file=sys.stderr) else: print(output) ``` ### Technical Analysis The script retrieves all blocks from the document and retains the `full_text` field for every textual block in `all_blocks`. Removing the structured `elements` field does not remove the underlying document content. The output also includes the document token, commented block content, formatting information, comment identifiers, anchor text, and comment instructions. The Skill requires this state to be saved to the workspace even though its core purpose is to process unresolved comments on selected blocks. No retention limit, cleanup procedure, restrictive file mode, redaction mechanism, or user consent step is provided. Python's ordinary `open()` call uses permissi ...[truncated 1640 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fetch and retain only blocks with unresolved comments, plus the minimum user-approved neighboring context required for interpretation. 2. Make local persistence opt-in instead of mandatory and clearly disclose which fields will be stored. 3. Create output files with owner-only permissions, such as mode `0600`, using a secure file-creation method. 4. Add automatic deletion after edits are completed and document a short maximum retention period. 5. Provide a redacted output mode that omits unrelated `full_text`, document tokens, and identifiers not needed for later operations. 6. Avoid printing full state to standard output by default because logs may have separate retention and access policies. 7. Keep state in a dedicated, access-controlled directory and prevent it from being committed, uploaded, or included in general workspace artifacts. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:11
Finding
Shell command examples interpolate unquoted externally derived values<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:11-14, 41-44, 61-66` **Vulnerability Type**: Potential shell command injection through unquoted arguments **Risk Level**: Medium ### Complete Code Snippet ```markdown ### Step 1 — Fetch doc state ```bash python3 scripts/fetch_doc.py <doc_url_or_token> --out workspace/<token>_state.json ``` ``` ```markdown For each comment requiring a text change, construct a patches list and run: ```bash python3 scripts/patch_blocks.py <doc_token> patches.json ``` ``` ```markdown ### Step 4 — Resolve addressed comments ```bash python3 scripts/resolve_comments.py <doc_token> <comment_id> [comment_id ...] # or via stdin: echo '["id1","id2"]' | python3 scripts/resolve_comments.py <doc_token> - ``` ``` ### Technical Analysis The documented commands contain placeholders derived from a user-supplied URL or token and remotely sourced comment identifiers. They are shown as direct shell substitutions without quoting or format validation. If an Agent constructs a shell command by replacing these placeholders verbatim, shell metacharacters in an attacker-controlled value may be interpreted as command separators, substitutions, redirections, or pipeline operators. The Python scripts themselves use `subprocess.run()` with argument arrays and do not set `shell=True`, which prevents shell injection inside their calls to `lark-cli`. The vulnerable boundary is the Skill instruction layer: an Agent may execute the documented top-level examples through a shell after directly interpolating untrusted values. ### Attack Path 1. An attacker supplies a crafted document token, URL, or other substituted value containing shell metacharacters. 2. The Agent follows `SKILL.md` and replaces a placeholder directly in one of the displayed shell commands. 3. The Agent executes the resulting command using a shell. 4. The shell parses the attacker's metacharacters rather than treating the entire value as a single argument. 5. The injected ...[truncated 1008 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Invoke the Python scripts through structured process APIs with separate argument arrays rather than constructing shell command strings. 2. Validate document tokens, block identifiers, and comment identifiers against strict allowlists matching documented Lark identifier formats. 3. Parse Lark URLs with a URL parser and reject unexpected schemes, hosts, path structures, fragments, and characters. 4. If a shell is unavoidable, apply robust shell quoting to every substituted argument, including output paths. 5. Do not derive output filenames directly from raw user input. Generate a safe filename from a validated token or a random local identifier. 6. Update the Skill instructions to forbid direct interpolation of document content or comment content into commands. 7. Prefer stdin or securely generated JSON files for lists of identifiers instead of constructing shell pipelines from remote values. ]]>
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The code uses lark-cli to read document blocks and unresolved comments, then builds a JSON object containing commented_blocks and all_blocks. There are no API calls that modify document content, no PATCH/update operations, and no logic that applies comment instructions back to the document. The fetching/showing-comments portion of the description is accurate, but the broader declared purpose emphasizes applying targeted edits based on comments, which this code chunk does not do. Therefore the description overstates the implemented capability, creating a material mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
There is a material description-behavior mismatch. The declared purpose emphasizes comment-centric functionality: reading unresolved comments, showing document comments, and revising content based on those comments. The supplied code contains no logic for retrieving comments, filtering unresolved comments, parsing feedback, or associating comments with document blocks. Instead, it is a lower-level utility that accepts precomputed patches and sends them to the Feishu/Lark documents batch_update endpoint to replace block text elements. While applying edits to a Feishu document is related to part of the broader declared workflow, the crucial advertised capability of fetching/processing comments is absent, and the code’s actual primary purpose is generic block patching.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
There is a clear description-behavior mismatch. The declared purpose centers on comment-driven document review and editing: reading unresolved comments, processing feedback, and applying edits to the document. The supplied code does none of that. It issues PATCH requests to the Feishu comments endpoint with `is_solved: true`, which marks existing comments as resolved. This is a materially different primary purpose and an undeclared capability relative to the description. While both involve Feishu comments, resolving comments is not the same as fetching them or editing document content based on them.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill invokes Python scripts, writes state files into a workspace, and relies on networked Feishu/Lark API access, but it does not declare any explicit tool scope or permission boundaries. This makes the operational authority of the skill implicit rather than constrained, increasing the chance that an agent can perform filesystem, shell, or remote document actions without transparent review or least-privilege controls.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
This code file contains user-facing natural-language text entirely in Chinese in the module docstring and later CLI help/output strings. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified, which is not present here.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
params = {"page_size": 500}
        if page_token:
            params["page_token"] = page_token
        r = subprocess.run(
            ["lark-cli", "api", "GET",
             f"/open-apis/docx/v1/documents/{doc_token}/blocks",
             "--params", json.dumps(params)],
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def fetch_comments(doc_token: str, file_type: str = "docx") -> list:
    r = subprocess.run(
        ["lark-cli", "drive", "file.comments", "list",
         "--params", json.dumps({
             "file_token": doc_token,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""调用 batch_update API,返回响应"""
    body = build_batch_request(patches)

    result = subprocess.run(
        ["lark-cli", "api", "PATCH",
         f"/open-apis/docx/v1/documents/{doc_token}/blocks/batch_update",
         "--data", json.dumps(body)],
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The skill metadata says it should read unresolved comments and revise documents based on them, but this script instead marks comments as resolved. In an agent setting, that mismatch can silently destroy review state and hide unresolved feedback, making it easier for users or downstream automation to believe comments were processed when they were only dismissed.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def resolve_comment(doc_token: str, comment_id: str) -> dict:
    """调用 API 将单条评论标记为已解决"""
    result = subprocess.run(
        ["lark-cli", "api", "PATCH",
         f"/open-apis/drive/v1/files/{doc_token}/comments/{comment_id}",
         "--params", '{"file_type": "docx"}',
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script performs remote PATCH operations that change document comment state without any confirmation prompt, dry-run mode, or strong warning. In this skill context, that is more dangerous because the advertised purpose is comment-driven revision; an agent could prematurely resolve comments in a shared document and erase visible review signals without the user's informed approval.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The module docstring and runtime messages are written in Chinese, which imposes a specific language on users. There is no indication that this locale restriction is optional, configurable, or justified as region-specific.

Static analysis

No suspicious patterns detected.