Back to skill

Security audit

AnyCrawl-API

Security checks for vulnerabilities and agentic risk

Overview

This AnyCrawl integration appears purpose-aligned, but it sends user crawl/search data to a third-party API and has under-disclosed credential and request-scoping risks.

Review before installing. Use a limited-scope AnyCrawl key, avoid placing secrets in shell profile files on shared or synced systems, and do not submit private/internal URLs or sensitive query content unless that external transfer is approved. The request-building bug should be fixed before relying on crawl status/results/cancel with untrusted tool arguments.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:195
Finding
Unescaped User-Controlled API Path and Query Components<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 195–234 **Vulnerability Type**: Authenticated API path and query injection **Risk Level**: Medium ### Vulnerable Code ```javascript export async function anycrawl_crawl_status({ job_id }) { if (!job_id) { throw new Error("Job ID is required"); } return await anycrawlRequest(`/crawl/${job_id}/status`); } /** * Get crawl results (paginated) * * @param {Object} params * @param {string} params.job_id - Crawl job ID (required) * @param {number} params.skip - Number of results to skip (default: 0) * * @returns {Promise<Object>} Crawled pages with content */ export async function anycrawl_crawl_results({ job_id, skip = 0 }) { if (!job_id) { throw new Error("Job ID is required"); } return await anycrawlRequest(`/crawl/${job_id}?skip=${skip}`); } /** * Cancel a crawl job * * @param {Object} params * @param {string} params.job_id - Crawl job ID (required) * * @returns {Promise<Object>} Cancellation confirmation */ export async function anycrawl_crawl_cancel({ job_id }) { if (!job_id) { throw new Error("Job ID is required"); } return await anycrawlRequest(`/crawl/${job_id}`, { method: "DELETE" }); } ``` ### Technical Analysis The `job_id` value is inserted directly into URL paths without validation or percent-encoding. The `skip` value is likewise interpolated directly into a query string without enforcing its documented numeric type. An attacker who can supply tool arguments may include path separators, traversal components, query delimiters, or fragment delimiters. When the resulting string is processed as a URL, these characters can alter the intended API path or query parameters rather than remaining part of a single job identifier. The risk is particularly significant in `anycrawl_crawl_cancel`, because the manipulated endpoint receives an authenticated `DELETE` request. The shared `anycrawlRequest` function automatically at ...[truncated 2183 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate `job_id` against the exact identifier format issued by AnyCrawl. If job IDs are UUIDs, use a strict UUID allowlist: ```javascript function validateJobId(jobId) { if (typeof jobId !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(jobId)) { throw new Error("Invalid Job ID"); } return jobId; } ``` 2. Percent-encode every value inserted into a path segment: ```javascript const encodedJobId = encodeURIComponent(validateJobId(job_id)); return await anycrawlRequest(`/crawl/${encodedJobId}/status`); ``` 3. Require `skip` to be a non-negative safe integer: ```javascript if (!Number.isSafeInteger(skip) || skip < 0) { throw new Error("Skip must be a non-negative safe integer"); } ``` 4. Construct query strings with `URLSearchParams` instead of string interpolation: ```javascript const encodedJobId = encodeURIComponent(validateJobId(job_id)); const query = new URLSearchParams({ skip: String(skip) }); return await anycrawlRequest(`/crawl/${encodedJobId}?${query.toString()}`); ``` 5. Add defense-in-depth checks inside `anycrawlRequest`, such as allowing only explicitly supported endpoint patterns and rejecting endpoint strings containing traversal components or fragments. 6. Add tests using malicious values containing `../`, `/`, `?`, `&`, `#`, and percent-encoded equivalents to verify that they are rejected or safely encoded. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
# AnyCrawl Skill

AnyCrawl API integration for OpenClaw - Scrape, Crawl, and Search web content with high-performance multi-threaded crawling.

## Setup

### Method 1: Environment variable (Recommended)

```bash
export ANYCRAWL_API_KEY="your-api-key"
```

Make it permanent by adding to `~/.bashrc` or `~/.zshrc`:
```bash
echo 'export ANYCRAWL_API_KEY="your-api-key"' >> ~/.bashrc
source ~/.bashrc
```

Get your API key at: https://anycrawl.dev

### Method 2: OpenClaw gateway config

```bash
openclaw config.patch --set ANYCRAWL_API_KEY="your-api-key"
```

## Functions

### 1. anycrawl_scrape

Scrape a single URL and convert to LLM-ready structured data.

**Parameters:**
- `url` (string, required): URL to scrape
- `engine` (string, optional): Scraping engine - `"cheerio"` (default), `"playwright"`, `"puppeteer"`
- `formats` (array, optional): Output formats - `["markdown"]`, `["html"]`, `["text"]`, `["json"]`, `["
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
# AnyCrawl Skill

AnyCrawl API integration for OpenClaw - Scrape, Crawl, and Search web content with high-performance multi-threaded crawling.

## Setup

### Method 1: Environment variable (Recommended)

```bash
export ANYCRAWL_API_KEY="your-api-key"
```

Make it permanent by adding to `~/.bashrc` or `~/.zshrc`:
```bash
echo 'export ANYCRAWL_API_KEY="your-api-key"' >> ~/.bashrc
source ~/.bashrc
```

Get your API key at: https://anycrawl.dev

### Method 2: OpenClaw gateway config

```bash
openclaw config.patch --set ANYCRAWL_API_KEY="your-api-key"
```

## Functions

### 1. anycrawl_scrape

Scrape a single URL and convert to LLM-ready structured data.

**Parameters:**
- `url` (string, required): URL to scrape
- `engine` (string, optional): Scraping engine - `"cheerio"` (default), `"playwright"`, `"puppeteer"`
- `formats` (array, optional): Output formats - `["markdown"]`, `["html"]`, `["text"]`, `["json"]`, `["
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill advertises search, scrape, and crawl features but does not warn users that their queries, URLs, and possibly fetched page contents are sent to a third-party AnyCrawl service. This creates a real privacy and data-handling risk because users may submit sensitive internal URLs, tokens in query strings, or proprietary search terms without informed consent.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The setup instructions recommend persisting the API key in shell startup files such as ~/.bashrc without warning that this stores the credential in plain text. Plain-text secrets in profile files are commonly exposed through backups, dotfile syncing, shared accounts, accidental commits, or local disclosure to other processes/users.

External Transmission

Medium
Category
Data Exfiltration
Content
// AnyCrawl Skill for OpenClaw
// API Docs: https://docs.anycrawl.dev
// API Base: https://api.anycrawl.dev/v1

const API_BASE = "https://api.anycrawl.dev/v1";
const API_KEY = process.env.ANYCRAWL_API_KEY;
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
// AnyCrawl Skill for OpenClaw
// API Docs: https://docs.anycrawl.dev
// API Base: https://api.anycrawl.dev/v1

const API_BASE = "https://api.anycrawl.dev/v1";
const API_KEY = process.env.ANYCRAWL_API_KEY;
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This skill forwards user-supplied URLs, search queries, and crawl parameters to the third-party AnyCrawl API, which creates a real data exposure boundary outside the local agent/runtime. While this appears to be the intended functionality of the integration rather than malicious behavior, the lack of explicit disclosure or consent means users may unknowingly send sensitive internal URLs, queries, or crawl targets to an external service.

Missing User Warnings

Low
Confidence
83% confidence
Finding
The code reads an API credential from the environment and uses it for outbound requests without any user-facing notice that a third-party credential is required and will be used. This is not credential exfiltration in the code shown, but it is still a transparency and trust issue because users may invoke the skill without understanding that their environment-provided secret authorizes external API activity.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
index.js:6