Back to skill

Security audit

Zhiyan Smart Research

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does the advertised literature search/report workflow, but its command and session-file handling create review-worthy risks from crafted input.

Review before installing. The literature-search purpose is legitimate, but the publisher should change the documented workflow to pass arguments without shell interpolation and validate --session-id before writing files. Users should also be comfortable with research queries going to Crossref/PubMed and reports being stored locally in the skill directory.

Vulnerability Patterns
  • 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
  • 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)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:67
Finding

Shell Command Injection Through Interpolated User-Controlled Research Topics

Content
View full analysis
" \ --report-file /tmp/report.md \ --papers-json /tmp/papers.json ``` A similar pattern is used for the search query at `SKILL.md`, line 35: ```bash python3 {baseDir}/scripts/search_literature.py "" > /tmp/papers.json ``` ### Technical Analysis The Skill instructs the Agent to insert the original user question directly into a double-quoted shell command. Double quotes do not neutralize shell command substitution, and embedded quotation marks can terminate the intended argument. If the Agent constructs and executes this command through a shell, a malicious topic containing syntax such as command substitution can cause additional commands to execute. This risk is not inherent to literature searching or report storage. It results from constructing shell command text with untrusted data instead of passing the topic as an isolated process argument. Exploitation depends on the host Agent following the documented workflow and invoking the generated command through a shell. The vulnerable instruction explicitly directs the Agent toward that execution pattern. ### Attack Path 1. An attacker supplies a crafted research question containing shell syntax, such as a command substitution expression. 2. The Agent follows the Skill instructions and places the original question inside the `--topic` argument. 3. The Agent submits the resulting command to a shell. 4. The shell evaluates the attacker-controlled syntax before invoking `save_research.py`. 5. The injected command executes with the same operating-system permissions as the Agent process. The search command can expose the same attack path if an attacker-controlled or insufficiently sanitized query is inserted into the doc ...[truncated 599 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/save_research.py:125
Finding

Path Traversal and Arbitrary Markdown File Overwrite Through Session IDs

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/literature.py:19
Finding

Crossref Contact Email Is Unnecessarily Disclosed to PubMed

Content
View full analysis
str: return os.environ.get("CROSSREF_MAILTO", "smart-research@openclaw.local").strip() def _fetch_json(url: str, headers: dict | None = None) -> dict: req = urllib.request.Request(url, headers={"Accept": "application/json", **(headers or {})}) req.add_header("User-Agent", f"SmartResearch/1.0 (mailto:{crossref_mailto()})") with urllib.request.urlopen(req, timeout=TIMEOUT) as resp: return json.loads(resp.read().decode()) def _fetch_text(url: str) -> str: req = urllib.request.Request(url) req.add_header("User-Agent", f"SmartResearch/1.0 (mailto:{crossref_mailto()})") with urllib.request.urlopen(req, timeout=TIMEOUT) as resp: return resp.read().decode() ``` The affected helper is used for PubMed requests at lines 80–92: ```python def search_pubmed(query: str, limit: int = 8) -> list[dict]: search_params = urllib.parse.urlencode( {"db": "pubmed", "term": query, "retmax": str(limit), "retmode": "json"} ) try: search_data = json.loads(_fetch_text(f"{PUBMED_SEARCH}?{search_params}")) except (urllib.error.URLError, json.JSONDecodeError, TimeoutError): return [] ids = (search_data.get("esearchresult") or {}).get("idlist") or [] if not ids: return [] summary_params = urllib.parse.urlencode({"db": "pubmed", "id": ",".join(ids), "retmode": "json"}) try: summary_data = json.loads(_fetch_text(f"{PUBMED_SUMMARY}?{summary_params}")) ``` ### Technical Analysis `CROSSREF_MAILTO` is documented as a Crossref contact email, and users are encouraged to configure it with their own address. However, `_fetch_text()` attaches that value to every PubMed request as part of the `User-Agent` header. The contact ema ...[truncated 1397 chars]
Remediation
View remediation
str: req = urllib.request.Request(url) req.add_header("User-Agent", "SmartResearch/1.0") with urllib.request.urlopen(req, timeout=TIMEOUT) as resp: return resp.read().decode() ``` - Prefer service-specific request helpers so privacy-sensitive headers cannot accidentally cross service boundaries. - Update the privacy and network documentation to identify the exact data sent to each external endpoint. - If a contact address is ever needed for another service, obtain explicit configuration and consent for that service rather than reusing `CROSSREF_MAILTO`. - Add tests asserting that PubMed requests never contain the configured Crossref email. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The implemented behavior is a backend-free literature search utility for Crossref and PubMed, which aligns partially with the declared literature retrieval aspect. However, the declared description emphasizes one-shot production of a structured academic report and explicitly references OpenClaw LLM support. This code contains no LLM calls, no summarization, no literature review synthesis, no gap/innovation analysis, and no recommendation generation. The primary implemented capability is narrower than advertised, so the description does not accurately represent the supplied code chunk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The changelog entry says the agent must output a full six-section report, and surrounding entries describe those sections entirely in Chinese, indicating a forced language/locale behavior. This can violate language-choice policy because no user opt-in or alternative language option is mentioned.

Content

No source excerpt is available for this finding.

Rp1

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.

Rp1

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.

Rp1

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.

Rp1

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.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill instructs use of network access, reading environment variables, and reading/writing files, but it does not declare any explicit tool scope or permissions boundary. That makes the effective privilege set opaque to users and reviewers, increasing the chance of over-broad access or accidental misuse if the runtime grants more capability than the task actually needs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill explicitly saves the user's original question and generated report into persistent session files without any user notice or consent flow. Research questions can contain sensitive business, medical, legal, or personal information, so silent retention creates a meaningful privacy and data-governance risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The writing rules state 中文输出;专有名词可保留英文, which forces a specific language for all outputs. This is a natural-language policy concern because it does not offer the user a language preference or explain a justified locale restriction.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation states that research results are saved under research/sessions/*.md, but it does not clearly warn users up front that prompts, summaries, and possibly sensitive research topics are persisted locally across sessions. This creates a privacy and data-handling risk because users may assume an interactive literature tool is ephemeral when it is actually writing potentially sensitive content to disk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The headings, prompts, and output examples are all written in Chinese, and there is no statement that users may interact in other languages. Under the language/locale policy, this can be a natural-language policy violation when a specific language is imposed without explicit opt-in or documented justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The file’s user-facing instructions and descriptions are entirely in Chinese, and there is no indication that this language choice is optional or justified by a region-specific context. 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.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/literature.py (reported line 14)May include surrounding context.

python
from concurrent.futures import ThreadPoolExecutor, as_completed

TIMEOUT = 12
CROSSREF_BASE = "https://api.crossref.org/works"
PUBMED_SEARCH = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
PUBMED_SUMMARY = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi"

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/literature.py (reported line 142)May include surrounding context.

python
if token in abstract:
            score += 1
    year = paper.get("year")
    if year and year >= __import__("datetime").date.today().year - 5:
        score += 1
    return score

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code emits user-facing markdown table headers in Chinese, and later sections also use Chinese headings. Because the file provides no opt-in, language selection, or justification for a Chinese-only locale, it violates the language/locale policy criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The report template is built with Chinese section names such as '结论摘要', '参考文献', and '研究建议'. This forces a specific language in produced artifacts for all users, with no visible mechanism to choose another language or a documented region-specific purpose.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The instruction says the agent 'must' output in Chinese, which imposes a fixed language policy on every use of the template. This is a natural-language policy violation because the file does not offer the user a language choice or explain a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This README is a markdown file, so vague trigger guidance applies. The example phrase "帮我做 CRISPR 领域的文献综述" is natural-language invocation guidance, but the documentation does not clarify trigger boundaries, exclusions, or whether similar everyday phrasing could unintentionally activate the skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

This markdown file demonstrates creating /tmp/papers.json and saving a full report via save_research.py, which affects local user/system data. The example does not include any warning that files will be written or persisted, so users may not realize the skill stores outputs on disk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This script sends the user-provided query to external services via search_literature(query, limit=args.limit) without any disclosure, confirmation, or warning in the CLI flow. While expected for a literature-search tool, queries may contain sensitive research topics, unpublished ideas, or personal/regulated data, so silent transmission creates a real privacy and data-handling risk.

Content

No source excerpt is available for this finding.