Back to skill

Security audit

Trans Doc to Markdown

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches a document-conversion purpose, but it needs review because it can persistently modify its own guidance and handles Lexiang credentials through under-scoped network paths.

Review this before installing. Use it only in an environment where you trust the skill with Lexiang/Cursor MCP credentials and private document contents. Do not run it with MCP_BASE_URL set to an untrusted host, prefer a narrowly scoped Lexiang token, and avoid the optional Gemini translator unless sending the document to Google is acceptable. Treat the self-updating lessons/SKILL.md instructions as unsafe unless a trusted maintainer reviews changes before they affect future runs.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (3)

T02 · Agent Memory Poisoning

Error
Location
references/lessons-learned.md:5
Finding

Mandatory persistent self-modification enables Agent memory poisoning

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/lexiang_fetch.py:36
Finding

Environment-controlled MCP endpoint can receive the Lexiang bearer token

Content
View full analysis
tuple[str, str]: """Return the authorization header and company identifier.""" p = os.path.expanduser("~/.cursor/mcp.json") if not os.path.exists(p): raise SystemExit("ERROR: the MCP configuration file does not exist") d = json.load(open(p)) srv = d.get("mcpServers", {}).get("lexiang", {}) auth = srv.get("headers", {}).get("Authorization", "") if not auth: raise SystemExit("ERROR: Lexiang Authorization was not found") if not auth.lower().startswith("bearer "): auth = "Bearer " + auth company_from = os.environ.get("LEXIANG_COMPANY_FROM", "") or os.environ.get("COMPANY_FROM", "") url = srv.get("url", "") m = re.search(r"[?&]company_from=([^&]+)", url) if m: company_from = m.group(1) if not company_from: raise SystemExit("ERROR: company_from was not found") return auth, company_from def mcp_call(name: str, arguments: dict, auth: str, company_from: str) -> dict: url = f"{MCP_BASE}/mcp?company_from={company_from}" body = { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": name, "arguments": arguments}, } req = urllib.request.Request( url, data=json.dumps(body).encode("utf-8"), headers={"Content-Type": "application/json", "Authorization": auth}, method="POST", ) ``` The translated error messages and docstring above preserve the executable behavior of the original code. ### Technical Analysis Authenticated Lexiang retrieval legitimately requires a bearer token. However, the destination receiving that token is sele ...[truncated 2077 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lexiang_fetch.py:147
Finding

Unvalidated API-provided download URLs permit internal requests and unbounded downloads

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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 (44)

Tainted flow: 'req' from os.environ.get (line 81, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/lexiang_fetch.py (reported line 88)May include surrounding context.

python
method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=180) as resp:
            result = json.loads(resp.read().decode())
    except urllib.error.HTTPError as e:
        if e.code == 401:

Tainted flow: 'req' from os.environ.get (line 81, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/lexiang_fetch.py (reported line 136)May include surrounding context.

python
api_url = ASSET_API.format(asset_id=asset_id)
    req = urllib.request.Request(api_url, headers={"Authorization": token}, method="GET")
    try:
        with urllib.request.urlopen(req, timeout=60) as resp:
            body = resp.read()
            ctype = resp.headers.get("Content-Type", "")
    except urllib.error.HTTPError as e:

Tainted flow: 'URL' from os.environ.get (line 7, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/translate_gemini.py (reported line 30)May include surrounding context.

python
for attempt in range(3):
        try:
            resp = requests.post(URL, json=payload, timeout=120)
            if resp.status_code == 200:
                data = resp.json()
                return data["candidates"][0]["content"]["parts"][0]["text"]

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents this skill as a transformation pipeline that converts multiple input types into a bilingual Markdown work package. The actual code chunk does not perform conversion or packaging. It only parses CLI arguments, reads existing text files, invokes validate_document, and prints validation results. While validation could be a supporting component in a larger conversion workflow, this code chunk’s primary behavior is materially different from the declared purpose and lacks the advertised conversion/output functionality. No undeclared sensitive resource access or upload behavior is present, but the main purpose mismatch is clear.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The description claims a broader document-conversion skill that accepts multiple input types (PDFs, Lexiang links, prepared Markdown packages) and outputs a bilingual Markdown work package plus a final Markdown file. The supplied code is much narrower: it is a fetch/extraction utility specifically for Lexiang entries. It calls Lexiang MCP APIs to get entry metadata and AI-parsed content, downloads image assets, rewrites image references, strips OCR/image metadata, and saves intermediate/source artifacts. While parts of the declared output overlap (source.md, images/, meta.json, and no upload logic), the primary purpose is materially different because the code does not perform bilingual generation, document-profile selection, general PDF conversion, or prepared Markdown package processing. Therefore the description does not accurately represent the actual behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This is a material description-behavior mismatch. The declared purpose describes an end-to-end document conversion skill producing bilingual Markdown work packages from multiple input types. The actual code chunk is only a helper script for local PDF extraction tasks: text dump, thumbnails, manual band crop, and card-region crop. While PDF extraction could be a supporting subcomponent of the larger declared skill, the supplied code itself does not implement the claimed main behavior or outputs. It also introduces specific extraction/cropping behavior that is not represented in the description. Therefore the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description describes a document-conversion skill handling PDFs, Lexiang links, or prepared Markdown packages and producing a structured bilingual work package with multiple artifacts. The supplied code does something much narrower and materially different: it accepts a local Markdown file path, translates its content chunk-by-chunk via the Gemini API, and writes a single translated Markdown file. There is no PDF parsing, no Lexiang link handling, no package assembly, no metadata generation, and no image directory/source.md creation. Additionally, the code makes outbound requests to Google's Gemini API, which is a notable behavior absent from the declared description. This is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description centers on a conversion skill that ingests PDFs, Lexiang links, or prepared Markdown packages and outputs a bilingual Markdown work package. The actual code chunk does not implement conversion or packaging; it implements automated tests for a validator. While some checks align with the stated quality goals (preserving paragraphs, images, structure), the primary behavior is materially different: it validates finished bilingual Markdown and tests a CLI, rather than transforms source inputs into output artifacts. There is also no evidence here of handling PDFs, Lexiang links, output file generation, or publishing delegation. Therefore the description does not accurately represent this code chunk.

Content

No source excerpt is available for this finding.

MCP Config Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill accesses MCP server configuration files (mcp.json). MCP configs contain server URLs, authentication tokens, and tool definitions — reading them allows the skill to discover and potentially abuse other tool integrations.

Content

Scanner excerpt · scripts/lexiang_fetch.py (reported line 54)May include surrounding context.

python
"""返回 (authorization_header, company_from)。"""
    p = os.path.expanduser("~/.cursor/mcp.json")
    if not os.path.exists(p):
        raise SystemExit("ERROR: ~/.cursor/mcp.json 不存在,无法读取乐享 token")
    d = json.load(open(p))
    srv = d.get("mcpServers", {}).get("lexiang", {})
    auth = srv.get("headers", {}).get("Authorization", "")

MCP Config Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill accesses MCP server configuration files (mcp.json). MCP configs contain server URLs, authentication tokens, and tool definitions — reading them allows the skill to discover and potentially abuse other tool integrations.

Content

Scanner excerpt · scripts/lexiang_fetch.py (reported line 54)May include surrounding context.

python
"""返回 (authorization_header, company_from)。"""
    p = os.path.expanduser("~/.cursor/mcp.json")
    if not os.path.exists(p):
        raise SystemExit("ERROR: ~/.cursor/mcp.json 不存在,无法读取乐享 token")
    d = json.load(open(p))
    srv = d.get("mcpServers", {}).get("lexiang", {})
    auth = srv.get("headers", {}).get("Authorization", "")

MCP Config Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill accesses MCP server configuration files (mcp.json). MCP configs contain server URLs, authentication tokens, and tool definitions — reading them allows the skill to discover and potentially abuse other tool integrations.

Content

Scanner excerpt · scripts/lexiang_fetch.py (reported line 69)May include surrounding context.

python
if m:
        company_from = m.group(1)
    if not company_from:
        raise SystemExit("ERROR: company_from 未找到(检查 mcp.json URL 或 LEXIANG_COMPANY_FROM)")
    return auth, company_from

Tainted flow: 'download_url' from open (line 153, file read) → urllib.request.urlopen (network output)

High
Category
Data Flow
Confidence
95% confidence
Finding

The script trusts a url field returned by the asset API JSON and fetches it without validating the destination. A malicious or compromised API response could force the client to make arbitrary outbound requests, potentially probing internal services or downloading attacker-controlled payloads that are then written to disk as trusted images.

Content

Scanner excerpt · scripts/lexiang_fetch.py (reported line 160)May include surrounding context.

python
except (json.JSONDecodeError, UnicodeDecodeError) as e:
            return None, f"invalid asset json: {e}"
        try:
            with urllib.request.urlopen(download_url, timeout=120) as resp:
                data = resp.read()
                if not mime:
                    mime = resp.headers.get("Content-Type", "")

Tainted flow: 'dl' from open (line 427, file read) → urllib.request.urlopen (network output)

High
Category
Data Flow
Confidence
80% confidence
Finding

File contents flow to a network sink. This may indicate data exfiltration of sensitive files.

Content

Scanner excerpt · scripts/lexiang_fetch.py (reported line 434)May include surrounding context.

python
company_from,
        )
        pdf_path = os.path.join(out_dir, "paper.pdf")
        with urllib.request.urlopen(dl["data"]["url"], timeout=300) as r, open(
            pdf_path, "wb"
        ) as f:
            f.write(r.read())

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The README states that inputs are converted into a 'faithful bilingual (Chinese-English) Markdown package' and that translation is performed by the current model by default. This imposes a specific output language/locale behavior rather than offering the user a choice, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The translation section explicitly requires English text followed by Chinese translation and defines Chinese-specific formatting rules for titles, lists, and paragraphs. Because the file does not present this as an opt-in or region-justified constraint, it constitutes a natural-language locale policy violation.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill instructs use of shell commands, local file reads/writes, environment-dependent behavior, and network access, but it does not declare any explicit tool scope or permission boundaries. In an agent environment, this increases the chance of over-privileged execution, unintended external access, or unsafe file operations because the runtime cannot constrain the skill to the minimum required capabilities.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The description states the skill converts inputs into 'bilingual Markdown work packages', and the body consistently mandates Chinese/English bilingual output. This is a natural-language locale policy constraint applied by default, with no indication that the user can choose another language or opt in to the bilingual behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The instructions say that if the source is not Chinese, it should be translated into Chinese/English parallel text. This enforces a specific locale behavior automatically based on content detection rather than user choice, which matches the policy-violation criterion for forced language or locale.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The entire skill documentation and usage guidance are presented only in Chinese, with no indication that users may choose another language or that the locale restriction is intentional or required. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The file’s operational guidance is written entirely in Chinese and repeatedly mandates specific output formatting such as '标题单行双语 ## EN / 中文' and list items as 'English item / 中文条目', indicating a required Chinese-inclusive output convention. There is no language-choice or opt-in language telling the user they may select another language, so this imposes a locale/language policy by default.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The document repeatedly requires bilingual EN+CN output formats as part of the skill contract, such as stat cards, definition cards, and tables. This is a natural-language locale policy constraint that does not present user opt-in, choice, or a documented region-specific justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The table translation rule says content 'must' place Chinese and English in the same cell, creating a mandatory bilingual formatting policy. Because the file does not offer a language choice or explain a justified locale restriction, this is a policy-relevant natural-language constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly instructs the agent to produce Chinese-English bilingual output by default and to skip translation entirely when the source is already sufficiently Chinese, without indicating that this behavior is conditional on user preference. That can override the user's requested output language or format, causing policy noncompliance, user surprise, or unintended disclosure/processing of content in an extra language.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code contains natural-language error text that forces a specific language/locale for users, e.g. the exception at L059 is in Chinese. The file does not offer any language selection or indicate that the validator is intentionally restricted to a Chinese-speaking context, which matches the locale-policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Natural-language exception messages throughout the module are written in Chinese, such as the error at L118, and the same pattern continues in later validation failures. Because these are user-visible messages and there is no opt-in, fallback, or documented regional limitation in this file, this is a language policy violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.