Back to skill

Security audit

Web Gateway

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a legitimate OpenClaw web gateway, but it under-discloses persistent household memory, weak multi-user boundaries, network exposure, and location data flows.

Use only in a trusted local environment unless you first add authentication, bind to 127.0.0.1 by default, reject client-supplied system messages, prevent users from selecting other participants' identities, disable or make memory opt-in, confirm before using stored home/work locations, restrict any Google Maps browser key, and pin reviewed dependency versions.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
routes/state.py:8
Finding

Unauthenticated disclosure and modification of shared chat state

Content
View full analysis
Remediation
View remediation

T01 · Skill Instruction Hijacking

Error
Location
routes/chat.py:19
Finding

Client-supplied system messages are trusted and forwarded to OpenClaw

Content
View full analysis
list[dict]: user = canonical_user(user) messages = [{"role": "system", "content": get_prompt(user)}] for item in history or []: if not isinstance(item, dict): continue role = item.get("role") content = str(item.get("content") or "").strip() if role in {"system", "user", "assistant"} and content: messages.append({"role": role, "content": content}) if message: messages.append({"role": "user", "content": message}) return messages ``` ### Technical Analysis The `/api/chat` endpoint accepts a complete history from the caller. Both `normalize_history` and `build_messages` explicitly permit the privileged `system` role. Consequently, arbitrary callers can add system-level instructions after the application's own system prompt. Structural validation of a role name is not an authorization boundary. A ...[truncated 1766 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
routes/chat.py:398
Finding

Caller-controlled participant identity permits cross-user memory access

Content
View full analysis
str: memory_user = normalize_user_key(user) parts = [] user_context = memory.build_context_for_user(memory_user) if user_context: parts.append(user_context) memories = memory.search(query=message, limit=5) if memories: lines = [f"- {hit.subject} | {hit.field}: {hit.value}" for hit in memories] parts.append("Relevant persistent memory for this conversation:\n" + "\n".join(lines)) return "\n\n".join(parts).strip() ``` ### Technical Analysis The application treats a `user` value supplied in request JSON as the active participant. It also allows a recognized name prefix in the message to replace that identity. There is no authenticated principal, session binding, or authorizati ...[truncated 2084 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
static/js/ui.js:270
Finding

Private route locations and a browser API key are disclosed through Google Maps URLs

Content
View full analysis
``` ```javascript // static/js/ui.js:253-263 function setWindowFrame(win, url, title, launchUrl, extraHtml = "") { setWindowHtml(win, ``); const iframe = win.querySelector("iframe"); const timer = setTimeout(() => { try { const currentUrl = iframe?.contentWindow?.location?.href; if (!currentUrl || currentUrl === "about:blank") setWindowFallback(win, title, launchUrl, extraHtml); } catch (_err) { } }, 3000); iframe?.addEventListener("load", () => clearTimeout(timer)); } ``` ```javascript // static/js/ui.js:270-279 function buildGoogleMapsLaunchUrl(origin = "", destination = "") { if (!String(origin).trim() || !String(destination).trim()) return "https://www.google.com/maps"; return `https://www.google.com/maps/dir/?api=1&origin=${encodeURIComponent(origin)}&destination=${encodeURIComponent(destination)}`; } f ...[truncated 3329 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
Findings (26)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · config.py (reported line 12)May include surrounding context.

python
BASE_DIR = Path(__file__).resolve().parent
load_dotenv(BASE_DIR / ".env")


def _bool_env(name: str, default: bool = False) -> bool:

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code automatically memorizes conversational statements matching broad personal-profile patterns without any explicit memory command or user disclosure. Because these patterns include home, address, work, preferences, and activities, ordinary chat can silently become long-term profiling data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The fact store saves subject facts, source_user values, confidence, and change history to local JSON files, but the code provides no visible warning, prompt, or explanatory documentation about this persistence. Since this is a code file performing file writes of potentially sensitive memory data, the absence of any disclosure is a quality/safety issue under the rule.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code writes user profile data to disk, including profile fields, preferences, notes, and timestamps, but there is no confirmation prompt, visible logging/print, or explanatory comment/docstring disclosing that personal data is being persisted. Because the file itself performs safety-relevant data writes and contains no user-facing warning, it fits the missing-warning criterion for code files.

Content

No source excerpt is available for this finding.

External Transmission

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

python
"messages": build_messages(user=user, history=history, message=message),
    }

    response = requests.post(
        f"{OPENCLAW_BASE}/v1/chat/completions",
        headers=headers,
        json=payload,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code posts the assembled chat payload, including the user identifier and message/history content, to an external OpenClaw endpoint. There is no confirmation prompt, logging, comment, or other visible disclosure in this file warning that user-provided conversation data will be transmitted over the network.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The auto-memorization heuristics and structured-fact extractors target personal profile attributes, including address, home, work, interests, lessons, and activities, based on natural-language phrasing. This is dangerous because it silently converts normal conversation into persistent sensitive profiling without meaningful consent or strong necessity.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The route system prompt explicitly tells the model it may infer origin and destination from persistent memory or current-user context. In this application, that makes sensitive stored location data more likely to be surfaced in replies or used to generate route actions even without an explicit request for those exact values.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The handler persists personal facts such as home, work, address, likes, lessons, and activities into long-term memory, including via automatic memorization, without any visible consent, retention limit, or purpose restriction. This creates a user-profiling store of sensitive personal data that can later be surfaced to the model or used in downstream actions, increasing privacy and misuse risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

When messages begin with memory prefixes like 'remember that', the application immediately writes structured facts or notes to persistent memory and only afterward returns a success message. There is no prior warning, consent prompt, or review step, so users may not understand that personal information is being durably retained.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The route flow can infer origin and destination from stored home/work/address memory and trigger an open_route action even when the user did not explicitly provide those sensitive locations in the current request. That turns retained private location data into actionable outputs, which can expose or operationalize sensitive movements and places.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The application builds a memory context from stored data and sends it, together with the user message and history, to an external model call. Without clear disclosure, minimization, or filtering, personal facts may be transmitted to a third-party service beyond what users expect.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The POST /api/state endpoint accepts arbitrary JSON from any caller and writes it directly to persistent state with no authentication, authorization, validation, or schema checks visible in this file. This can allow unauthorized modification, corruption, or poisoning of application state, and if other parts of the application trust that state, the impact can expand into logic abuse or downstream security issues.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code sends the active user and full chat histories to /api/state via a POST request, but there is no visible confirmation prompt or user-facing notice indicating that conversations are being stored remotely. Because this involves transmitting potentially sensitive user message content, the lack of disclosure is a safety-relevant omission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Clicking the clearChat button immediately empties the active user's history and persists that change, making the deletion take effect without any confirmation step or warning. This is a destructive operation affecting user data, and the code provides no user disclosure before it happens.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The template injects googleMapsEmbedApiKey into window.GATEWAY_CONFIG, making the key available to any user of the page and to any script running in the browser. Even though Google Maps Embed keys are often intended for client-side use, exposing a broadly scoped or unrestricted API key can enable unauthorized reuse, quota exhaustion, or billing abuse if referrer and API restrictions are not tightly configured.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This code reads sensitive values such as OPENCLAW_TOKEN and GOOGLE_MAPS_EMBED_API_KEY from environment variables, which falls under access to credentials. There is no confirmation prompt, logging, comment, or docstring explaining this credential access behavior anywhere in the file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The configuration uses accented display names such as "Amélie" and "Théo", which encodes a specific locale-dependent representation in user-facing text. Under the policy provided, forcing a specific language or locale without opt-in or documented justification can be a natural-language policy issue.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency is specified with a lower bound only, which allows future unreviewed Flask releases to be installed and makes builds non-reproducible. This also prevents determining whether a deployed version is affected by known Flask advisories, increasing supply-chain and maintenance risk.

Content

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

text
Flask>=3.0.0
requests>=2.31.0
python-dotenv>=1.0.1

Unverifiable Dependency: Flask has 10 known advisory(ies) (CVE-2025-47278 (Flask uses fallback key instead of current signing key); CVE-2018-1000656 (Flask is vulnerable to Denial of Service via incorrect encoding of JSON data); CVE-2019-1010083 (Pallets Project Flask is vulnerable to Denial of Service via Unexpected memory u) +7 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

Flask has known advisories, and because the manifest does not pin a specific version, it is impossible to verify from this file whether the installed release is affected. The main risk here is uncertainty: deployments may resolve to insecure or inconsistent versions depending on install time and environment.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The requests package is not pinned to an exact version, so installations may resolve to different releases over time, including versions with newly introduced issues or incompatible behavior. This weakens reproducibility and makes it hard to verify whether known requests vulnerabilities are present.

Content

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

text
Flask>=3.0.0
requests>=2.31.0
python-dotenv>=1.0.1

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

Requests has multiple known advisories, and without version pinning there is no way to determine whether the environment will install a safe or affected release. This creates avoidable supply-chain uncertainty and complicates vulnerability management.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Using a minimum-version specifier for python-dotenv allows uncontrolled upgrades and prevents deterministic builds. That uncertainty can expose deployments to vulnerable or untested releases without an explicit review step.

Content

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

text
Flask>=3.0.0
requests>=2.31.0
python-dotenv>=1.0.1

Unverifiable Dependency: python-dotenv has 2 known advisory(ies) (CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding

python-dotenv has known advisories, and the unpinned requirement means the actual installed version cannot be verified from the manifest alone. That ambiguity increases the chance of deploying a vulnerable version and makes audits harder.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

This CSS file contains a natural-language comment in French: "Correction de la bulle "Jarvis réfléchit"". Under the stated policy, forcing a specific language without user opt-in can be a locale/language policy violation, and there is no indication here that the skill is intentionally region-specific or offers language choice.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
app.py:33

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
config.py:153