Back to skill

Security audit

OpenClaw Web Chat Pro

Security checks for vulnerabilities and agentic risk

Overview

This is a plausible web chat skill, but its production and authentication claims do not match the code, leaving private chats and AI execution exposed if deployed on a network.

Install only for local testing or behind strong network controls unless these issues are fixed. Before using it with sensitive chats, require real server-side authentication and session authorization, remove default/plaintext password handling, sanitize rendered Markdown, avoid exposing it on 0.0.0.0 by default, and review or remove the postinstall and systemd deployment steps.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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
Findings (6)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/server-v15.js:332
Finding

Authentication Bypass Exposes Chat History, Live Streams, and AI Execution

Content
View full analysis
{ const sessionId = req.params.sessionId; const history = chatHistory[sessionId] || []; res.json(history.slice(-200)); }); app.get('/api/session/:sessionId', (req, res) => { const sessionId = req.params.sessionId; const sessionConfig = config.sessions[sessionId] || {}; res.json({ sessionId, model: sessionConfig.model || 'qwen3.5-plus', createdAt: sessionConfig.createdAt, updatedAt: sessionConfig.updatedAt }); }); app.post('/api/auth/check', (req, res) => { const { password } = req.body; if (!password) { return res.status(400).json({ authenticated: false, error: '需要密码' }); } const authenticated = password === authConfig.password; res.json({ authenticated }); }); app.get('/api/export/:sessionId', (req, res) => { const sessionId = req.params.sessionId; const format = req.query.format || 'json'; const history = chatHistory[sessionId] || []; // Returns the selected history without an authorization check. }); app.post('/api/chat', (req, res) => { const { message, sessionId = 'web-chat', model } = req.body; // ... streamAI( message, sessionId, sessionModel, // ... ); }); io.on('connection', (socket) => { socket.on('join', (sessionId) => { const roomId = sessionId || 'web-chat'; socket.join(roomId); const history = chatHistory[sessionId] || []; socket.emit('history', history.slice(-200)); }); }); server.listen(PORT, '0.0.0.0', () => { // ... }); ``` ### Technical Analysis The password-check endpoint only returns a Boolean. It does not create an authenticated server-side session, issue a token, set a secure cookie, or establish an authorization context. None of ...[truncated 1728 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
src/public/index.html:595
Finding

Persistent Cross-Site Scripting Through Unsanitized Markdown Rendering

Content
View full analysis
{ console.log('[WS] Received streaming chunk'); if (!currentStreamingMessage) { currentStreamingMessage = appendMessage('assistant', '', Date.now(), true); } currentStreamingMessage.content += data.content; const contentEl = currentStreamingMessage.element.querySelector('.message-content'); if (contentEl) { contentEl.innerHTML = marked.parse(currentStreamingMessage.content) + ''; } }); function appendMessage( role, content, timestamp, streaming = false, model = null, stats = null, isWeb = false ) { const container = document.getElementById('messages'); const div = document.createElement('div'); div.className = `message ${role}${streaming ? ' streaming' : ''}`; const timeStr = new Date(timestamp).toLocaleTimeString(); const avatar = role === 'user' ? '👤' : '🤖'; const webBadge = isWeb ? '🌐 WEB' : ''; div.innerHTML = `
${avatar}
${streaming ? content : marked.parse(content)}
${timeStr} ${webBadge} ${!streaming && model ? createMessageMeta(model, stats, false) : ''}
`; container.appendChild(div); return { element: div, content: content }; } ``` ### Technical Analysis Chat and model content is passed through `marked.parse()` and inserted into the DOM with `innerHTML`. No HTML sanitizer or restrictive allowlist is applied. The same pattern is used for historical ...[truncated 1731 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
src/server-v15.js:108
Finding

Plaintext Password Storage and Public Default Credential

Content
View full analysis
{ const { password } = req.body; const authenticated = password === authConfig.password; res.json({ authenticated }); }); app.post('/api/auth/password', (req, res) => { const { oldPassword, newPassword } = req.body; if (oldPassword !== authConfig.password) { return res.status(401).json({ success: false, error: 'Original password incorrect' }); } authConfig.password = newPassword; saveAuth(authConfig); res.json({ success: true }); }); ``` ```js const savedPassword = sessionStorage.getItem('webchat_password'); if (savedAuth === 'true' && savedPassword) { const res = await fetch('/api/auth/check', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: savedPassword }) }); } if (data.authenticated) { sessionStorage.setItem('webchat_authenticated', 'true'); sessionStorage.setItem('webchat_password', password); } ``` ### Technical Analysis The service falls back to the publicly documented password `admin123`. Passwords are compared as plaintext and written directly into `chat-auth.json`. The main client also retains the password in `sessionStorage` to perform automatic reauthentication. No password-strength validation is ...[truncated 1199 chars]
Remediation
View remediation

T06 · System Persistence

Warning
Location
README.md:62
Finding

Privileged Cross-Session Persistence Through a systemd Service

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
src/public/index.html:7
Finding

Third-Party Browser Scripts Loaded Without Version Pinning or Integrity Verification

Content
View full analysis
``` The test page dynamically loads another remote script: ```js !function(n,t){ for(var e=3,r=n.createElement("canvas"), u=n.getElementsByTagName("script")[0],i=0;i
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
package.json:6
Finding

Installation Lifecycle Script Silently Writes Outside the Package Directory

Content
View full analysis
/dev/null || true" } ``` ### Technical Analysis Running `npm install` automatically invokes `postinstall`. This hook attempts to copy package-controlled web assets into a separate OpenClaw workspace component, outside the installed package directory. Errors and diagnostic output are discarded, and `|| true` forces a successful exit even when the copy fails. The supplied project does not contain a root-level `public` directory, so the command likely fails silently in this exact artifact. Nevertheless, the lifecycle behavior remains active and would overwrite external files if such a directory exists in another packaging context or later release. This exceeds the minimum privileges needed to install Node.js dependencies and is not disclosed in the installation instructions. ### Attack Path 1. The user follows the documented `npm install` command. 2. npm automatically executes the `postinstall` lifecycle hook. 3. If a root-level `public` source exists, its contents are recursively copied to `~/.openclaw/workspace/chat-web/public/`. 4. Existing assets in the destination may be replaced by package-controlled content. 5. The destination application subsequently serves or executes the overwritten assets. 6. Errors remain hidden, preventing the operator from reliably determining what changed. ### Impact Assessment The hook can modify another application under the current user's account and potentially introduce active browser content into that application. It does not request root access, so its direct scope is limited to fi ...[truncated 179 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (66)

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 · README.md (reported line 51)May include surrounding context.

bash
cd ~/.openclaw/workspace/skills/webchat-pro
npm install
cp .env.example .env
# 编辑 .env 配置密码和端口

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 · README.md (reported line 52)May include surrounding context.

cd ~/.openclaw/workspace/skills/webchat-pro npm install cp .env.example .env

编辑 .env 配置密码和端口

text

### 启动

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · src/public/index-v6.html (reported line 954)May include surrounding context.

html
</style>
</head>
<body>
  <!-- Auth Modal -->
  <div class="modal" id="authModal" role="dialog" aria-modal="true" aria-labelledby="authTitle">
    <div class="modal-content">
      <h2 id="authTitle">🔐 访问密码</h2>

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · src/public/index.html (reported line 440)May include surrounding context.

html
</head>
<body>
  <div class="container">
    <!-- Header -->
    <header class="header">
      <div class="header-left">
        <div class="logo">🌐 OpenClaw Chat</div>

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code stores the password in sessionStorage and reuses it for automatic re-authentication, exposing a plaintext secret to any script running in the origin. If an XSS issue or malicious third-party script is present, the password can be stolen and reused; the absence of user warning is secondary to the more serious insecure credential storage.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code claims to provide user authentication, but no authentication or session check protects sensitive routes such as /api/chat, /api/history/:sessionId, /api/export/:sessionId, /api/session/:sessionId, or the Socket.IO events. Any network client can access chat data, change models, join arbitrary rooms, and receive streamed responses, which makes the advertised protection misleading and leaves the application effectively unauthenticated.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

When the auth file is missing or unreadable, the server silently falls back to a hardcoded password of admin123, and even prints that default in startup output. This creates a predictable credential that attackers can guess immediately, especially since the service binds to 0.0.0.0 and appears intended for network access.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README presents these Pro capabilities as currently available in the product feature list, but later roadmap entries explicitly mark file upload, voice input/output, team collaboration, and API platform access as future work. This is an active contradiction in the skill's own documentation about what the codebase is supposed to do now versus later.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This markdown file advertises behaviors that can affect user data and privacy, including uploading documents, syncing data across devices, sharing conversations, exposing APIs, and exporting chat history. Under the markdown-specific SQP-2 criteria, the description should include a clear warning or disclosure about what user data may be stored, transmitted, or shared.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

npm start

systemd 服务

sudo systemctl enable openclaw-webchat sudo systemctl start openclaw-webchat

text

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

npm start

systemd 服务

sudo systemctl enable openclaw-webchat sudo systemctl start openclaw-webchat

text

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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 64)May include surrounding context.

npm start

systemd 服务

sudo systemctl enable openclaw-webchat sudo systemctl start openclaw-webchat

text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This is a markdown file, so missing user-facing warnings about behaviors that could affect user data or privacy should be flagged. The listed features imply collection, transmission, storage, or sharing of user content, but the document provides no disclosure about those risks or how data is handled.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The configuration example includes a hardcoded weak default password (admin123) and a wildcard CORS policy (ALLOWED_ORIGINS=*), both without warning. If copied into a real deployment, this can enable unauthorized access and broaden cross-origin attack exposure, especially for a web chat app that may handle sensitive prompts, files, or session data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The package description is written only in Chinese ("生产级 AI 聊天网页应用"), and feature labels/pricing text in the manifest are likewise Chinese-only. For a general-purpose chat web application manifest, this implies a fixed language/locale presentation without any stated opt-in, fallback, or region-specific justification.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The postinstall script performs filesystem side effects outside the package directory by copying files into ~/.openclaw/workspace/chat-web/public/. Install-time scripts execute automatically during dependency installation, so this gives the package the ability to modify a user workspace without explicit user consent, which is risky and not justified by a normal web app dependency manifest.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The description and author-facing metadata use Chinese-only natural language, and the file does not indicate that the skill is region-specific or that users can opt into another language. Under the language/locale policy, forcing a single language without user choice or justification is a policy concern.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

Server-provided chat content is inserted into the DOM with innerHTML after only replacing newlines, which does not prevent HTML or script-bearing markup from being interpreted. If an attacker can influence stored or reflected message content from the backend, this enables DOM-based XSS, session compromise, UI redress, or malicious requests in the victim's browser.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code sends user-entered chat content and a session identifier to the backend via a POST request, and also retrieves message history from the server. There is no visible warning, notice, or explanatory comment informing users that their messages are being transmitted to and stored by backend services.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Locally added chat messages are also rendered with innerHTML, so attacker-controlled input typed or injected into the client can become active markup immediately in the page. This makes self-XSS and any upstream input injection far more dangerous, and combined with message history rendering can become persistent or multi-user XSS if the backend stores and redistributes the content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This HTML/JS file stores a persistent session ID in localStorage and sends user messages plus that identifier to backend endpoints and a WebSocket connection. While the code logs activity to the developer console, it provides no user-facing notice in the UI, comments visible to users, or inline disclosure that conversations are stored/retrieved and transmitted to the server.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The document declares zh-CN as the page language, and the visible UI text is entirely in Chinese, with no indication that users can choose another language. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless clearly justified as region-specific.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The page generates and stores a persistent session ID in localStorage, then uses it in WebSocket and HTTP requests to load history and send chat content. While the code logs events to the console, there is no user-facing notice, prompt, comment, or inline disclosure that user messages and session-linked history will be transmitted to the server and retained across sessions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The document declares lang="zh-CN" and the visible UI strings throughout the page are fixed in Chinese, including labels, buttons, prompts, and status messages. This forces a specific locale on all users without any opt-in mechanism or documented justification that the skill is intended only for a Chinese-language or region-specific audience.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The login prompt explicitly reveals a default password ('admin123') in a UI described as a production version, which strongly suggests weak or shared default credentials may be accepted in deployment. If that password is valid or influences operator behavior, attackers can gain unauthorized access with minimal effort, and even if it is only informational, it normalizes insecure authentication practices and materially lowers attack cost.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/server-v15.js:227

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/public/index-v5.html:443

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/public/index-v6.html:1114

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/public/index.html:548