Back to skill

Security audit

Academic Paper Fetcher

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it advertises, but it automatically uses Sci-Hub and saves unvalidated remote PDFs with under-scoped file-writing behavior.

Install only if you intentionally want an agent to contact Sci-Hub with DOI queries and save the returned files locally. Run it in a controlled workspace, explicitly set the output directory, avoid shared or privileged directories, and treat downloaded PDFs as untrusted files.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_paper.py:39
Finding

Predictable Output Files Allow Symlink-Based File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_paper.py, lines 39-42, 55-60, and 94-99
Vulnerability Type: Predictable temporary and output files with unsafe overwrite behavior
Risk Level: Medium

Vulnerable Code

python
# Create filename from DOI
safe_doi = doi.replace('/', '_').replace('\\', '_')
pdf_filename = f"paper_{safe_doi}.pdf"
pdf_path = output_path / pdf_filename
temp_html = output_path / "temp_scihub.html"
python
curl_cmd = [
    'curl', '-L', sci_hub_url,
    '-H', 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
    '-o', str(temp_html),
    '-s'
]
python
curl_cmd = [
    'curl', '-L', pdf_url,
    '-H', 'User-Agent: Mozilla/5.0',
    '-o', str(pdf_path),
    '-s'
]

Technical Analysis

The script stores intermediate HTML in the fixed filename temp_scihub.html and derives the final filename predictably from the user-supplied DOI. It then passes these paths to curl -o, which opens existing destinations for writing and can follow symbolic links.

The implementation does not verify whether either destination already exists, whether it is a symbolic link, or whether the output directory is controlled by another user. It also does not create files atomically with exclusive-create semantics. Consequently, execution in a shared or attacker-writable directory creates a time-of-check/time-of-use and symlink-following risk.

Replacing slash and backslash characters limits direct directory traversal through the DOI, but it does not prevent an attacker from predicting the resulting filename and creating that path before execution.

Attack Path

  1. The victim invokes the script with an output directory writable by an attacker, such as a shared workspace.
  2. The attacker predicts the fixed temp_scihub.html path or derives the final PDF filename from the DOI.
  3. The attacker creates a symbolic link at ...[truncated 956 chars]
Remediation
View remediation

Remediation Suggestions

  • Create temporary files with Python's tempfile.NamedTemporaryFile or tempfile.mkstemp in a private directory with restrictive permissions.
  • Do not use a fixed temporary filename inside a potentially shared output directory.
  • Reject existing symbolic links and non-regular destination files.
  • Create final output files using exclusive-create semantics, such as os.open with O_CREAT | O_EXCL | O_NOFOLLOW where supported.
  • Download into a secure temporary file, validate the content, and use an atomic rename to publish the final PDF.
  • Define an explicit overwrite policy. Require user confirmation or fail safely if the final filename already exists.
  • Ensure output directories are owned by the executing user and are not writable by untrusted accounts.
  • Use try/finally cleanup so securely created temporary files are removed on every error path.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_paper.py:94
Finding

Unbounded and Unvalidated Remote Downloads Can Cause Resource Exhaustion or Deceptive File Storage

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_paper.py, lines 94-116
Vulnerability Type: Unrestricted remote download without size, timeout, HTTP-status, or file-type validation
Risk Level: Medium

Vulnerable Code

python
curl_cmd = [
    'curl', '-L', pdf_url,
    '-H', 'User-Agent: Mozilla/5.0',
    '-o', str(pdf_path),
    '-s'
]

try:
    subprocess.run(curl_cmd, check=True, capture_output=True)
except subprocess.CalledProcessError as e:
    print(f"❌ Failed to download PDF: {e}")
    temp_html.unlink()
    return None

# Clean up temp file
temp_html.unlink()

# Verify PDF was downloaded
if pdf_path.exists() and pdf_path.stat().st_size > 0:
    size_kb = pdf_path.stat().st_size / 1024
    print(f"✅ Downloaded: {pdf_filename} ({size_kb:.1f} KB)")
    return pdf_path
else:
    print(f"❌ Download failed or file is empty")
    return None

Technical Analysis

The script follows redirects and writes the complete response body to disk without enforcing connection, transfer-time, or file-size limits. It also omits curl --fail, so HTTP error responses can be stored and accepted as successful downloads.

The only integrity check is that the resulting file exists and has a size greater than zero. The script does not validate the response content type, confirm the %PDF- file signature, parse the file safely, or ensure that redirects remain within an approved host set. A compromised remote service or malicious redirect destination can therefore return arbitrary nonempty content under a .pdf filename.

Because no maximum transfer size or total timeout is configured, a remote endpoint can send a very large response or sustain a slow transfer for an extended period.

Attack Path

  1. The script requests the Sci-Hub page and extracts a path treated as a PDF location.
  2. The remote service, a compromised endpoint, or a redirect target responds with an oversized ...[truncated 1024 chars]
Remediation
View remediation

Remediation Suggestions

  • Add curl --fail-with-body or equivalent HTTP-status handling so unsuccessful HTTP responses are rejected.
  • Configure connection and total transfer timeouts, for example --connect-timeout and --max-time.
  • Enforce a documented maximum download size. Check Content-Length when available and also enforce a streaming byte limit because the header may be absent or false.
  • Restrict redirects by validating every destination scheme and host, or disable redirects that leave an explicit allowlist.
  • Require HTTPS for all requests and redirected destinations.
  • Download into a secure temporary file rather than directly into the final destination.
  • Validate the response media type while treating it only as advisory.
  • Verify that the downloaded file begins with the PDF signature %PDF- and perform an appropriate structural validation before accepting it.
  • Delete partial or invalid downloads on every failure path.
  • Atomically rename the validated temporary file to its final filename only after all checks pass.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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 (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The declared behavior says files are saved under research/papers/ and that PubMed-related requests are supported, but the implemented behavior reportedly allows a configurable output directory that may default to the current directory and lacks PubMed handling. This mismatch is dangerous because users and downstream agents may trust the documented constraints while the skill writes files to unintended locations or behaves unpredictably.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill is explicitly built to use Sci-Hub, a piracy-oriented third-party service, which creates significant legal, compliance, and trust risk and encourages the agent to retrieve content from an untrusted source. In context, this makes the download-and-save behavior more dangerous because the agent is directed toward a site outside normal academic publisher or API channels.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill describes actions that require network access and filesystem writes, yet it declares no explicit tool scope or permission boundary. In an agent environment, this makes the skill harder to audit and can permit broader-than-expected tool use, especially since it performs automatic downloads from an external site and saves files locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill advertises automatic PDF downloads and local saves but does not prominently warn users that invoking it will contact a third-party site and write files into the workspace. In agentic workflows, lack of an explicit warning can lead to silent side effects, unexpected storage of untrusted files, and unsafe chaining with other tools that later process those PDFs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The workflow sends user-supplied DOIs to Sci-Hub without any privacy or integrity warning. This exposes user research interests to a third party and may retrieve untrusted or tampered content from a site outside the user's trust boundary, which is especially relevant because the skill then stores the file locally for later use.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script performs network access to a third-party site and writes downloaded content to local disk without any confirmation, trust prompt, or strong validation of what is being saved. In an agent-skill context, this is dangerous because a user may trigger unintended outbound requests and local file creation from untrusted remote content.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Using subprocess to invoke curl adds a command-execution capability that is not inherently required by a DOI-to-PDF fetcher. The manifest describes a downloader, but the code depends on spawning external programs twice, which is a broader and more powerful capability than the stated purpose justifies.

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

python
]
    
    try:
        subprocess.run(curl_cmd, check=True, capture_output=True)
    except subprocess.CalledProcessError as e:
        print(f"❌ Failed to fetch Sci-Hub page: {e}")
        return None

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

python
]
    
    try:
        subprocess.run(curl_cmd, check=True, capture_output=True)
    except subprocess.CalledProcessError as e:
        print(f"❌ Failed to fetch Sci-Hub page: {e}")
        return None

Static analysis

No suspicious patterns detected.