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

Content
View full analysis
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
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_doc.py:217
Finding

Full document contents are collected and persistently stored in plaintext

Content
View full analysis
--out workspace/_state.json ``` ```markdown This saves full doc state to `workspace/_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
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:11
Finding

Shell command examples interpolate unquoted externally derived values

Content
View full analysis
--out workspace/_state.json ``` ``` ```markdown For each comment requiring a text change, construct a patches list and run: ```bash python3 scripts/patch_blocks.py patches.json ``` ``` ```markdown ### Step 4 — Resolve addressed comments ```bash python3 scripts/resolve_comments.py [comment_id ...] # or via stdin: echo '["id1","id2"]' | python3 scripts/resolve_comments.py - ``` ``` ### 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
View remediation
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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

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/fetch_doc.py (reported line 66)May include surrounding context.

python
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)],

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/fetch_doc.py (reported line 81)May include surrounding context.

python
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,

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/patch_blocks.py (reported line 63)May include surrounding context.

python
"""调用 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)],

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

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/resolve_comments.py (reported line 19)May include surrounding context.

python
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"}',

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.