Back to skill

Security audit

Crawl From X

Security checks for vulnerabilities and agentic risk

Overview

The skill’s X/Twitter crawling purpose is legitimate, but its media downloader can be driven by untrusted post text to make arbitrary HTTP requests and store unbounded data locally.

Review this before installing. Use a dedicated low-privilege X account, avoid running scheduled crawls on sensitive networks, treat exported Markdown as untrusted content, and add or request safeguards such as an X-media host allowlist, private-IP blocking, redirect validation, content-type checks, and download size quotas before crawling untrusted accounts.

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/media_downloader.py:25
Finding

Server-Side Request Forgery Through Attacker-Controlled Markdown Media URLs

Content
View full analysis

Vulnerability Details

File Location: scripts/media_downloader.py, lines 25-29 and 43-76
Vulnerability Type: Server-Side Request Forgery (SSRF) and unrestricted outbound request
Risk Level: High

Vulnerable Code

python
# Extract Markdown image format: ![alt](url)
markdown_img_pattern = r'!\[.*?\]\((https?://[^\)]+)\)'
matches = re.findall(markdown_img_pattern, content, re.IGNORECASE)
urls.extend(matches)
python
def download_media(self, url: str) -> Tuple[Optional[str], str]:
    """Download a media file and return (local path, original URL)."""
    try:
        # Clean URL by removing parameters such as name=orig
        clean_url = re.sub(r'[?\&]name=[^&\s\)\]]*', '', url)

        # Parse URL to obtain a filename
        parsed = urlparse(clean_url)
        path = parsed.path
        filename = path.split('/')[-1]

        # Add an extension if one is missing
        ext = os.path.splitext(filename)[1]
        if not ext:
            ext = '.jpg'
            filename = filename + ext

        # Generate a unique filename
        base_name = os.path.splitext(filename)[0] or 'image'
        counter = 1
        while os.path.exists(self.images_dir / filename):
            filename = f"{base_name}_{counter}{ext}"
            counter += 1

        filepath = self.images_dir / filename

        # Download the file
        response = requests.get(clean_url, stream=True, timeout=30)
        response.raise_for_status()

        with open(filepath, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)

Related data flow:

  • scripts/craw_hot.py, lines 535-558 and 607-627, inserts untrusted post text and media URLs directly into Markdown.
  • scripts/craw_hot.py, lines 1177-1183 and 1260-1274, automatically invokes MediaDownloader.process_markdown() after crawling.

...[truncated 3353 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not extract arbitrary Markdown image URLs from untrusted post text. Download only URLs taken from structured API media fields.
  2. Enforce an exact hostname allowlist, such as pbs.twimg.com and video.twimg.com, using parsed hostnames rather than suffix or substring checks.
  3. Permit HTTPS only.
  4. Resolve the hostname before connecting and reject every resolved loopback, private, link-local, multicast, unspecified, or reserved address.
  5. Disable redirects, or validate the scheme, hostname, port, and resolved addresses again at every redirect hop.
  6. Restrict destination ports to 443 unless another port is explicitly required.
  7. Verify that responses have an approved image or video media type before saving them.
  8. Consider running media retrieval in an isolated process or network namespace that cannot reach localhost, private networks, or metadata endpoints.
  9. Escape untrusted post text before embedding it in Markdown so that tweet text cannot create active images or links.
  10. Add tests covering direct private IPs, IPv6 loopback, alternate IP representations, DNS rebinding, and redirect-to-private-address cases.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/media_downloader.py:70
Finding

Unbounded Media Downloads Permit Disk and Bandwidth Exhaustion

Content
View full analysis

Vulnerability Details

File Location: scripts/media_downloader.py, lines 70-77 and 114-121
Vulnerability Type: Unrestricted file download and resource exhaustion
Risk Level: Medium

Vulnerable Code

python
response = requests.get(clean_url, stream=True, timeout=30)
response.raise_for_status()

with open(filepath, 'wb') as f:
    for chunk in response.iter_content(chunk_size=8192):
        if chunk:
            f.write(chunk)
python
downloaded_count = 0
for url in urls:
    local_path, original_url = self.download_media(url)
    if local_path:
        # Replace URL with local path
        content = content.replace(original_url, local_path)
        downloaded_count += 1

Technical Analysis

Streaming a response avoids holding the entire body in memory, but the implementation places no limit on:

  • The response Content-Length.
  • The number of bytes read from a chunked response.
  • The number of media URLs processed.
  • The total bytes downloaded during one crawl.
  • Available disk space.
  • The number or size of redirects.

The 30-second timeout is not a total download deadline in requests; a server that continues sending data within each read interval may keep the transfer active for substantially longer. Each response is written until the server closes it.

Because arbitrary Markdown image URLs can be introduced through crawled post text, a remote post author can direct the crawler to very large or indefinitely streamed resources. Even legitimate X media can consume substantial storage over repeated scheduled executions because no retention policy or aggregate quota is implemented.

Attack Path

  1. An attacker publishes one or more posts containing image syntax that references attacker-controlled URLs.
  2. The victim crawls the account.
  3. The URLs are copied into generated Markdown and automatically passed to the media downloader.
  4. The attacker's s ...[truncated 1002 chars]
Remediation
View remediation

Remediation Suggestions

  1. Reject responses whose declared Content-Length exceeds a conservative per-file limit.
  2. Maintain an independent byte counter while streaming and abort once the actual limit is exceeded, regardless of headers.
  3. Apply aggregate limits for file count and total bytes per crawl.
  4. Enforce a real overall transfer deadline in addition to connect and read timeouts.
  5. Download into a temporary file and atomically rename it only after all validations succeed.
  6. Delete partial files when a transfer fails or exceeds a limit.
  7. Verify media types and use server-independent, generated filenames with approved extensions.
  8. Check available disk space before and during a batch.
  9. Add retention and cleanup controls for scheduled use.
  10. Make automatic media downloading optional and require explicit user consent for untrusted accounts.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/craw_hot.py:439
Finding

Tracked Post URLs Are Disclosed to an Undocumented Third-Party API

Content
View full analysis

Vulnerability Details

File Location: scripts/craw_hot.py, lines 439-460
Vulnerability Type: Privacy-sensitive data disclosure to a third party
Risk Level: Low

Vulnerable Code

python
def _fetch_via_fxtwitter(self, url: str) -> Optional[Dict]:
    """Fetch content through the fxtwitter API."""
    api_url = re.sub(r'(x\.com|twitter\.com)', 'api.fxtwitter.com', url)
    try:
        resp = requests.get(
            api_url,
            headers=self.config.request_headers,
            timeout=30
        )
        if resp.status_code == 200:
            return resp.json()
        self.logger.warning(f"fxtwitter API returned {resp.status_code}")
    except Exception as e:
        self.logger.warning(f"fxtwitter error: {str(e)}")
    return None

def _fetch_via_syndication(self, tweet_id: str) -> Optional[Dict]:
    """Fetch content through the syndication API."""
    url = (
        "https://cdn.syndication.twimg.com/"
        f"tweet-result?id={tweet_id}&token=0"
    )
    try:
        resp = requests.get(
            url,
            headers=self.config.request_headers,
            timeout=20
        )

Technical Analysis

Every crawled X post URL is first transformed into a request to api.fxtwitter.com, a third-party service distinct from X/Twitter. This discloses the requested account path and post identifier to that service, along with the victim's source IP address and request metadata.

The audited request does not attach browser cookies, X credentials, authorization tokens, or environment secrets. Therefore, the finding is not credential exfiltration. The exposed information is the set of posts the user tracks and the timing and network origin of requests.

The Skill documentation states that content is retrieved “through API” but does not clearly identify FxTwitter as the first-choice third-party recipient or provide an opt-out. The official Twitter s ...[truncated 1161 chars]

Remediation
View remediation

Remediation Suggestions

  1. Clearly disclose FxTwitter as a third-party data recipient in README.md and SKILL.md.
  2. Obtain explicit user consent before enabling the third-party provider.
  3. Prefer the official syndication endpoint and make FxTwitter an opt-in fallback.
  4. Provide configuration that permits users to disable each external provider.
  5. Document exactly what metadata is transmitted and confirm that browser cookies and authentication headers are never forwarded.
  6. Minimize request logs and avoid logging full post URLs unless needed for diagnostics.
  7. For sensitive deployments, support a direct or self-hosted retrieval method.

T08 · Insecure Dependencies

Note
Location
README.md:80
Finding

Mutable Latest-Version Installation Instructions Weaken Supply-Chain Reproducibility

Content
View full analysis

Vulnerability Details

File Location: README.md, lines 80-84 and 104-108; SKILL.md, lines 17-20
Vulnerability Type: Unpinned package installation
Risk Level: Low

Vulnerable Code

bash
npx clawhub@latest install crawl-from-x

The README also instructs users to run:

bash
pip install -r requirements.txt

but the audited project structure contains no requirements.txt or lock file.

Technical Analysis

The @latest selector resolves to a mutable npm release at installation time. Future users may therefore execute a different installer version from the one used when the Skill was reviewed. The project also imports the external requests package without shipping a pinned dependency manifest.

No malicious dependency, typosquatted package, or remote code payload was identified in the audited artifact. The risk arises from non-reproducible installation and the inability to verify that later dependency versions match the reviewed environment.

Attack Path

  1. A user follows the documented installation command at a later date.
  2. The package registry resolves clawhub@latest to whatever release is current at that time.
  3. If that release is compromised, malicious, or incompatible, its installation logic executes under the invoking user's privileges.
  4. Separately, an operator may manually install an arbitrary current version of requests because no pinned manifest is provided.
  5. The resulting environment differs from the reviewed Skill and may introduce new supply-chain or compatibility risk.

Impact Assessment

The potential privilege scope equals that of the user running npx or pip. A compromised installer or dependency could theoretically read user-accessible data, modify files, or execute commands. However, this audit found no evidence that such compromise is currently present, so the issue is rated Low and concerns preventative supply-chain hardening.

Remediation
View remediation

Remediation Suggestions

  1. Pin the ClawHub CLI to a reviewed exact version instead of @latest.
  2. Where supported, verify the package integrity hash or signed provenance.
  3. Add a dependency manifest that pins requests to an approved version range.
  4. Provide a lock file with hashes for reproducible installation.
  5. Add automated dependency vulnerability scanning and a controlled update process.
  6. Ensure documentation references files that are actually included in the package.
  7. Publish checksums or signatures for reviewed Skill releases.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (71)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 283)May include surrounding context.

方案 1:等待当前实例完成

方案 2:删除锁文件(谨慎使用)

rm /path/to/.crawl-from-x.lock

text

### 浏览器连接失败

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The stated description frames the skill as a simple post-crawling/export tool, but the documented behavior extends to downloading and storing media locally, rewriting Markdown references, and controlling an external browser relay tied to a logged-in X account. This mismatch can mislead users about the true execution and data-handling scope, reducing informed consent and increasing the chance of sensitive account or local-data exposure.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The embedded post contains a prompt explicitly instructing an AI assistant to dump all stored memories, prior instructions, and personal context. In a skill that crawls and exports social posts, this is dangerous because downstream agents may ingest the Markdown and accidentally treat quoted tweet content as instructions, enabling prompt injection that could exfiltrate sensitive memory or system context.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The embedded natural-language prompt explicitly instructs a model to enumerate all stored memories, preferences, personal information, and prior context, which is a direct data-exfiltration pattern if later ingested or replayed by an agent. In a skill that crawls and exports social posts, preserving such prompt content without isolation increases the risk that downstream LLM tooling treats untrusted post text as executable instruction rather than inert data.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The embedded prompt explicitly instructs an AI assistant to dump all stored memories, prior instructions, and personal context, which could exfiltrate sensitive user data if copied into a live assistant session. In this skill's context—a tool that archives and republishes untrusted social content—preserving such prompts verbatim makes prompt-injection style data-harvesting content available to downstream agents or users, increasing the chance of privacy leakage.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

文档中嵌入了一段自然语言提示,明确要求AI导出其保存的全部用户记忆、个人信息、偏好、项目和历史上下文。这类提示可被后续读取该文档的代理或模型当作可执行指令,从而诱导泄露敏感上下文,形成典型的提示注入/数据外泄风险。

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The embedded text includes a prompt explicitly asking an AI system to dump all stored memories, personal data, prior instructions, and conversation context. In an agent or skill ecosystem, preserved third-party prompt content can become prompt-injection payload material if later reused by an LLM for summarization, indexing, or execution-adjacent tasks, potentially causing disclosure of sensitive memory or system context.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The embedded natural-language prompt explicitly asks an AI system to disclose all stored memories, prior-user context, personal details, and verbatim historical instructions. If this content is later ingested by an agent, summarizer, or memory-aware LLM without robust prompt isolation, it can trigger unauthorized disclosure of sensitive data from prior conversations or memory stores.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The captured tweet includes a prompt that explicitly asks an AI system to disclose all stored user memories, prior conversational context, preferences, and personal information for export. In a skill that aggregates untrusted social-media content, preserving and later presenting this prompt creates a prompt-injection/data-exfiltration hazard if downstream agents or users copy, replay, or automatically process the content as instructions rather than inert text.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

帖子中包含一段自然语言提示,明确要求模型导出其保存的全部用户记忆、上下文、偏好和个人信息,并尽可能逐字输出。这类内容若被下游代理、总结器或执行型 LLM误当作可遵循指令,可能诱导泄露会话记忆、系统偏好或其他敏感上下文;而该技能的场景是自动抓取并汇总社交媒体内容,正使这种‘不可信内容注入’更具现实风险。

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The embedded tweet contains a prompt explicitly instructing an AI assistant to reveal all stored memories, prior conversation context, personal details, preferences, and behavioral instructions. If this exported content is later ingested by another agent, summarizer, or automation pipeline without prompt/data separation, it can act as prompt-injection content and trigger unauthorized disclosure of sensitive user data.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file includes a prompt that explicitly instructs a model to disclose all stored memories, personal data, behavioral preferences, and prior conversation context. Even though it appears as quoted third-party social content, this is dangerous in an agent skill context because scraped prompt-like text can be copied, reused, or inadvertently treated as operational guidance, leading to privacy leakage or exfiltration of sensitive context.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This embedded prompt explicitly asks an AI to dump all stored memories and contextual information, including personal information, user preferences, and prior instructions, which is a direct data-exfiltration pattern. In the context of a crawl-and-export skill, the danger is increased because untrusted third-party content is being archived verbatim and may later be fed into downstream agents, creating a realistic indirect prompt injection path.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This post includes direct natural-language instructions to disclose all stored memories and contextual information about the user, including personal data and prior preferences. In the context of a content-crawling skill, preserving and potentially reusing this text creates a realistic indirect prompt injection risk: any later LLM that processes the markdown may be induced to leak sensitive memory or conversation state.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

This content contains an explicit prompt telling an AI to reveal all stored memories and contextual knowledge about the user, including personal information and response preferences, and to output it in a copyable code block. That is a direct data-exfiltration pattern: if a downstream LLM or automation pipeline ingests and follows embedded instructions from this markdown, it could disclose sensitive retained context or memory data.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This section includes a fully formed prompt telling an AI to reveal all stored memory, personal details, behavioral preferences, and prior context verbatim. In the context of a crawling/export skill, this is dangerous because archived third-party content may later be fed into an LLM-enabled workflow, where prompt injection could induce sensitive data disclosure from the model or connected memory systems.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The embedded prompt explicitly instructs a model to disclose all stored memories, personal information, behavioral preferences, and prior conversational context in a copyable format. If reused by an agent or surfaced to a model with memory access, it could trigger unauthorized disclosure of sensitive user data and system-derived context well beyond the immediate task.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The prompt is a direct request for a model to disclose all remembered user data, behavioral preferences, project history, and other retained context, which could expose sensitive personal and operational information. In the context of an agent skill that archives and may later process social posts, this becomes more dangerous because untrusted content can be replayed into downstream LLMs and trigger unintended disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The README explicitly instructs users to scrape using a browser that is already logged into X and to rely on Browser Relay, but it does not clearly explain the privacy and security implications of routing authenticated browsing actions through that relay. This can expose session-derived content, account-scoped data, and browsing activity beyond what users may expect, especially because the tool operates with an authenticated session.

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.

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.md (reported line 210)May include surrounding context.

bash
# 添加到 crontab,每天早上 8 点运行
crontab -e

# 添加以下行:
0 8 * * * cd /path/to/clawd && /usr/bin/python3 skills/crawl-from-x/scripts/craw_hot.py crawl

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill exposes capabilities that imply file read/write, network access, and shell execution, but it does not declare any tool scope or permissions boundaries in the manifest. This makes the effective trust boundary unclear and increases the risk of overprivileged execution, especially for a scraping skill that interacts with local files, browser services, and external networks.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding

Using npx clawhub@latest installs and executes whatever package version is current at runtime, creating a supply-chain risk if a malicious or compromised release is published. Because installation is performed through a package runner, users may execute unreviewed code with local system access before noticing any issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation does not clearly warn users that scraped X/Twitter content and media will be downloaded and stored locally. This omission can lead to unintentional retention of potentially sensitive, copyrighted, or policy-restricted data on disk, especially in shared or less secure environments.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.