Back to skill

Security audit

DeepReader

Security checks for vulnerabilities and agentic risk

Overview

DeepReader is a disclosed web-ingestion skill, but it needs review because it automatically fetches arbitrary links and saves untrusted web content into agent memory.

Install only if you are comfortable with automatic URL fetching and memory writes. Prefer using it in an environment with outbound network controls that block localhost, private networks, and cloud metadata services; review or clear saved memory entries after ingestion; and consider pinning dependencies before production use.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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

Error
Location
parsers/generic.py:64
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis
list[str]: """Return a deduplicated, order-preserved list of URLs found in *text*.""" seen: set[str] = set() urls: list[str] = [] for match in _URL_PATTERN.finditer(text): url = match.group(0).rstrip(".,;:!?)") # strip trailing punctuation if url not in seen: seen.add(url) urls.append(url) return urls ``` The generic parser then fetches that destination and follows redirects automatically: ```python def _fetch_html(self, url: str) -> str | None: """Download the raw HTML content of *url*.""" response = requests.get( url, headers=self._get_headers(), timeout=self.timeout, allow_redirects=True, ) response.raise_for_status() return response.text ``` ### Technical Analysis Any URL that is not classified as Twitter, Reddit, or YouTube is passed to `GenericParser`. The implementation does not resolve and inspect the destination address before making the request. Consequently, an attacker can supply URLs targeting: - Loopback interfaces such as `127.0.0.1` or `::1` - RFC1918 private networks - Link-local addresses - Cloud instance metadata services - Internal DNS names - Reserved or otherwise non-public address ranges Setting `allow_redirects=True` also permits a public endpoint to redirect the request to a private destination. No validation is performed after a redirect or after DNS resolution, leaving the implementation exposed to redirect-ba ...[truncated 2019 chars]
Remediation
View remediation

T02 · Agent Memory Poisoning

Warning
Location
__init__.py:104
Finding

Automatic Persistence of Attacker-Controlled Web Content Enables Agent Memory Poisoning

Content
View full analysis
{excerpt}") else: lines.append("*(To be filled by the Agent)*") lines.append("") # Content section lines.append("## Content") lines.append("") lines.append(result.content) lines.append("") return "\n".join(lines) ``` The generated document is written into the configured agent memory directory: ```python # Write to disk filepath.write_text(markdown, encoding="utf-8") logger.info("Saved content to %s (%d bytes)", filepath, len(markdown)) return str(filepath) ``` ### Technical Analysis The content returned by webpages, tweets, Reddit posts, comments, video descriptions, and transcripts is attacker-controlled. The implementation treats a successful network retrieval as sufficient authorization to place that content in the agent's persistent memory inbox. No mechanism: - Separates untrusted retrieved data from trusted agent instructions - ...[truncated 2567 chars]
Remediation
View remediation
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 (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description promises a broad reader for multiple specific platforms (X, Reddit, YouTube, and any webpage) that converts content into clean Markdown. The supplied code only covers a generic HTML webpage parsing path. It uses requests to download page HTML, trafilatura to extract text in TXT format, and BeautifulSoup heuristics as fallback. There is no evidence in this chunk of platform-specific handling for X, Reddit, or YouTube, and no transcript extraction logic. The output is explicitly configured as plain text rather than Markdown. While generic webpage extraction is consistent with part of the description ('any webpage'), the chunk materially underdelivers on the broader declared purpose, so this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The declared description presents a broad multi-source web reader covering X, Reddit, YouTube, and any webpage. The supplied code chunk, however, is narrowly scoped to YouTube only: it validates YouTube URLs, extracts a video ID, fetches YouTube metadata from the page, obtains transcripts using youtube_transcript_api, and formats the result. There is no evidence in this chunk of handling Twitter/X, Reddit, or generic webpages. While the YouTube portion is consistent with the description, the overall declared purpose is materially broader than the actual behavior shown here, so this chunk does not accurately represent the full declared capability.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises network fetching and writing ingested content to agent memory, but it declares no explicit tool scope or permission boundaries. In an agent environment, missing scope declarations can allow broader-than-expected network and file-write behavior, making review, policy enforcement, and least-privilege controls harder.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill states that it reads web content, but does not prominently warn users that fetched content is saved into agent memory as Markdown files. This creates a privacy and data-governance risk because sensitive, malicious, or irrelevant remote content may be persisted unexpectedly and later influence agent behavior.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Automatically triggering on any HTTP/HTTPS URL is overly broad and can cause the skill to fetch untrusted remote content without clear user intent. In an agent setting this raises the chance of prompt-injection exposure, unexpected network access, and silent persistence of attacker-controlled content into memory.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest describes DeepReader as a web content reader that reads social posts, webpages, and transcripts into clean Markdown. In this file, the skill not only formats content but creates directories and writes Markdown files into the agent's long-term memory/inbox, which is a persistence side effect beyond simple reading/parsing behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest advertises broad ingestion of webpages, social media, comments, and transcripts into agent memory but does not describe privacy boundaries, retention, or handling of potentially sensitive third-party content. In this context, omission of data-handling warnings is risky because the skill is explicitly designed for collection and persistence of external content, which can lead to accidental ingestion of personal, copyrighted, or confidential material.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The trigger regex matches any message containing any HTTP(S) URL, so the skill can auto-invoke on a very broad set of conversations unrelated to intentional scraping or ingestion. In a skill that reads webpages and writes extracted content into memory, this increases the chance of unneeded collection, processing, and storage of sensitive or private data from arbitrary links shared in chat.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This parser sends tweet-derived identifiers to a third-party service (api.fxtwitter.com) without any indication in this file that the user is informed or consented. Even if only the username and tweet ID are transmitted, that still leaks user-requested browsing targets and associated metadata to an external operator, which is a real privacy issue for an agent skill whose purpose is to ingest arbitrary links.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The hardcoded transmission target https://api.fxtwitter.com/ confirms that tweet requests are sent off-platform to an external service. In the context of a content-ingestion skill, this is risky because the agent may process sensitive or private investigative targets, and those access patterns become visible to the third-party API operator.

Content

Scanner excerpt · parsers/twitter.py (reported line 130)May include surrounding context.

python
self, original_url: str, username: str, tweet_id: str,
    ) -> ParseResult:
        """Fetch tweet via the FxTwitter public JSON API."""
        api_url = f"https://api.fxtwitter.com/{username}/status/{tweet_id}"

        max_attempts = 2
        last_error = ""

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The fallback logic sends requests to randomly selected public Nitter instances, meaning user-requested tweet paths are disclosed to unknown community-run servers. This increases privacy and trust risk beyond the primary backend because the destination varies, operators are not controlled, and reply-thread scraping may expose user activity patterns without any visible warning in this code.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The top-level docstring frames the component as responsible for formatting extracted content into Markdown with YAML frontmatter, which understates the material behavior implemented below: directory creation and file persistence to the agent's memory area. While not wildly inconsistent, the documentation omits and thereby misrepresents the key side effect of writing to long-term memory.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The default headers hard-code Accept-Language to en-US,en;q=0.9, which imposes a specific language/locale preference for all requests. This is a natural-language policy concern because the file does not offer user opt-in, configuration, or justification for forcing English.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The parser performs a network request to the provided YouTube URL via requests.get, which transmits the user's target URL and system request metadata to an external service. While the module docstring mentions fetching metadata and transcripts, there is no user-facing warning, confirmation, or explicit disclosure near the request itself about contacting YouTube.

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, so builds may resolve to different versions over time. That weakens supply-chain reproducibility and makes it harder to ensure a reviewed, non-vulnerable version is consistently installed.

Content

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

text
# DeepReader Skill - Dependencies
# Web scraping & content extraction
trafilatura>=1.12.0
requests>=2.31.0
lxml>=5.1.0
lxml-html-clean>=0.4.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
99% confidence
Finding

Using requests with only a minimum version allows environment-dependent resolution to potentially vulnerable or behavior-changing releases. In a web-ingestion skill, network-facing libraries are security-sensitive, so non-reproducible installs increase supply-chain and patch-verification risk.

Content

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

text
# DeepReader Skill - Dependencies
# Web scraping & content extraction
trafilatura>=1.12.0
requests>=2.31.0
lxml>=5.1.0
lxml-html-clean>=0.4.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
98% confidence
Finding

requests has multiple known advisories, and because the manifest does not pin a specific version, it is impossible to verify that deployed environments are using a fixed release. In this skill, requests is directly relevant because the tool fetches arbitrary remote content, increasing exposure to network-library flaws such as credential leakage or verification issues.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
99% confidence
Finding

lxml is a high-risk parser component and is specified without an exact version, so installations may vary and could include releases with parser or sanitizer flaws. Because this skill processes arbitrary remote HTML, parser security matters more here than in a non-networked context.

Content

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

text
# Web scraping & content extraction
trafilatura>=1.12.0
requests>=2.31.0
lxml>=5.1.0
lxml-html-clean>=0.4.0

# YouTube transcript extraction

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

lxml has a history of security issues, and the lack of version pinning means vulnerable parser/sanitizer behavior may still be present at deployment time. This is more dangerous in this skill because it ingests and parses untrusted web pages at scale, making malformed or malicious markup a realistic attack vector.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
99% confidence
Finding

lxml-html-clean is a sanitizer/cleaning component with only a minimum version constraint, leaving deployed versions non-deterministic. For a skill that cleans untrusted web content, sanitizer bypasses could allow unsafe content to survive filtering and affect downstream consumers.

Content

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

text
trafilatura>=1.12.0
requests>=2.31.0
lxml>=5.1.0
lxml-html-clean>=0.4.0

# YouTube transcript extraction
youtube-transcript-api>=0.6.2

Unverifiable Dependency: lxml-html-clean has 8 known advisory(ies) (CVE-2026-49825 (`lxml_html_clean.Cleaner` does not strip `javascript:` URLs from namespaced URL ); CVE-2024-52595 (HTML Cleaner allows crafted scripts in special contexts like svg or math to pass); CVE-2026-28348 (lxml-html-clean has CSS @import Filter Bypass via Unicode Escapes) +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
99% confidence
Finding

lxml-html-clean has known sanitizer bypass advisories, and without exact pinning there is no assurance the installed version contains the necessary fixes. Since the skill's purpose is to convert hostile web content into clean Markdown, sanitizer weaknesses are especially relevant and could allow script-bearing or deceptive content to survive processing.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The YouTube transcript library is not pinned, so the installed version may drift over time and introduce security or integrity issues without review. While less inherently risky than an HTML parser, it still expands the supply-chain attack surface.

Content

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

text
lxml-html-clean>=0.4.0

# YouTube transcript extraction
youtube-transcript-api>=0.6.2

# Data validation & settings
pydantic>=2.5.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

pydantic is specified with a lower bound only, making dependency resolution non-reproducible and complicating assurance that vulnerable releases are excluded. Validation libraries can also be exposed to denial-of-service or parsing issues when handling untrusted input.

Content

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

text
youtube-transcript-api>=0.6.2

# Data validation & settings
pydantic>=2.5.0
pydantic-settings>=2.1.0

# URL parsing & utilities

Unverifiable Dependency: pydantic has 4 known advisory(ies) (CVE-2021-29510 (Use of "infinity" as an input to datetime and date fields causes infinite loop i); CVE-2024-3772 (Pydantic regular expression denial of service); CVE-2021-29510 (Pydantic is a data validation and settings management using Python type hinting.) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
96% confidence
Finding

pydantic has known advisories and the unpinned requirement makes it unverifiable whether a safe version will be installed. While the likely consequence is lower than parser-library issues, vulnerable validation code can still enable denial of service or unexpected parsing behavior on attacker-controlled data.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

pydantic-settings is unpinned, which means configuration-related behavior can change across installs and vulnerable versions cannot be confidently excluded. Even if not directly internet-facing, configuration loaders can affect secrets handling and trusted boundaries.

Content

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

text
# Data validation & settings
pydantic>=2.5.0
pydantic-settings>=2.1.0

# URL parsing & utilities
tldextract>=5.1.0

Static analysis

No suspicious patterns detected.