Back to skill

Security audit

OpenClaw Admin Main

Security checks for vulnerabilities and agentic risk

Overview

This is a real OpenClaw admin dashboard, but it exposes high-impact host control features with weak defaults and unsafe implementation choices that users should review before installing.

Install only in a tightly controlled local or isolated environment. Set strong AUTH_USERNAME and AUTH_PASSWORD before starting it, do not expose the backend port to a network, avoid running it as root/admin, and treat backups as secret-bearing because they may include .env. Review and fix the npm update endpoint, file API scoping, media auth, token storage, and vulnerable archive dependencies before production use.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (7)

T09 · Insecure Skill Coding Practices

Error
Location
server/index.js:376
Finding

Unauthenticated Command Injection in the Global Package Update Endpoint

Content
View full analysis
{ try { const { version } = req.body const packageSpec = version ? `openclaw@${version}` : 'openclaw@latest' console.log(`[Server] Updating OpenClaw via npm: ${packageSpec}`) // Execute npm update command const { execSync } = await import('child_process') const output = execSync(`npm install -g ${packageSpec}`, { encoding: 'utf8', timeout: 120000 }) ``` ### Technical Analysis The `/api/npm/update` route does not use `authMiddleware`. Therefore, any client that can reach the backend can invoke this privileged operation regardless of whether dashboard authentication is enabled. The request-controlled `version` value is embedded directly into a command string passed to `execSync`. Because `execSync` invokes a shell when given a string, shell metacharacters in `version` can alter the intended command and execute additional operating-system commands. The endpoint also performs a global npm installation, which may modify system-wide executable files and packages. Its effective privileges are those of the backend process and may be especially severe if the service runs as an administrator or root user. ### Attack Path 1. An attacker discovers or gains network access to the backend service. 2. The attacker sends a POST request to `/api/npm/update`. 3. The request body supplies a maliciously constructed `version` containing shell syntax. 4. The server concatenates that value into `npm install -g openclaw@...`. 5. `execSync` passes the resulting string to the system shell. 6. The injected command executes with the backend process's privileges. ### Impact Assessment Successful exploitation provides arbitrary command execution under the server account. An attacker ma ...[truncated 398 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server/index.js:174
Finding

Fail-Open Authentication Exposes an Interactive Host Shell

Content
View full analysis
{ const cols = parseInt(req.query.cols) || 120 const rows = parseInt(req.query.rows) || 36 const nodeId = req.query.nodeId || 'local' // ... try { const shell = process.platform === 'win32' ? 'powershell.exe' : process.env.SHELL || '/bin/bash' const ptyProcess = pty.spawn(shell, [], { name: 'xterm-256color', cols, rows, cwd: process.env.HOME || process.cwd(), env: { ...process.env, TERM: 'xterm-256color' } }) ``` Input is written directly to that shell: ```js app.post('/api/terminal/input', authMiddleware, (req, res) => { const { sessionId, data } = req.body if (!sessionId || !data) { return res.status(400).json({ ok: false, error: { message: 'sessionId and data are required' } }) } const session = terminalSessions.get(sessionId) if (!session) { return res.status(404).json({ ok: false ...[truncated 1820 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server/index.js:552
Finding

Attacker-Controlled Workspace Root Enables Arbitrary Filesystem Access

Content
View full analysis
{ try { let relPath = req.query.path || req.query.name const workspaceParam = req.query.workspace || '' const binary = req.query.binary === 'true' // ... const workspaceBase = expandHomePath(workspaceParam) const absPath = safePath(relPath, workspaceBase) // ... const content = readFileSync(absPath, 'utf-8') res.json({ ok: true, file: { name: basename(absPath), path: relPath, content, size: stats.size, updatedAtMs: stats.mtimeMs, extension: ext, } }) ``` The write endpoint uses the same request-controlled root: ```js app.post('/api/files/set', authMiddleware, async (req, res) => { try { const { path: relPath, name, content, workspace: workspaceParam } = req.body const filePath = relPa ...[truncated 2506 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
src/stores/auth.ts:4
Finding

Bearer Session Token Stored in Browser Local Storage

Content
View full analysis
{ const token = ref(localStorage.getItem(AUTH_TOKEN_KEY)) const authEnabled = ref(true) const loading = ref(false) const error = ref(null) const isAuthenticated = computed(() => !!token.value) function setToken(newToken: string | null) { token.value = newToken if (newToken) { localStorage.setItem(AUTH_TOKEN_KEY, newToken) } else { localStorage.removeItem(AUTH_TOKEN_KEY) } } ``` ### Technical Analysis The administrative bearer token is persisted in `localStorage`. Values in local storage are available to all JavaScript executing in the same origin and cannot be protected with the `HttpOnly` attribute. Any cross-site scripting flaw, compromised frontend dependency, malicious browser extension with suitable access, or injected same-origin script can retrieve the token and transmit it to an attacker. The token remains valid for up to 24 hours according to the backend session implementation, increasing its replay value. ### Attack Path 1. An attacker obtains script execution in the dashboard origin, such as through an XSS flaw or compromised frontend dependency. 2. The malicious script reads the `auth_token` local-storage entry. 3. The attacker copies the bearer token to another client. 4. The token is replayed against privileged API endpoints. 5. The attacker acts with the victim administrator's effective privileges until logout or expiration. ### Impact Assessment A stolen token may authorize: - Gateway configuration access. - Filesystem management. - Terminal and remote-desktop operations. - Backup creation, download, restoration, or deletion. - Arbitrary Gateway RPC requests exposed through the dashboard ...[truncated 78 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
src/api/device-identity.ts:4
Finding

Exportable Device Private Key Persisted in Browser Local Storage

Content
View full analysis
{ const privateKeyRaw = utils.randomSecretKey() const publicKeyRaw = await getPublicKeyAsync(privateKeyRaw) const deviceId = await fingerprintPublicKey(publicKeyRaw) return { deviceId, publicKey: base64UrlEncode(publicKeyRaw), privateKey: base64UrlEncode(privateKeyRaw), } } ``` ```ts export async function loadOrCreateDeviceIdentity(): Promise { try { const raw = localStorage.getItem(STORAGE_KEY) if (raw) { const parsed = JSON.parse(raw) as StoredIdentity if ( parsed?.version === 1 && typeof parsed.deviceId === 'string' && typeof parsed.publicKey === 'string' && typeof parsed.privateKey === 'string' ) { // ... return { deviceId: parsed.deviceId, publicKey: parsed.publicKey, privateKey: parsed.privateKey, } } } } catch { // ignore and regenerate } const identity = await generateIdentity() const stored: StoredIdentity = { version: 1, deviceId: identity.deviceId, publicKey: identity.publicKey, privateKey: identity.privateKey, createdAtMs: Date.now(), } localStorage.setItem(STORAGE_KEY, JSON.stringify(stored)) return identity } ``` ### Technical Analysis The Ed25519 private key is Base64URL-encoded and written directly to `localStorage`. Base64URL provides serialization only; it does not encrypt or otherwise protect the private key. All same-origin JavaScript can export this key. The issue therefore converts any fro ...[truncated 995 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/api/http-client.ts:53
Finding

Bearer Tokens Transmitted in Query Strings and Written to Console Logs

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
package-lock.json:68
Finding

Dependency Lockfile Uses Multiple Third-Party Package Mirrors

Content
View full analysis
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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (140)

Known Vulnerable Dependency: tar==6.2.1 — 12 advisory(ies): CVE-2026-59873 (node-tar: Decompression/parse DoS via unlimited input); CVE-2026-24842 (node-tar Vulnerable to Arbitrary File Creation/Overwrite via Hardlink Path Trave); CVE-2026-26960 (Arbitrary File Read/Write via Hardlink Target Escape Through Symlink Chain in no) +9 more

Critical
Category
Supply Chain
Confidence
95% confidence
Finding

The lockfile includes tar 6.2.1 under @mapbox/node-pre-gyp, and the listed advisories indicate real archive extraction issues including path traversal, hardlink abuse, and DoS. Even though this instance is an optional/transitive dependency rather than obvious application code, shipping a known vulnerable archive parser is dangerous because any code path that processes attacker-controlled tar content can lead to overwrite or resource exhaustion.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Critical
Category
Not specified by scanner
Confidence
99% confidence
Finding

The terminal streaming API spawns a real interactive shell (bash or PowerShell) on the host via node-pty and exposes input/output over HTTP endpoints. Any authenticated user can execute arbitrary OS commands with the server's privileges, which is effectively remote code execution on the host.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Critical
Category
Not specified by scanner
Confidence
98% confidence
Finding

The desktop APIs start capture/control tooling (Xvfb, x11vnc, ffmpeg, xdotool, PowerShell screen capture) and let clients stream the host desktop plus inject mouse and keyboard events. This grants authenticated users effective remote control of the host GUI, enabling theft of secrets, destructive actions, and lateral compromise through the operator's session.

Content

No source excerpt is available for this finding.

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.en.md (reported line 265)May include surrounding context.

初始化环境变量

bash
cp .env.example .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.en.md (reported line 438)May include surrounding context.

初始化环境变量

bash
cp .env.example .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 265)May include surrounding context.

初始化环境变量

bash
cp .env.example .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 438)May include surrounding context.

初始化环境变量

bash
cp .env.example .env

开发模式

Known Vulnerable Dependency: adm-zip==0.5.16 — 2 advisory(ies): CVE-2026-76845 (adm-zip extraction follows destination symlinks, allowing arbitrary file overwri); CVE-2026-39244 (adm-zip: Crafted ZIP file triggers 4GB memory allocation)

High
Category
Supply Chain
Confidence
94% confidence
Finding

adm-zip 0.5.16 is directly declared and the advisories describe realistic archive extraction issues such as following destination symlinks and memory exhaustion from crafted ZIPs. In an application that also includes upload/extraction-related packages, this is especially concerning because attacker-supplied archives are a common input path.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: brace-expansion==2.0.2 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
87% confidence
Finding

brace-expansion 2.0.2 has multiple DoS-style advisories around pathological expansion inputs, and this is a genuine vulnerable package version. However, it is a low-level transitive utility and exploitation generally requires attacker control over glob/brace patterns flowing into the library, so impact is lower unless untrusted patterns are accepted by the application or tooling.

Content

No source excerpt is available for this finding.

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.en.md (reported line 401)May include surrounding context.

md
"type": "module",
  "scripts": {
    "dev": "vite",
    "dev:server": "node --env-file=.env server/index.js",
    "dev:all": "concurrently -n \"frontend,backend\" -c \"blue,green\" \"vite\" \"node --env-file=.env server/index.js\"",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview",

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 401)May include surrounding context.

md
"type": "module",
  "scripts": {
    "dev": "vite",
    "dev:server": "node --env-file=.env server/index.js",
    "dev:all": "concurrently -n \"frontend,backend\" -c \"blue,green\" \"vite\" \"node --env-file=.env server/index.js\"",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview",

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 · package.json (reported line 9)May include surrounding context.

json
"type": "module",
  "scripts": {
    "dev": "vite",
    "dev:server": "node --env-file=.env server/index.js",
    "dev:all": "concurrently -n \"frontend,backend\" -c \"blue,green\" \"vite\" \"node --env-file=.env server/index.js\"",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview",

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 · package.json (reported line 10)May include surrounding context.

json
"type": "module",
  "scripts": {
    "dev": "vite",
    "dev:server": "node --env-file=.env server/index.js",
    "dev:all": "concurrently -n \"frontend,backend\" -c \"blue,green\" \"vite\" \"node --env-file=.env server/index.js\"",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview",

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 · package.json (reported line 13)May include surrounding context.

json
"type": "module",
  "scripts": {
    "dev": "vite",
    "dev:server": "node --env-file=.env server/index.js",
    "dev:all": "concurrently -n \"frontend,backend\" -c \"blue,green\" \"vite\" \"node --env-file=.env server/index.js\"",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview",

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 · server/index.js (reported line 2316)May include surrounding context.

js
"type": "module",
  "scripts": {
    "dev": "vite",
    "dev:server": "node --env-file=.env server/index.js",
    "dev:all": "concurrently -n \"frontend,backend\" -c \"blue,green\" \"vite\" \"node --env-file=.env server/index.js\"",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview",

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 · server/index.js (reported line 20)May include surrounding context.

js
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

const envPath = join(__dirname, '../.env')

function loadEnvConfig() {
  if (!existsSync(envPath)) {

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 · server/index.js (reported line 2423)May include surrounding context.

js
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

const envPath = join(__dirname, '../.env')

function loadEnvConfig() {
  if (!existsSync(envPath)) {

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The /api/npm/update endpoint is not protected by authMiddleware and executes npm install -g using a user-supplied version string interpolated into a shell command. This permits unauthenticated remote attackers to trigger package installation and likely command injection, resulting in immediate host compromise.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
94% confidence
Finding

The /api/rpc endpoint forwards arbitrary method and params directly to gateway.call with no method allowlist or argument validation. Even if protected by auth, this effectively exposes the full backend gateway surface to any authenticated user, enabling unintended privileged actions well beyond the apparent UI scope.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
93% confidence
Finding

The terminal streaming route creates a PTY running PowerShell or Bash, enabling arbitrary shell command execution on the host. While the code logs session creation to the server console, it provides no confirmation prompt, warning banner, or inline disclosure to the user about the high-impact nature of opening a full shell.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
90% confidence
Finding

The desktop session and input endpoints allow screen capture plus remote mouse and keyboard control of a display, which can materially affect system integrity and user data. The implementation contains operational logs but no confirmation prompt or explicit user disclosure before creating or controlling a desktop session.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The /api/media endpoint is unauthenticated and serves files from several host-local media directories derived from configuration and common home paths. That allows any network caller to retrieve locally stored media content and potentially enumerate sensitive artifacts, especially because the code searches multiple real host directories rather than a confined application-owned store.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The backup/restore functionality can execute system backup commands, ingest uploaded archives, restore OpenClaw state, and overwrite the application's .env and database files. This creates a powerful admin primitive that can alter authentication, redirect services, or replace persisted state, and its restore path relies on archive content that may be attacker-controlled.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
87% confidence
Finding

The backup creation logic explicitly copies the application's .env into the generated backup archive. Since .env commonly contains credentials and auth secrets, anyone able to create or download backups can obtain sensitive secrets and potentially take over the system or connected services.

Content

Scanner excerpt · server/index.js (reported line 2790)May include surrounding context.

js
updateBackupRecord(taskId, { status: 'running', progress: 50, message: task.message, stage: 'env_config' })

    if (existsSync(ENV_PATH)) {
      const tempEnvPath = join(tempDir, '.env')
      copyFileSync(ENV_PATH, tempEnvPath)
      filesToArchive.push({ path: tempEnvPath, name: '.env' })
      console.log('[Backup] Environment config added')

Credential Access

High
Category
Privilege Escalation
Confidence
87% confidence
Finding

Adding .env to filesToArchive causes sensitive configuration secrets to be embedded in distributable backup artifacts. This expands the blast radius of any backup disclosure and can expose authentication material for the app and upstream services.

Content

Scanner excerpt · server/index.js (reported line 2792)May include surrounding context.

js
if (existsSync(ENV_PATH)) {
      const tempEnvPath = join(tempDir, '.env')
      copyFileSync(ENV_PATH, tempEnvPath)
      filesToArchive.push({ path: tempEnvPath, name: '.env' })
      console.log('[Backup] Environment config added')
    }

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
server/index.js:385

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
server/index.js:456

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
server/gateway.js:144

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
server/index.js:41

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/api/device-identity.ts:84

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/utils/markdown.ts:534