Back to skill

Security audit

paper-summary-json

Security checks for vulnerabilities and agentic risk

Overview

The skill generally does what it claims, but it handles untrusted paper URLs, document text, and generated reports with too little containment.

Review this skill before installing. Use it only with papers and URLs you trust, avoid internal or private-network URLs, expect local copies and reports to remain on the Desktop output path, and prefer a constrained workspace. The skill should add URL allowlisting or network blocking, size limits, manifest path containment, HTML sanitization, and stronger prompt boundaries for untrusted document text.

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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/prepare_papers.py:94
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis
Path: dest_dir.mkdir(parents=True, exist_ok=True) last_error: Exception | None = None for attempt in range(max_retries + 1): try: req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 paper-analysis-evidence"}) with urllib.request.urlopen(req, timeout=timeout) as resp: content_type = resp.headers.get("Content-Type") name = guess_download_name(url, content_type) dest = dest_dir / name if dest.exists(): stem, suffix = dest.stem, dest.suffix digest = hashlib.sha1(url.encode("utf-8")).hexdigest()[:8] dest = dest_dir / f"{stem}_{digest}{suffix}" with open(dest, "wb") as f: shutil.copyfileobj(resp, f) return dest ``` ### Technical Analysis The `url` value originates from the user-controlled `--urls` argument and is passed directly to `urllib.request.urlopen()`. The implementation does not: - Restrict URLs to approved HTTP or HTTPS schemes. - Reject loopback, private, link-local, multicast, or reserved IP addresses. - Resolve hostnames and validate all resulting addresses. - Validate redirect destinations. - Restrict requests to known paper repositories. - Prevent access to cloud instance metadata endpoints or internal services. Because the request originates from the machine running the Skill, an attacker can use the downloader as a network proxy into resources that may not be reachable from the attacker directly. ### Attack Path 1. An attacker supplies a URL that resolves to an internal service, loopback address, or cloud metadata endpoint. ...[truncated 1051 chars]
Remediation
View remediation

T01 · Skill Instruction Hijacking

Error
Location
scripts/prepare_papers.py:341
Finding

Untrusted Paper Content Is Embedded Directly into Instruction-Bearing LLM Prompts

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/render_report.py:233
Finding

Model-Controlled Markdown Can Produce Active HTML in Generated Reports

Content
View full analysis
str: data = parse_json_text(structured_json) verify = parse_json_text(verification_json) en = is_english(language) title = data.get("title", "Untitled" if en else "未识别题目") if en: return f"""# Paper Analysis ## Title {title} ## Task and Problem - Task: {data.get('task', '')} - Background: {data.get('background', '')} - Problem Statement: {data.get('problem_statement', '')} ## Method Overview - Method Name: {data.get('method_name', '')} - Method Core: {data.get('method_core', '')} ``` The resulting Markdown is converted to HTML without sanitizing the generated body: ```python def markdown_to_html(markdown: str, title: str = "Paper Analysis") -> str: try: import markdown as markdown_lib # type: ignore body = markdown_lib.markdown(markdown, extensions=["tables", "fenced_code", "sane_lists"]) ``` The generated body is then inserted into the HTML document: ```python {body} """ ``` ### Technical Analysis The structured and verification JSON files contain model-generated values derived from untrusted papers. Those values are interpolated into Markdown without escaping. When the optional `markdown` package is available, Python Markdown normally permits raw HTML contained in Markdown input. The resulting HTML body is not passed through an allowlist sanitizer before being embedded in `final_report.html`. Consequently, model output containing elements such as `
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/render_report.py:377
Finding

Manifest-Controlled Output Paths Permit Arbitrary File Overwrite

Content
View full analysis
dict[str, str]: structured = structured_path.read_text(encoding="utf-8", errors="ignore") verification = verification_path.read_text(encoding="utf-8", errors="ignore") markdown = build_markdown(structured, verification, language) output_md.parent.mkdir(parents=True, exist_ok=True) output_md.write_text(markdown, encoding="utf-8") output_html.write_text(markdown_to_html(markdown), encoding="utf-8") write_docx(markdown, output_docx) return {"markdown": str(output_md), "html": str(output_html), "docx": str(output_docx)} ``` Paths are loaded directly from the manifest without validation: ```python def render_manifest(manifest_path: Path) -> list[dict[str, str]]: manifest = json.loads(manifest_path.read_text(encoding="utf-8")) outputs: list[dict[str, str]] = [] language = manifest.get("language", "中文") for paper in manifest.get("papers", []): result = render_one( Path(paper["structured_json_file"]), Path(paper["verification_json_file"]), paper.get("language") or language, Path(paper["final_markdown_file"]), Path(paper["final_html_file"]), Path(paper["final_docx_file"]), ) outputs.append({"paper_id": paper.get("id", ""), **result}) return outputs ``` ### Technical Analysis The manifest is treated as trusted configuration even though it is selected using the command-line `--manifest` argument. Every input and output path is accepted as an unrestricted `Path`. There is no check that paths: - Are relative rather than ...[truncated 1776 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/prepare_papers.py:99
Finding

Unbounded Downloads and DOCX Expansion Permit Resource Exhaustion

Content
View full analysis
str: with zipfile.ZipFile(path) as zf: xml = zf.read("word/document.xml").decode("utf-8", errors="ignore") ``` ### Technical Analysis The request timeout controls how long individual network operations may wait, but it does not constrain the total number of downloaded bytes. A server can return a very large or indefinitely streamed response and consume available disk space. DOCX files are ZIP archives. Calling `ZipFile.read()` expands the complete `word/document.xml` entry into memory. The code does not inspect the declared uncompressed size, compressed size, compression ratio, or total expanded archive size. A crafted compression bomb can therefore consume excessive memory even if the downloaded file is relatively small. Subsequent PDF and text extraction also lacks explicit document, page, and extracted-text limits, increasing the denial-of-service surface. ### Attack Path 1. An attacker supplies a URL serving a very large response, a slow continuous stream, or a crafted DOCX compression bomb. 2. `download_url()` writes the entire response without tracking cumulat ...[truncated 778 chars]
Remediation
View remediation
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill directs the agent to read local files, write multiple output artifacts, access the network to download papers, and execute shell/Python commands, but it declares no explicit tool scope or permission boundaries. That creates unnecessary privilege ambiguity: a caller or runtime may permit broader operations than users expect, increasing the chance of unintended file access, persistent local writes, or network activity.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill explicitly downloads user-supplied URLs, copies local papers, and stores intermediate files plus final reports under the desktop, but it does not require clear user consent or warn that retained local copies will persist. This can expose sensitive paper contents, proprietary drafts, or metadata to other local users/processes and can surprise users who expected transient processing only.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file allows either Chinese or English output, but it forces a default of Chinese when unspecified. That is a natural-language policy concern because it imposes a language choice without explicit user opt-in.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The workflow documentation explicitly instructs the agent to download remote paper URLs and persist multiple artifacts under the user's Desktop, but it does not mention network access, local file creation, or consent requirements. In an agent setting, undocumented outbound requests and broad local writes can surprise users, leak metadata, or create privacy and storage risks, especially when processing untrusted URLs or sensitive papers.

Content

No source excerpt is available for this finding.

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

python
errors: list[str] = []
    for module_name in ("pypdf", "PyPDF2"):
        try:
            module = __import__(module_name)
            reader = module.PdfReader(str(path))
            pages = []
            for page in reader.pages:

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

python
except Exception as exc:
            errors.append(f"{module_name}: {exc}")
    try:
        completed = subprocess.run(
            ["pdftotext", str(path), "-"],
            check=True,
            stdout=subprocess.PIPE,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The templates explicitly require that all natural-language JSON content use {language}, and the CLI restricts that value to 中文 or 英文. This imposes a language policy on outputs without any user opt-in beyond those two choices, which can violate locale/language choice expectations for users needing other languages.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The file includes Chinese-only workflow naming and an error message string, but does not state that the skill is China/Chinese-specific or offer a language option. This can violate language/locale policy when a skill appears generally applicable but implicitly forces one language.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The generated HTML always sets <html lang="zh-CN">, even though the script supports both Chinese and English reports via --language and per-paper language settings. This forces a specific locale in the output without honoring the user's selected language, which is a natural-language locale policy issue.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.