Back to skill

Security audit

论文研读卡·观点可追句、引文可回源

Security checks for vulnerabilities and agentic risk

Overview

The paper-reading workflow is coherent, but its optional online setup can send your API key to an unapproved server.

Use manual cards and the local checker while leaving Cue credentials unset until the connection examples validate destinations and the helper rejects unsafe redirects. Before enabling external parsing, explicitly approve which documents go to Cue, especially unpublished work. Review and pin optional installer and dependency versions.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (26)

Tainted flow: 'req' from os.environ.get (line 188, 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/cue_connectivity_check.py (reported line 193)May include surrounding context.

python
code = None
    raw = ""
    try:
        with urllib.request.urlopen(req, timeout=args.timeout) as resp:
            code, raw = resp.status, resp.read().decode("utf-8", "replace")
    except urllib.error.HTTPError as err:
        detail = ""

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.en.md (reported line 88)May include surrounding context.

bash
   CATALOG="https://cuecue.cn/api/mcp-catalog"
   curl -s "$CATALOG" | python3 -c 'import json,sys
   rows=json.load(sys.stdin)["data"]["domains"]
   [print(r.get("id"), r.get("external_status"), (r.get("routing") or {}).get("url", "-")) for r in rows]'
   DOMAIN_ID="<replace with one of the ids listed by the line above>"

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.en.md (reported line 92)May include surrounding context.

md
rows=json.load(sys.stdin)["data"]["domains"]
   [print(r.get("id"), r.get("external_status"), (r.get("routing") or {}).get("url", "-")) for r in rows]'
   DOMAIN_ID="<replace with one of the ids listed by the line above>"
   ROUTING_URL="$(curl -s "$CATALOG" | python3 -c 'import json,sys
   rows=json.load(sys.stdin)["data"]["domains"]
   hit=[r["routing"]["url"] for r in rows if r.get("id")==sys.argv[1] and r.get("routing")]
   print(*hit,sep="\n") if hit else sys.exit("Not found: that id is not in the catalog response (or its routing has no url field) - run the line above and pick one of the ids listed")' "$DOMAIN_ID")"

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.md (reported line 86)May include surrounding context.

bash
   CATALOG="https://cuecue.cn/api/mcp-catalog"
   curl -s "$CATALOG" | python3 -c 'import json,sys
   rows=json.load(sys.stdin)["data"]["domains"]
   [print(r.get("id"), r.get("external_status"), (r.get("routing") or {}).get("url", "-")) for r in rows]'
   DOMAIN_ID="<把这里换成上一行列出的那个域 id>"

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.md (reported line 90)May include surrounding context.

md
rows=json.load(sys.stdin)["data"]["domains"]
   [print(r.get("id"), r.get("external_status"), (r.get("routing") or {}).get("url", "-")) for r in rows]'
   DOMAIN_ID="<把这里换成上一行列出的那个域 id>"
   ROUTING_URL="$(curl -s "$CATALOG" | python3 -c 'import json,sys
   rows=json.load(sys.stdin)["data"]["domains"]
   hit=[r["routing"]["url"] for r in rows if r.get("id")==sys.argv[1] and r.get("routing")]
   print(*hit,sep="\n") if hit else sys.exit("未取到:该 id 不在目录现值里(或它没有 routing 里的 url 字段)——先跑上面那行列出现场的 id 再填")' "$DOMAIN_ID")"

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The supplied code's primary purpose is service connectivity diagnostics, not academic-paper analysis. It contains no PDF processing, page anchoring, citation verification, reproduction analysis, or review-scaffold generation, and the description does not disclose MCP catalog discovery or credential-backed endpoint testing. The optional POST is guarded by HTTPS and an allowed-host suffix check, but those safeguards do not make this materially different capability part of the declared purpose. Also, despite comments claiming no packets are sent by default, the normal path performs an anonymous catalog GET unless a local catalog file is supplied.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The supplied chunk implements generic authenticated MCP discovery, not the declared academic-paper workflow. It contains no PDF processing, evidence anchoring, citation verification, or reading-card generation, and establishes no connection between the discovered tools and those functions. Although tool discovery could support a larger implementation, that relationship is not demonstrated here. The credential is sent to the catalog-selected endpoint; the snippet does not establish malicious exfiltration.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The supplied chunk implements generic remote-tool discovery rather than the declared paper-reading workflow. It does not retrieve or analyze a paper, verify citations, or generate reading materials, and it does not establish that the selected MCP service supports those tasks. Tool discovery could be a supporting implementation detail if that connection were shown, but it is absent here. The code also sends CUE_API_KEY to the selected endpoint; this is observable credential use, not by itself evidence of malicious exfiltration.

Content

No source excerpt is available for this finding.

Automatic redirects can forward credentials outside the destination restrictions promised by the documentation.

High
Category
Excessive Agency
Confidence
99% confidence
Finding

The destination guard checks only the initial URL, but urllib.request.urlopen follows redirects automatically. For supported redirects such as POST-to-GET 302/303 responses, urllib can copy the Authorization header to a different host or an HTTP destination, allowing a compromised or attacker-influenced authorized endpoint to disclose CUE_API_KEY despite the advertised restrictions.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.en.md (reported line 44)May include surrounding context.

bash
# 1. Put it in the skills directory
mkdir -p ~/.workbuddy/skills
cp -r paper-study-cn ~/.workbuddy/skills/

# 2. Copy the card template and fill it following section 1

External document parsing lacks an explicit warning that paper content is sent to a third-party service.

Medium
Category
Output Handling
Confidence
91% confidence
Finding

The documentation offers external parsing of user-provided local papers without explicitly explaining that document content would leave the user's environment. Consent to parsing costs is not informed consent to third-party disclosure, particularly for unpublished or confidential manuscripts; optional installation and a manual fallback reduce but do not eliminate this risk.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding

The command runs an unpinned npm installer through npx and installs skills from a mutable repository reference. Although this is not itself an MCP server declaration, compromised or unexpectedly changed upstream releases could execute installer code or introduce hostile agent instructions; interactive skill selection does not verify their integrity.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding

Selecting only cue-omni-reader narrows the installed content but does not pin either the npm installer or the repository revision. Upstream compromise or later changes could therefore affect executable installation code or the instructions used to handle users' papers.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
98% confidence
Finding

The shell example sends CUE_API_KEY to a URL copied directly from the live catalog without enforcing HTTPS or an authorized destination host. A compromised or misconfigured catalog could redirect the credential to an attacker-controlled endpoint or an unencrypted connection; the separately described Python helper's safeguards do not protect this shell example.

Content

Scanner excerpt · README.en.md (reported line 97)May include surrounding context.

md
hit=[r["routing"]["url"] for r in rows if r.get("id")==sys.argv[1] and r.get("routing")]
   print(*hit,sep="\n") if hit else sys.exit("Not found: that id is not in the catalog response (or its routing has no url field) - run the line above and pick one of the ids listed")' "$DOMAIN_ID")"
   [ -n "$ROUTING_URL" ] || { echo "Fetch failed: ROUTING_URL is empty - run the line above, then set DOMAIN_ID to one of the ids listed" >&2; exit 1; }
   curl -s -X POST "$ROUTING_URL" \
     -H "Authorization: Bearer $CUE_API_KEY" \
     -H "Accept: application/json, text/event-stream" \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \

MCP server referenced without pinned version: 'npx skills'.

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding

The self-installation command also relies on an unpinned npx package and mutable repository content. Checking that the skill appears in the installed list proves installation occurred, not that the installed code and instructions match a reviewed version.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.md (reported line 42)May include surrounding context.

bash
# 1. 放到技能目录
mkdir -p ~/.workbuddy/skills
cp -r paper-study-cn ~/.workbuddy/skills/

# 2. 复制卡模板,按 §一 三步填

MCP server referenced without pinned version: 'npx skills'.

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding

The installation command invokes an unpinned npm executable through npx skills and references a mutable skill repository. Although this is an installer rather than an MCP server, a compromised future package or repository revision could execute code or install adversarial agent instructions under the user's account.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding

Selecting only cue-omni-reader limits which skill is installed but does not pin either the npx skills executable or the repository revision. Changes to either upstream source can therefore introduce executable code or malicious instructions without changes to this README.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
98% confidence
Finding

The shell example sends CUE_API_KEY to a URL taken directly from catalog JSON without validating its scheme or host. A compromised or manipulated catalog response could direct the bearer token to an attacker-controlled HTTPS endpoint or an unencrypted HTTP endpoint; the separately described Python helper's protections do not protect this shell command.

Content

Scanner excerpt · README.md (reported line 95)May include surrounding context.

md
hit=[r["routing"]["url"] for r in rows if r.get("id")==sys.argv[1] and r.get("routing")]
   print(*hit,sep="\n") if hit else sys.exit("未取到:该 id 不在目录现值里(或它没有 routing 里的 url 字段)——先跑上面那行列出现场的 id 再填")' "$DOMAIN_ID")"
   [ -n "$ROUTING_URL" ] || { echo "取值失败:ROUTING_URL 为空——先跑上面那行列出现场 id,再把 DOMAIN_ID 换成其中之一" >&2; exit 1; }
   curl -s -X POST "$ROUTING_URL" \
     -H "Authorization: Bearer $CUE_API_KEY" \
     -H "Accept: application/json, text/event-stream" \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \

The parsing workflow requests confirmation for consumption but does not warn that paper content may be sent to the external Cue service.

Medium
Category
Output Handling
Confidence
89% confidence
Finding

The skill accepts local papers and pasted text and describes using an external parsing service, but its required confirmation concerns consumption rather than explicitly authorizing disclosure of document contents. Users could approve parsing without understanding that an unpublished or confidential document may leave their machine; the actual upload implementation is not provided here.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'npx skills'.

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding

The self-installation command uses an unpinned npm executable and a mutable huhoo/cue-awesome repository reference. Its legitimate installation purpose does not remove the supply-chain risk: a later compromised release or revision could run code or introduce persistent malicious skill instructions.

Content

No source excerpt is available for this finding.

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, file_read, file_write, network, shell.

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

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

python
scripts_dir.mkdir(parents=True)
        dst = scripts_dir / Path(__file__).name
        shutil.copy2(str(Path(__file__).resolve()), str(dst))
        r = subprocess.run([sys.executable, '-B', str(dst), str(card)], capture_output=True, text=True)
        out = r.stdout + r.stderr
        named = '尺子的来源不可读' in out
        detail = '隔离副本(无 assets/ 模板)退码 %s,详行含「尺子的来源不可读」=%s' % (

The documented no-network default actually performs a catalog GET without --post.

Medium
Category
Excessive Agency
Confidence
100% confidence
Finding

Without --catalog-file, the script fetches the catalog before checking whether --post, --list, or --domain was supplied, contradicting repeated claims that the default sends no packets. Although other documentation acknowledges the anonymous GET, users relying on the no-network assurances can unintentionally disclose network metadata to an external service, including during otherwise local paper-reading work.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.