Back to skill

Security audit

deep-scout

Security checks for vulnerabilities and agentic risk

Overview

This research skill largely matches its advertised purpose, but it needs review because it can fetch unrestricted URLs and leaves sensitive query state on disk without enforcing private permissions.

Install only if you are comfortable with a research skill sending queries and fetched page content through search, fetch, browser, Firecrawl, and LLM tooling. Avoid sensitive internal URLs or confidential research topics unless your runtime enforces safe network egress, and review/delete ~/.openclaw/state/deep-scout/state.json after use on shared machines.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/firecrawl-wrap.sh:21
Finding
Unrestricted HTTP(S) Targets Permit Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/firecrawl-wrap.sh:21-33`; related fetch instructions in `SKILL.md:97-114` **Vulnerability Type**: Server-Side Request Forgery caused by insufficient URL validation **Risk Level**: High ### Complete Code Snippet ```bash # Validate URL format (must start with http:// or https://) if [[ ! "$URL" =~ ^https?:// ]]; then echo '{"error": "Invalid URL: must start with http:// or https://"}' >&2 exit 1 fi # Run Firecrawl only when a bounded timeout command is available. TIMEOUT_BIN="$(command -v timeout 2>/dev/null || command -v gtimeout 2>/dev/null || true)" if [[ -z "$TIMEOUT_BIN" ]]; then echo "FIRECRAWL_UNAVAILABLE" exit 0 fi result=$("$TIMEOUT_BIN" 30 firecrawl scrape -- "$URL" --format markdown 2>/dev/null || echo "") ``` The related agent instructions apply similarly unrestricted URLs to all fetch tiers: ```text **Tier 1 — web_fetch (fast):** Call web_fetch(url) If content length >= 200 chars → accept, trim to max_chars_per_source **Tier 2 — Firecrawl (deep/JS):** If Tier 1 fails or returns < 200 chars: Run: bash "{baseDir}/scripts/firecrawl-wrap.sh" <url> <max_chars> If output != "FIRECRAWL_UNAVAILABLE" and != "FIRECRAWL_EMPTY" → accept **Tier 3 — Browser (last resort):** If Tier 2 fails: Call browser(action="open", url=url) Call browser(action="snapshot") ``` ### Technical Analysis The Firecrawl wrapper validates only whether a URL begins with `http://` or `https://`. It does not parse and validate the destination hostname or resolved address. Consequently, it does not reject: - IPv4 or IPv6 loopback addresses. - RFC 1918 private network addresses. - Link-local addresses. - Cloud metadata service addresses. - Reserved, multicast, or otherwise non-public destinations. - Public hostnames that resolve to non-public addresses. - Redirects from an initially public URL to a prohibited destination. The same untrusted URL is used by `web_fetch`, Firecrawl, and browser-based fallbac ...[truncated 1658 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse URLs with a standards-compliant URL parser instead of a shell regular expression. 2. Permit only `http` and `https`, and reject embedded credentials, malformed hosts, ambiguous numeric addresses, and unexpected ports. 3. Resolve every hostname before connecting and reject all loopback, private, link-local, multicast, reserved, and metadata-service address ranges for both IPv4 and IPv6. 4. Prevent DNS rebinding by connecting only to the validated resolved address while preserving the intended HTTP host and TLS server name. 5. Disable redirects or validate the destination of every redirect using the same rules. 6. Apply the same destination policy consistently to `web_fetch`, Firecrawl, and browser fetches. Validation solely inside the Firecrawl wrapper does not protect the other tiers. 7. Prefer an egress proxy that enforces public-Internet-only access independently of model output and script behavior. 8. Where practical, allow only URLs returned directly by the trusted search provider and reject URLs added or modified by the LLM. 9. Add tests covering IPv4, IPv6, alternative address encodings, user-info syntax, redirects, DNS rebinding, and cloud metadata endpoints. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run.sh:45
Finding
Research Queries Are Persisted Without Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.sh:45-47` and `scripts/run.sh:110-132` **Vulnerability Type**: Insecure local storage of potentially sensitive query data **Risk Level**: Medium ### Complete Code Snippet The state directory and file path are initialized without a restrictive `umask` or explicit permissions: ```bash STATE_DIR="${DEEP_SCOUT_STATE_DIR:-${HOME}/.openclaw/state/deep-scout}" mkdir -p "$STATE_DIR" STATE_FILE="${STATE_DIR}/state.json" ``` The potentially sensitive query is then persisted to that file: ```bash init_state() { cat > "$STATE_FILE" <<EOF { "query": $(echo "$QUERY" | python3 -c "import json,sys; print(json.dumps(sys.stdin.read().strip()))"), "config": { "depth": $DEPTH, "freshness": "$FRESHNESS", "country": "$COUNTRY", "language": "$LANGUAGE", "search_count": $SEARCH_COUNT, "min_score": $MIN_SCORE, "style": "$STYLE", "max_chars": $MAX_CHARS }, "stage": "init", "started_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "search_results": [], "filtered_urls": [], "fetched_content": {}, "report": null, "done": false } EOF } ``` ### Technical Analysis The script relies on the invoking process's existing `umask`. Under common defaults, `mkdir -p` may create the state directory with mode `0755`, while shell redirection may create `state.json` with mode `0644`. This can make the directory traversable and the state file readable by other local users. Research queries may contain confidential project names, business plans, personal names, incident details, or other sensitive information. The state is retained across invocations, but the implementation does not establish a retention period or automatic cleanup process. The configurable `DEEP_SCOUT_STATE_DIR` also receives no ownership, symlink, or trust-boundary validation. In adversarial local environments, this increases the possibility of writing through attacker-controlled paths. ### Attack Path 1. A user invokes ...[truncated 1108 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Set a restrictive process mask before creating any state: ```bash umask 077 ``` 2. Explicitly create and enforce the directory permissions: ```bash mkdir -p -- "$STATE_DIR" chmod 700 -- "$STATE_DIR" ``` 3. Create the state file atomically with mode `0600`, such as by using `mktemp` inside the protected directory, writing the content, and atomically renaming it. 4. Verify that the state directory and file are owned by the current user. 5. Reject symbolic links and avoid following pre-existing attacker-controlled filesystem objects. 6. Canonicalize and validate `DEEP_SCOUT_STATE_DIR` before use, particularly when its parent directory is writable by other users. 7. Document what data is persisted, how long it is retained, and how users can delete it. 8. Add an option to disable persistent state for sensitive research. 9. Remove state automatically after successful completion unless resumability is explicitly requested. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description describes an end-to-end deep intelligence pipeline that transforms a query into a structured research report with citations. The supplied code does not implement such a pipeline. It only performs one bounded fetch/scrape step for a provided URL using Firecrawl, with basic validation and fallback behavior when dependencies are unavailable. This is a materially narrower and different primary purpose than the declared multi-stage search/filter/fetch/synthesize system.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill describes use of network access, environment variables, file reads, and file writes, but it does not declare an explicit tool scope such as permissions or allowed-tools. That creates an avoidable trust gap: the runtime may grant broader capabilities than users expect, and the skill can process local and remote data without clear upfront limitation.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill sends user queries, search results, fetched page content, browser snapshots, and possibly URLs to multiple third-party services and tools, including web search, Firecrawl, browser automation, and LLM calls, without an explicit privacy warning. Users may unknowingly submit sensitive research topics, proprietary URLs, or extracted page content to external processors.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The config sets `language: "en"` as a fixed locale, which is a natural-language policy concern when no user opt-in or alternative language handling is documented in the file. This can impose a language constraint on all uses of the skill regardless of user preference.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script writes persistent state to `${HOME}/.openclaw/state/deep-scout` by default, which is outside the skill directory and occurs without an explicit user-facing notice or consent. Because the state includes the user query, pipeline configuration, URLs, fetched content, and final report material, sensitive research data may be stored in a broader, longer-lived location than the user expects, increasing confidentiality and privacy risk in shared or multi-tenant environments.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The options table sets `--language` default to `en`, which can impose an English-language default on search and synthesis behavior. The file does not explicitly frame this as a user-selectable preference or provide opt-in language guidance beyond the raw flag.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The skill documents writing the final report to an arbitrary user-specified file but does not warn about overwriting local files or restricting output paths. In an agent context, this can lead to accidental modification of important files if the path is mis-specified or manipulated through natural-language tasking.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The pipeline metadata explicitly sets the language to "en", which indicates a fixed locale choice in the skill output/configuration. The file does not provide any user opt-in, alternative language option, or documented justification for restricting output to English.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The prompt sets `Output language: {{language}}` and later instructs the model to `Write in {{language}}`, which imposes a locale/language constraint in natural language. The file does not indicate that this language is user-selected, optional, or justified as a region-specific requirement, so it may violate the language/locale policy criterion.

Static analysis

No suspicious patterns detected.