Back to skill

Security audit

academic-talon(学术利爪)

Security checks for vulnerabilities and agentic risk

Overview

This academic-paper skill is mostly purpose-aligned, but it exposes local PDFs and document contents with weak scoping that users should review before installing.

Review before installing. Use only on trusted machines, run GROBID locally, do not point GROBID_API_URL at an untrusted service, avoid public or shared-network PDF serving unless protected by authentication, and treat the documented sandbox claims as unreliable until path validation and symlink handling are fixed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/start_pdf_server.py:163
Finding

Unauthenticated PDF Server Binds to All Network Interfaces by Default

Content
View full analysis
3: host = sys.argv[3] else: # Default to all interfaces (accessible from any IP) host = "0.0.0.0" # Handle "内网" or "private" alias if host.lower() in ['内网', 'private', 'lan']: # Still listen on 0.0.0.0 for all interfaces # User can access from any private IP host = "0.0.0.0" start_server(port, host) ``` ### Technical Analysis The PDF server binds to `0.0.0.0` by default, making it reachable through every configured network interface. The `private`, `lan`, and equivalent alias also maps to `0.0.0.0`; it does not technically restrict traffic to private address ranges. The request handler does not implement authentication or per-document authorization. Disabling directory listings reduces casual discovery but does not prevent access to a known or guessed filename. Citation-key filenames are generated from predictable author, year, and title components, so they are not suitable as access-control tokens. Serving PDFs to other intranet devices is part of the declared optional functionality, but binding to all interfaces without authentication exceeds the minimum privilege required for local Zotero use. ### Attack Path 1. A user starts the server using the documented command or omits the host argument. 2. The server binds to all interfaces on the selected port. 3. A local-network attacker, or an internet attacker where firewall or N ...[truncated 584 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/start_pdf_server.py:56
Finding

Symlink Following Allows the PDF Server to Escape Its Document Root

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
skill.py:50
Finding

String-Prefix Path Validation Permits Access to Sibling Directories

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/pdf_analyzer.py:105
Finding

Arbitrary PDF URL Fetching Enables SSRF and Unbounded Disk Consumption

Content
View full analysis
Optional[str]: """Download PDF from URL to local file""" try: # Determine save directory if save_dir: if not os.path.exists(save_dir): os.makedirs(save_dir) target_dir = save_dir else: target_dir = PDF_DIR # Generate filename if not provided if not filename: if paper_info: citation_key = generate_bibtex_citation_key(paper_info) filename = f"{citation_key}.pdf" else: filename = os.path.basename(url) if not filename.endswith('.pdf'): filename = f"{filename}.pdf" filename = os.path.basename(filename) import re filename = re.sub(r'[^\w\.\-]', '_', filename) if not filename.endswith('.pdf'): filename = f"{filename}.pdf" pdf_path = os.path.abspath(os.path.join(target_dir, filename)) target_dir_abs = os.path.abspath(target_dir) if not pdf_path.startswith(target_dir_abs + os.sep): print(f"Error: filename escapes target directory sandbox: {filename}") return None print(f"Downloading PDF from {url} to {pdf_path}") response = requests.get(url, stream=True, timeout=30) response.raise_for_status() with open(pdf_path, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) print(f"PDF downloaded successfully: {pdf_path}") return pdf_path except Exception as e: print(f"Error downloading PDF: {e}") return None ``` ```python if pdf_in ...[truncated 1969 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Runtime Dependencies Are Installed Without Version or Integrity Pinning

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/search.py:121
Finding

arXiv Search Queries and Responses Use Plaintext HTTP

Content
View full analysis
1: search_query = " AND ".join([f"all:{term}" for term in query_terms]) else: search_query = f"all:{query}" params = { "search_query": search_query, "max_results": limit * 2, "sortBy": "submittedDate", "sortOrder": "descending" } try: res = requests.get(url, params=params, timeout=30) res.raise_for_status() ``` ### Technical Analysis The arXiv API request uses plaintext HTTP. Search terms are included in the query string, and the returned XML supplies metadata and links used by later workflows. An on-path observer can read the research query, while an active network attacker can modify the response because transport integrity is absent. No API key is sent in this request, but academic search terms may reveal confidential research topics, investigations, or business interests. ### Attack Path 1. A user performs an arXiv search while connected through an untrusted or compromised network. 2. The Skill sends the search query over plaintext HTTP. 3. A passive observer records the query and returned metadata, or an active attacker intercepts the response. 4. The active attacker modifies paper metadata or returned links. 5. A user or subsequent workflow may follow an attacker-controlled result or download URL. ### Impact Assessment The vulnerability exposes search-query confidentiality and allows response tampering. Modified responses can mislead research output and may direct later downloads to attacker-controlled content. The immediate request does not expose configured API credentials. ]]>
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (50)

Tainted flow: 'grobid_api_url' from os.getenv (line 29, credential/environment) → requests.get (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/pdf_analyzer.py (reported line 30)May include surrounding context.

python
try:
        # Get GROBID API URL from environment variable, default to http://localhost:8070/api
        grobid_api_url = os.getenv("GROBID_API_URL", "http://localhost:8070/api")
        response = requests.get(f"{grobid_api_url}/isalive", timeout=10)
        return response.status_code == 200 and response.text == "true"
    except Exception:
        return False

Tainted flow: 'grobid_url' from os.getenv (line 247, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

The code posts full PDF contents to a URL derived from GROBID_API_URL without validating that the endpoint is local or approved. If an attacker can influence environment configuration, sensitive local documents may be exfiltrated to an attacker-controlled server, which is especially significant in an academic assistant that processes potentially unpublished papers and private PDFs.

Content

Scanner excerpt · scripts/pdf_analyzer.py (reported line 272)May include surrounding context.

python
'consolidateCitations': '1'
            }
            # Set timeout to 5 minutes
            response = requests.post(grobid_url, files=files, data=data, timeout=300)
        
        print(f"Grobid response status code: {response.status_code}")

Tainted flow: 'params' from os.getenv (line 272, credential/environment) → requests.get (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/search.py (reported line 71)May include surrounding context.

python
headers["x-api-key"] = api_key

    try:
        res = requests.get(url, params=params, headers=headers, timeout=30)
        res.raise_for_status()
        data = res.json()

Tainted flow: 'params' from os.getenv (line 272, credential/environment) → requests.get (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/search.py (reported line 142)May include surrounding context.

python
}
    
    try:
        res = requests.get(url, params=params, timeout=30)
        res.raise_for_status()
        
        # Parse XML response

Tainted flow: 'params' from os.getenv (line 272, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The SerpAPI key is placed in the request query parameters, which can be logged by intermediaries, client debug output, or upstream infrastructure more readily than an authorization header. This increases the risk of credential exposure while transmitting searches to a third-party service.

Content

Scanner excerpt · scripts/search.py (reported line 282)May include surrounding context.

python
}
    
    try:
        res = requests.get(url, params=params, timeout=30)
        res.raise_for_status()
        data = res.json()

Tainted flow: 'payload' from os.getenv (line 390, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The Tavily API key is embedded in the JSON request body and transmitted to an external provider along with user queries. While expected for API use, this still expands external data exposure and increases credential-handling risk if request bodies are logged or mishandled.

Content

Scanner excerpt · scripts/search.py (reported line 403)May include surrounding context.

python
try:
        # Use POST request as recommended by Tavily API docs
        res = requests.post(url, json=payload, headers=headers, timeout=30)
        res.raise_for_status()
        
        try:

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The description suggests end-to-end parsing and citation generation, but the finding indicates the skill may only store provided BibTeX strings and build local URLs while relying on external services and credentials. Such misrepresentation can cause operators to trust the skill with sensitive PDFs and API keys without understanding what processing is local versus remote, especially given GROBID and Zotero data flows.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The description suggests end-to-end parsing and citation generation, but the finding indicates the skill may only store provided BibTeX strings and build local URLs while relying on external services and credentials. Such misrepresentation can cause operators to trust the skill with sensitive PDFs and API keys without understanding what processing is local versus remote, especially given GROBID and Zotero data flows.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The description suggests end-to-end parsing and citation generation, but the finding indicates the skill may only store provided BibTeX strings and build local URLs while relying on external services and credentials. Such misrepresentation can cause operators to trust the skill with sensitive PDFs and API keys without understanding what processing is local versus remote, especially given GROBID and Zotero data flows.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/pdf_analyzer.py (reported line 7)May include surrounding context.

python
from dotenv import load_dotenv
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/pdf_analyzer.py (reported line 12)May include surrounding context.

python
from dotenv import load_dotenv
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/search.py (reported line 6)May include surrounding context.

python
from dotenv import load_dotenv
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/search.py (reported line 11)May include surrounding context.

python
from dotenv import load_dotenv
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/start_pdf_server.py (reported line 149)May include surrounding context.

python
from dotenv import load_dotenv
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/zotero_archiver.py (reported line 13)May include surrounding context.

python
from dotenv import load_dotenv
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/zotero_archiver.py (reported line 18)May include surrounding context.

python
from dotenv import load_dotenv
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skill.py (reported line 19)May include surrounding context.

python
from dotenv import load_dotenv
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/pdf_analyzer.py (reported line 8)May include surrounding context.

python
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)
else:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/search.py (reported line 7)May include surrounding context.

python
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)
else:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

python
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)
else:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skill.py (reported line 15)May include surrounding context.

python
from typing import Dict, List, Optional

# Load environment variables from .env file
dotenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
if os.path.exists(dotenv_path):
    load_dotenv(dotenv_path)
else:

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/search.py (reported line 64)May include surrounding context.

python
"fields": "title,abstract,authors,year,venue,url,externalIds,publicationTypes,referenceCount,citationCount,influentialCitationCount"
    }

    # Get API key from environment variable
    api_key = os.getenv("SEMANTIC_SCHOLAR_API_KEY")
    headers = {}
    if api_key:

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/search.py (reported line 260)May include surrounding context.

python
"fields": "title,abstract,authors,year,venue,url,externalIds,publicationTypes,referenceCount,citationCount,influentialCitationCount"
    }

    # Get API key from environment variable
    api_key = os.getenv("SEMANTIC_SCHOLAR_API_KEY")
    headers = {}
    if api_key:

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/search.py (reported line 373)May include surrounding context.

python
"fields": "title,abstract,authors,year,venue,url,externalIds,publicationTypes,referenceCount,citationCount,influentialCitationCount"
    }

    # Get API key from environment variable
    api_key = os.getenv("SEMANTIC_SCHOLAR_API_KEY")
    headers = {}
    if api_key:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill advertises and documents capabilities requiring environment access, filesystem read/write, and network use, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an over-privileged/under-specified execution model where an agent may invoke the skill with broader capabilities than reviewers or policy expect, increasing the chance of unintended file, network, or secret access.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.