Back to skill

Security audit

Firecrawl Local

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent local Firecrawl wrapper, but its shell script handles the optional API key unsafely enough to warrant review before installation.

Install only if you trust and control the Firecrawl endpoint. Before using FIRECRAWL_API_KEY, the run.sh curl argument handling should be fixed to use a Bash array or otherwise pass the Authorization header as one quoted argument. Keep FIRECRAWL_LOCAL_URL on localhost unless you intentionally want scrape requests and the API key sent to another server.

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
run.sh:22
Finding

Unsafe Authentication Header Construction Causes Curl Argument Injection and Credential Exposure

Content
View full analysis

Vulnerability Details

File Location: run.sh, lines 22–34
Vulnerability Type: Unquoted command substitution and unsafe curl argument construction
Risk Level: Medium

Vulnerable Code

bash
auth_header() {
  if [ -n "$API_KEY" ]; then
    echo "-H" "Authorization: Bearer $API_KEY"
  fi
}

api_call() {
  local method="$1" endpoint="$2" body="$3"
  curl -s -X "$method" \
    "$LOCAL_URL$endpoint" \
    -H "Content-Type: application/json" \
    $(auth_header) \
    -d "$body"
}

Technical Analysis

The output of auth_header is inserted into the curl command through the unquoted command substitution $(auth_header). Bash consequently performs word splitting and pathname expansion on that output.

For example, a normal API key causes the intended argument:

text
Authorization: Bearer API_KEY

to be split into multiple command-line arguments resembling:

text
-H
Authorization:
Bearer
API_KEY

Curl treats Authorization: as the value of -H, while Bearer and the API key can be interpreted as additional URLs rather than as parts of the header. Authentication therefore becomes malformed even when the API key is benign.

If an attacker can influence FIRECRAWL_API_KEY, whitespace-delimited curl options or parameters embedded in the value may additionally modify curl's behavior. Shell metacharacters in the value are not reparsed as shell syntax, so this is not direct arbitrary shell-command execution; however, curl argument injection remains possible through word splitting.

Attack Path

  1. FIRECRAWL_API_KEY is configured with a normal secret or a value influenced by an attacker.
  2. A user or agent invokes the skill to scrape, map, or crawl a URL.
  3. auth_header prints the header option and API key as plain text.
  4. The unquoted $(auth_header) expression undergoes shell word splitting and pathname expansion.
  5. Curl receives the API ...[truncated 1240 chars]
Remediation
View remediation

Remediation Suggestions

Construct curl arguments using a Bash array so each option and value remains exactly one argument:

bash
api_call() {
  local method="$1" endpoint="$2" body="$3"
  local -a args

  args=(
    -s
    -X "$method"
    -H "Content-Type: application/json"
    -d "$body"
  )

  if [[ -n "$API_KEY" ]]; then
    args+=(-H "Authorization: Bearer $API_KEY")
  fi

  curl "${args[@]}" -- "$LOCAL_URL$endpoint"
}

Additional hardening measures should include:

  1. Remove auth_header, since emitting command-line arguments as text encourages unsafe reparsing.
  2. Keep every curl argument in an array and expand it with "${args[@]}".
  3. Place -- before the final URL to prevent URL values beginning with a hyphen from being interpreted as options.
  4. Restrict FIRECRAWL_LOCAL_URL to expected HTTP or HTTPS schemes and trusted hosts when remote endpoints are not required.
  5. Avoid printing the API key in diagnostics or error output.
  6. Add tests using keys containing spaces, wildcard characters, leading hyphens, and curl-like option text to verify that each key remains a single header value.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (4)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill invokes shell-based setup and execution behavior but does not declare any explicit tool scope such as permissions or allowed-tools. That creates an authorization ambiguity where an agent may treat shell access as implicitly permitted, increasing the chance of unintended command execution in response to routine scraping requests.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger guidance is extremely broad, including ordinary phrases like 'scrape this' or 'get content from X', which can cause the skill to activate in many normal conversations without clear user intent for local shell-backed scraping. In context, that is more dangerous because the skill assumes access to a localhost service and shell execution, so over-triggering can lead to unnecessary local network interaction or command use.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

The setup instructions write an executable script into a persistent per-user directory under ~/.openclaw/skills and mark it executable. Persistent installation is risky because it survives the current session, can be invoked later without renewed scrutiny, and may expand the lasting attack surface if the script is modified, replaced, or later triggered automatically.

Content

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

Setup (one-time)

bash
mkdir -p ~/.openclaw/skills/firecrawl-local
cp run.sh ~/.openclaw/skills/firecrawl-local/run.sh
chmod +x ~/.openclaw/skills/firecrawl-local/run.sh

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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 · run.sh (reported line 30)May include surrounding context.

sh
api_call() {
  local method="$1" endpoint="$2" body="$3"
  curl -s -X "$method" \
    "$LOCAL_URL$endpoint" \
    -H "Content-Type: application/json" \
    $(auth_header) \

Static analysis

No suspicious patterns detected.