Back to skill

Security audit

web-reader-pro

Security checks for vulnerabilities and agentic risk

Overview

This is a real web-reading skill, but it uses risky installation and fetching patterns that deserve careful review before use.

Install only in an isolated environment with limited filesystem and network access. Avoid using this on internal, signed, authenticated, or secret-bearing URLs unless you disable third-party routing and prevent internal-network fetches. Review or replace the Scrapling installer before running it, preferably with pinned package versions and no persistent npx wrapper.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/web_reader_pro.py:623
Finding

Unrestricted URL Fetching Enables SSRF and Disclosure of Sensitive URLs to Jina

Content
View full analysis
Tuple[str, str]: """ Extract using Jina Reader API. """ api_key = os.environ.get('JINA_API_KEY') if not api_key: raise ValueError("JINA_API_KEY not set") # Use Jina's reader API jina_url = f"https://r.jina.ai/{url}" headers = { 'Authorization': f'Bearer {api_key}', 'X-Return-Format': 'markdown', } response = requests.get(jina_url, headers=headers, timeout=30) response.raise_for_status() ``` ```python def _extract_webfetch(self, url: str) -> Tuple[str, str]: """ Extract using basic HTTP request + HTML parsing. Fallback method when other tiers fail. """ headers = { 'User-Agent': 'Mozilla/5.0 (compatible; WebReaderPro/1.0)', 'Accept': 'text/html,application/xhtml+xml', } response = requests.get(url, headers=headers, timeout=15) response.raise_for_status() ``` ### Technical Analysis The public fetching methods accept caller-controlled URLs without validating the URL scheme, hostname, resolved address, port, embedded credentials, or redirect destination. The direct WebFetch tier passes the URL directly to `requests.get()`. Consequently, the process can be induced to connect to loopback, private-network, link-local, or other reserved destinations reachable from its execution environment. Python Requests also follows redirects by default, so validating only an initial URL would not be sufficient. The Jina tier constructs `https://r.jina.ai/{url}` using the complete caller-provided value. This discloses the target URL to a third-party service. If the URL includes signed query parameters, capability tokens, session identifiers, private document paths, or other sensitive valu ...[truncated 2173 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Error
Location
scripts/install_scrapling.sh:117
Finding

Mutable npm Packages Are Downloaded and Executed Without Version or Integrity Pinning

Content
View full analysis
/dev/null; then log_info "Installing via npm (global)..." if npm install -g @scrapinghub/scrapling 2>/dev/null; then log_success "Scrapling installed successfully via npm!" elif npm install -g scrapling 2>/dev/null; then log_success "Scrapling installed successfully via npm!" else # Try alternative package name log_info "Trying alternative package..." npm install -g @scrapinghub/scrapling --force 2>/dev/null || \ npm install -g scrapling --force 2>/dev/null || true fi # Verify installation if command -v scrapling &> /dev/null; then log_success "Scrapling is now available!" return 0 fi fi # Try npx approach as fallback log_info "Trying npx as alternative..." if npx --yes scrapling --version &> /dev/null; then log_success "Scrapling available via npx" # Create a wrapper script create_scrapling_wrapper return 0 fi log_error "Failed to install Scrapling via npm/npx" return 1 } create_scrapling_wrapper() { log_info "Creating scrapling wrapper script..." WRAPPER_PATH="${INSTALL_DIR}/scrapling" mkdir -p "${INSTALL_DIR}" cat > "${WRAPPER_PATH}" << 'WRAPPER_EOF' #!/bin/bash # Scrapling wrapper script - calls npx scrapling exec npx --yes scrapling "$@" WRAPPER_EOF chmod +x "${WRAPPER_PATH}" log_success "Wrapper created at: ${WRAPPER_PATH}" } ``` ```bash verify_installation() { log_info "Verifying installation..." if command -v scrapling &> /dev/null; then ...[truncated 3229 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Open-Ended Python Dependency Constraints Produce Unreviewed and Non-Reproducible Installations

Content
View full analysis
=2.28.0 beautifulsoup4>=4.11.0 lxml>=4.9.0 markdownify>=0.11.0 # Caching diskcache>=5.4.0 # Utilities tqdm>=4.64.0 tenacity>=8.0.0 rich>=13.0.0 # Optional: for enhanced HTML parsing selectolax>=0.3.13 ``` ### Technical Analysis Every active dependency uses an open-ended lower-bound constraint. A command such as `pip install -r requirements.txt` can therefore install future releases that did not exist when the Skill was audited. Python packages execute code during installation, import, and normal operation. Consequently, compromised releases, dependency account takeovers, or newly introduced vulnerabilities can affect later installations without any change to this repository. The requirements also include utilities that were not shown as necessary in the audited implementation, such as `tqdm` and `rich`. Unnecessary dependencies enlarge the supply-chain attack surface. The audit did not establish that any currently available dependency version is malicious. The confirmed issue is the absence of reproducible version and integrity controls. ### Attack Path 1. A user follows the documented installation command: `pip install -r requirements.txt`. 2. pip resolves versions newer than the listed minimum versions. 3. A newly compromised, malicious, or vulnerable compatible release is selected. 4. Package installation hooks or imported package code execute with the privileges of the user running the Skill. 5. The package can access files, environment variables, and network resources available to that process. ### Impact Assessment A compromised dependency can execute arbitrary code under the installing or runtime user's account. This may expose API keys, cached web content, configuration files, and other user-accessible data. The issue do ...[truncated 260 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (34)

Tainted flow: 'headers' from os.environ.get (line 634, 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/web_reader_pro.py (reported line 639)May include surrounding context.

python
'X-Return-Format': 'markdown',
        }
        
        response = requests.get(jina_url, headers=headers, timeout=30)
        response.raise_for_status()
        
        content = response.text

Tainted flow: 'headers' from os.environ.get (line 634, 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/web_reader_pro.py (reported line 705)May include surrounding context.

python
'Accept': 'text/html,application/xhtml+xml',
        }
        
        response = requests.get(url, headers=headers, timeout=15)
        response.raise_for_status()
        
        html = response.text

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as a web-reading utility, but the documented installation flow includes package installation, dependency checks, executable wrapper creation, and PATH-related setup. That mismatch is dangerous because users may invoke or approve the skill expecting passive content extraction while it performs broader local system modification and shell-mediated environment setup.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as a web-reading utility, but the documented installation flow includes package installation, dependency checks, executable wrapper creation, and PATH-related setup. That mismatch is dangerous because users may invoke or approve the skill expecting passive content extraction while it performs broader local system modification and shell-mediated environment setup.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises capabilities that inherently require network, filesystem, environment-variable access, and shell execution, but it does not declare any explicit tool scope or permissions boundaries. In an agent environment, this can lead to over-privileged execution, where the runtime may grant broader access than users expect for a 'web reader' skill.

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 Tier 1 uses the Jina Reader API but does not clearly warn users that requested URLs and potentially retrieved page content may be transmitted to an external third-party service. This creates a privacy and data-handling risk, especially if users process sensitive internal URLs, authenticated resources, or regulated content under the assumption that extraction is local.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/install_scrapling.sh (reported line 83)May include surrounding context.

sh
echo ""
        echo "Please install Node.js first:"
        echo "  - macOS:    brew install node"
        echo "  - Ubuntu:   sudo apt install nodejs npm"
        echo "  - Windows:  Download from https://nodejs.org/"
        echo "  - Or visit: https://nodejs.org/ for installer"
        return 1

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
96% confidence
Finding

The script executes npx --yes scrapling --version without pinning a package version, causing retrieval of whatever package version is current at execution time. In an installer context this is risky because package compromise, typosquatting, or a malicious upstream release could lead to arbitrary code execution on the user's machine.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
89% confidence
Finding

Creating a persistent wrapper in ${HOME}/.local/bin/scrapling alters future command resolution and establishes long-lived behavior outside the immediate script run. In context, this persistence is especially concerning because the wrapper delegates to unpinned npx, extending the supply-chain risk to every later scrapling use.

Content

Scanner excerpt · scripts/install_scrapling.sh (reported line 144)May include surrounding context.

sh
if npx --yes scrapling --version &> /dev/null; then
        log_success "Scrapling available via npx"
        # Create a wrapper script
        create_scrapling_wrapper
        return 0
    fi

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding

This wrapper script permanently routes scrapling executions to npx --yes scrapling "$@", meaning every future invocation may fetch and run the latest remote package if not cached locally. That creates a persistent supply-chain execution path where a compromised or changed upstream package can execute arbitrary code later, beyond the initial install.

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
96% confidence
Finding

The script verifies functionality by executing npx --yes scrapling --version without version pinning, again allowing arbitrary upstream code to be fetched and run. In a web-scraping skill installer, this is more dangerous because the file's purpose is dependency bootstrapping, so users are likely to execute it with trust and little review.

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.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest describes a web content extraction skill with fallback fetch methods, but this implementation invokes a local external program via subprocess to perform one extraction tier. Spawning local executables is a broader host-execution capability than simple HTTP fetching/parsing and is not explicitly justified in the manifest text.

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

python
]
        
        try:
            result = subprocess.run(
                scrapling_cmd,
                capture_output=True,
                text=True,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The constructor writes a provided Jina API key into the process environment, which can unintentionally expand the secret's visibility to other libraries, subprocesses, or diagnostics within the same process. In this skill, that is more concerning because the code can invoke an external binary, increasing the chance that inherited environment variables expose the credential beyond its intended scope.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is specified with a lower bound only, which makes builds non-reproducible and can silently pull in different versions over time. In a web-scraping skill that processes untrusted remote content, this increases supply-chain risk and makes it hard to verify whether a vulnerable or incompatible release of requests is installed.

Content

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

text
# Core dependencies
requests>=2.28.0
beautifulsoup4>=4.11.0
lxml>=4.9.0
markdownify>=0.11.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 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

requests has multiple known advisories, and because the manifest does not pin a version, it is impossible to determine whether the installed release is affected. This is more concerning in a web-reader skill because requests handles attacker-controlled URLs and remote responses, making client-side HTTP flaws more relevant.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

beautifulsoup4 is unpinned, so installations may resolve to different releases over time. While not an immediate exploit by itself, unpinned parsing libraries in a skill that consumes arbitrary web content can introduce avoidable supply-chain exposure and unpredictable behavior.

Content

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

text
# Core dependencies
requests>=2.28.0
beautifulsoup4>=4.11.0
lxml>=4.9.0
markdownify>=0.11.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

lxml is a security-sensitive parser and is only constrained by a minimum version, leaving the actual installed release undefined. Because this skill parses untrusted HTML from the web, using an unpinned parser increases the chance of deploying a version with known parsing or sanitization flaws.

Content

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

text
# Core dependencies
requests>=2.28.0
beautifulsoup4>=4.11.0
lxml>=4.9.0
markdownify>=0.11.0

# Caching

Unverifiable Dependency: lxml has 14 known advisory(ies) (CVE-2021-43818 (lxml's HTML Cleaner allows crafted and SVG embedded scripts to pass through); CVE-2014-3146 (lxml Cross-site Scripting Via Control Characters); CVE-2021-28957 (lxml vulnerable to Cross-Site Scripting ) +11 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
98% confidence
Finding

lxml has a history of security advisories, and the unpinned requirement means the actual installed version may be vulnerable. Because this skill parses arbitrary remote HTML and possibly malformed content, parser vulnerabilities are more dangerous here than in a less exposed application.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

markdownify is unpinned, which undermines reproducibility and can cause vulnerable or unstable versions to be installed without notice. Since the skill converts web-sourced HTML into markdown, this dependency is part of the content-processing attack surface.

Content

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

text
requests>=2.28.0
beautifulsoup4>=4.11.0
lxml>=4.9.0
markdownify>=0.11.0

# Caching
diskcache>=5.4.0