Back to skill

Security audit

Retraction Watcher

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate reference-checking purpose, but its URL fetching, file output, dependencies, and reporting failure modes are under-scoped enough that users should review it before installing.

Review before installing. Use it only in a sandbox or disposable workspace, avoid sensitive manuscripts unless third-party DOI/PMID lookups are acceptable, avoid --url except for trusted HTTPS sources, and set --output only to a safe report path. Treat a clean report as non-definitive until failed or unchecked lookups are separately accounted for.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:662
Finding

Unrestricted URL Retrieval Enables Server-Side Request Forgery and Local Resource Access

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py:662-669
Vulnerability Type: Server-Side Request Forgery (SSRF) and unsafe URL scheme handling
Risk Level: High

python
elif args.url:
    try:
        import urllib.request
        with urllib.request.urlopen(args.url, timeout=30) as response:
            text = response.read().decode('utf-8')
    except Exception as e:
        print(f"Error fetching URL: {e}", file=sys.stderr)
        sys.exit(1)

Technical Analysis

The value of args.url is supplied directly to urllib.request.urlopen() without validating its scheme, hostname, port, resolved IP address, or redirect destination. Python's URL handling may support non-HTTP schemes such as file:, while HTTP requests may target loopback, private, link-local, reserved, or cloud metadata addresses.

A timeout only limits request duration; it does not prevent access to unauthorized resources. Redirects are also followed without revalidating the destination. The response body is read without a size limit and is subsequently processed as attacker-selected document content.

Attack Path

  1. An attacker causes the Skill or its invoking Agent to process an attacker-controlled --url value.
  2. The attacker supplies a URL targeting a local file, loopback service, private network service, or cloud metadata endpoint.
  3. urllib.request.urlopen() retrieves the resource using the privileges and network access of the Skill process.
  4. The response is stored in text and passed into citation parsing and report generation.
  5. Depending on response structure and selected output format, extracted content or identifiers can be exposed through the report, console, or subsequent external API requests.

Impact Assessment

Successful exploitation can provide indirect read access to resources reachable by the process, including local files supported by URL handlers, internal HTTP services, and ins ...[truncated 303 chars]

Remediation
View remediation

Remediation Suggestions

  • Permit only https URLs and reject all other schemes, including file, ftp, and data.
  • Reject embedded credentials, non-approved ports, malformed hostnames, and ambiguous IP representations.
  • Resolve the hostname before connecting and block loopback, private, link-local, multicast, reserved, and unspecified address ranges for both IPv4 and IPv6.
  • Disable automatic redirects or validate the scheme, hostname, port, and resolved address after every redirect.
  • Consider using an explicit allowlist of trusted publication or bibliography hosts.
  • Apply strict response-size and download-time limits and validate the expected content type.
  • Run network retrieval in a sandbox with no access to local files, metadata services, or internal networks.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:250
Finding

Failed Retraction Lookups Are Incorrectly Reported as Clear

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py:250-265, 434-442, 464-466
Vulnerability Type: Fail-open error handling and integrity misclassification
Risk Level: High

python
try:
    req = urllib.request.Request(url, headers=headers)
    with urllib.request.urlopen(req, timeout=10) as response:
        data = response.read().decode('utf-8')
        return json.loads(data)
except HTTPError as e:
    if e.code == 404:
        return None
    print(f"  Warning: HTTP {e.code} for {url}", file=sys.stderr)
    return None
except (URLError, json.JSONDecodeError, TimeoutError) as e:
    print(f"  Warning: Request failed for {url}: {e}", file=sys.stderr)
    return None
python
for citation in self.citations:
    result = self.results.get(citation.index)
    if result:
        stats['checked'] += 1
        stats[result.status] = stats.get(result.status, 0) + 1
    else:
        stats['clear'] += 1
python
if total_issues == 0:
    lines.append("\n✅ No retraction issues found in your references!")

Technical Analysis

The request layer returns None for multiple semantically different outcomes: a successful negative result, HTTP errors, DNS or connection errors, timeouts, and invalid JSON responses. The reporting layer interprets the absence of a RetractionRecord as proof that a citation is clear.

As a result, the application cannot distinguish “checked and no issue found” from “not checked successfully.” The declared unknown statistic is initialized but is not used for these failures. Citations lacking usable identifiers are likewise classified as clear even though no authoritative lookup was completed.

Attack Path

  1. A user submits references for retraction verification.
  2. An attacker or environmental failure blocks, redirects, throttles, or corrupts responses from the external retraction services.
  3. _make_request() catches the fai ...[truncated 692 chars]
Remediation
View remediation

Remediation Suggestions

  • Introduce explicit result states such as clear, issue, unknown, and error.
  • Return structured request results that preserve HTTP status, source, and failure reason.
  • Mark a citation as clear only after at least one authoritative source successfully returns a definitive negative result.
  • Classify missing identifiers, timeouts, invalid responses, and unavailable services as unknown or unchecked.
  • Display lookup coverage and failed data sources prominently in every output format.
  • Prevent reports with incomplete checks from claiming that no issues were found.
  • Add bounded retries with exponential backoff for transient failures.
  • Add tests covering HTTP errors, malformed JSON, timeouts, missing identifiers, and total service outage.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/main.py:721
Finding

Unrestricted Output Path Allows Destructive File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py:721-724
Vulnerability Type: Arbitrary writable-file overwrite
Risk Level: Medium

python
if args.output:
    with open(args.output, 'w', encoding='utf-8') as f:
        f.write(report)
    print(f"\nReport written to: {args.output}")

Technical Analysis

The user-controlled output path is passed directly to open() in write mode. There is no workspace confinement, canonical-path validation, traversal prevention, symlink protection, or check for an existing file. Mode w truncates the destination before writing the report.

This behavior conflicts with the security checklist in SKILL.md, which states that the output directory should be restricted to the workspace.

Attack Path

  1. An attacker influences the arguments used to invoke the Skill.
  2. The attacker supplies an absolute path, traversal path, or symlink destination through --output.
  3. The process opens the resolved destination in w mode.
  4. Any existing content is truncated and replaced with the generated report.

Impact Assessment

The Skill can overwrite any file writable by its operating-system identity. Potential effects include project corruption, destruction of user data, modification of writable configuration files, and denial of service. This does not independently bypass operating-system permissions; its scope is bounded by the privileges of the Skill process.

Remediation
View remediation

Remediation Suggestions

  • Define a dedicated output directory and resolve both it and the requested destination to canonical paths.
  • Reject destinations outside the approved directory.
  • Reject absolute paths, traversal components, and symlink destinations.
  • Create files exclusively by default so existing files are not silently truncated.
  • Require an explicit overwrite option or confirmation before replacing an existing report.
  • Use race-resistant file APIs and restrictive permissions.
  • When practical, generate a safe server-side filename rather than accepting a complete destination path.

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unpinned and Unnecessary Runtime Dependencies Create Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-2
Vulnerability Type: Unpinned third-party dependencies
Risk Level: Medium

text
dataclasses
pypdf2

The documented installation path in SKILL.md:238-242 is:

text
# Python dependencies
pip install -r requirements.txt

Technical Analysis

Neither dependency is constrained to a reviewed version or protected by an integrity hash. Each installation may therefore resolve to a different package release from the configured package index. This prevents reproducible builds and allows unreviewed future package versions to enter the execution environment.

The project declares Python 3.10 or later, where dataclasses is part of the standard library. Installing the external backport is unnecessary for the declared baseline and expands the supply-chain attack surface.

The audit did not establish that either named package is currently malicious; the confirmed issue is unsafe dependency management.

Attack Path

  1. An operator follows the documented pip install -r requirements.txt command.
  2. The package resolver selects mutable versions from the configured package index.
  3. A compromised, malicious, or unexpectedly incompatible future release can be downloaded because no exact version or hash is required.
  4. Package installation or later import executes code from that unreviewed release with the installer or Skill process privileges.

Impact Assessment

A compromised dependency can execute code with the privileges of the Python installation or Skill runtime, potentially accessing the same files, environment variables, and network resources available to the process. The absence of exact versions also creates availability and compatibility risk even when no malicious package is involved.

Remediation
View remediation

Remediation Suggestions

  • Remove dataclasses because the declared Python 3.10+ runtime already provides it.
  • Pin PyPDF2 to an exact, reviewed version using its canonical package name.
  • Generate and enforce cryptographic hashes for all downloaded distributions.
  • Maintain a lock file or constraints file for reproducible installations.
  • Prefer an approved internal package mirror where available.
  • Audit dependency provenance and vulnerability advisories regularly.
  • Test and review dependency updates before changing the lock file.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared purpose is narrow, but the documented behavior is materially broader: it can fetch arbitrary URLs, process full local documents, write reports, and contact multiple external services. This mismatch can mislead users and runtime policy systems, enabling unexpected data ingestion and exfiltration under the guise of a simple reference-checking tool.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The top-level feature list explicitly states that the tool 'Falls back to title matching when identifiers missing'. In implementation, check_citation only queries Open Retractions and Crossref by DOI and PubMed by PMID, with no title-based lookup path at all.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill documents file read/write and network-capable behavior but does not declare an explicit tool scope such as permissions or allowed-tools. That creates ambiguity for enforcement and review, increasing the chance the agent can access files or make outbound requests beyond what users and operators expect.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger conditions use broad natural-language phrases like checking references or reviewing a manuscript without strong exclusion boundaries. Overbroad activation increases the chance the skill will run in contexts involving unrelated or sensitive documents, causing unnecessary file access or external lookups.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill states it performs external API/database lookups but does not clearly warn that user-supplied references, raw text, document-derived metadata, or URLs may be sent to third-party services. In a research setting, bibliography contents and manuscript references can be sensitive, so silent transmission creates confidentiality and compliance risks.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
78% confidence
Finding

The skill explicitly depends on external endpoints such as Crossref, PubMed, and Open Retractions, which means user-provided or document-derived data may leave the local environment. While external lookup is central to the feature, it still represents a genuine transmission surface that can expose sensitive research context if not minimized and disclosed.

Content

Scanner excerpt · SKILL.md (reported line 192)May include surrounding context.

md
## Data Sources

- **Retraction Watch Database**: https://retractionwatch.com/
- **Crossref API**: https://api.crossref.org/
- **PubMed E-utilities**: https://www.ncbi.nlm.nih.gov/home/develop/api/
- **Open Retractions**: https://openretractions.com/

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

python
if cache_key in self.cache:
            return self.cache[cache_key]
        
        url = f"https://api.crossref.org/works/{urllib.parse.quote(doi)}"
        data = self._make_request(url)
        
        if not data or 'message' not in data:

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest describes a skill focused on scanning document reference lists, which implies operating on provided document content. The code adds a --url mode that retrieves arbitrary remote content over the network and treats it as input, expanding behavior beyond simply scanning supplied references.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency is unpinned, so builds are not reproducible and may silently pull different releases over time. For a security-sensitive automation skill, this increases supply-chain risk because a newly published vulnerable or malicious version could be installed without review.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
dataclasses
pypdf2

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

PyPDF2 is unpinned, which allows unpredictable dependency resolution and may install a release with known parsing flaws. Because this skill scans document reference lists and likely processes untrusted PDFs, an unsafe or regressed parser version increases exposure to denial-of-service conditions and other parser bugs.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
dataclasses
pypdf2

Unverifiable Dependency: pypdf2 has 8 known advisory(ies) (CVE-2023-36464 (pypdf and PyPDF2 possible Infinite Loop when a comment isn't followed by a chara); CVE-2023-36807 (PyPDF2 vulnerable to possible Infinite Loop when reading malformed objects); CVE-2023-36810 (PyPDF2 quadratic runtime with malformed PDF missing xref marker) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The manifest references PyPDF2 without a version, and PyPDF2 has multiple known advisories including malformed-PDF infinite loop and quadratic-runtime issues. In this skill's context, where PDFs are likely attacker-controlled inputs, those flaws could be triggered to hang the process or exhaust resources during document scanning.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code file performs an outbound network request when the --url option is used, fetching remote document content via urllib. While the argument name hints at remote access, there is no explicit user-facing warning, confirmation, or descriptive log before the fetch that user-provided URLs will be contacted and data transmitted over the network.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.