T02 · Agent Memory Poisoning
- Location
references/lessons-learned.md:5- Finding
Mandatory persistent self-modification enables Agent memory poisoning
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
references/lessons-learned.md:5Mandatory persistent self-modification enables Agent memory poisoning
scripts/lexiang_fetch.py:36Environment-controlled MCP endpoint can receive the Lexiang bearer token
scripts/lexiang_fetch.py:147Unvalidated API-provided download URLs permit internal requests and unbounded downloads
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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"]
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.
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.
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.
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.
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.
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.
"""返回 (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", "")
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.
"""返回 (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", "")
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.
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
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.
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", "")
File contents flow to a network sink. This may indicate data exfiltration of sensitive files.
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())
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
No suspicious patterns detected.