Back to skill

Security audit

MyOpenClaw Backup Restore

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real backup and restore tool, but it handles credential-bearing backups through unsafe shell commands and an exposed HTTP server, so it should be reviewed carefully before use.

Only use this with backups you created and trust. Do not expose the HTTP server beyond localhost, do not transfer archives over plain HTTP, rotate any tokens exposed through a backup or server URL, and treat restore as equivalent to installing persistent code and credentials into ~/.openclaw.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/backup-restore.js:260
Finding
Shell Command Injection Through User-Controlled Archive and Backup Paths<![CDATA[ ## Vulnerability Details **File Location**: `scripts/backup-restore.js:260-307` **Related Locations**: `scripts/server.js:198-200`, `scripts/server.js:255-268` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```js function createArchive(sourceDir, archivePath) { const dirName = path.basename(sourceDir); const parentDir = path.dirname(sourceDir); if (hasTar()) { execSync(`tar -czf "${archivePath}" -C "${parentDir}" "${dirName}"`, { stdio: 'ignore' }); } else if (IS_WIN) { // Fallback: PowerShell Compress-Archive (creates zip) const zipPath = archivePath.replace(/\.tar\.gz$/, '.zip'); execSync( `powershell -NoProfile -Command "Compress-Archive -Path '${sourceDir}' -DestinationPath '${zipPath}' -CompressionLevel Optimal -Force"`, { stdio: 'ignore' } ); fs.renameSync(zipPath, archivePath); warn('Used ZIP format (tar not available). Archive is still cross-platform compatible.'); } else { error('tar is required but not found. Install it with your package manager.'); } } function extractArchive(archivePath, destDir) { fs.mkdirSync(destDir, { recursive: true }); if (hasTar()) { try { execSync(`tar -xzf "${archivePath}" -C "${destDir}"`, { stdio: 'ignore' }); return; } catch {} } if (IS_WIN) { try { execSync( `powershell -NoProfile -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${destDir}' -Force"`, { stdio: 'ignore' } ); return; } catch {} } else { try { execSync(`unzip -o "${archivePath}" -d "${destDir}"`, { stdio: 'ignore' }); return; } catch {} } error('Could not extract archive. Ensure tar or zip tools are available.'); } ``` The HTTP server also constructs shell commands from configured paths and uploaded filenames: ```js const out = execSync( `node "${BACKUP_JS}" backup --output-dir "${BACKUP_DIR}"`, { encoding: 'utf8', timeout: ...[truncated 2316 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace every shell-string invocation with an executable and an argument array: ```js const { execFileSync } = require('child_process'); execFileSync('tar', [ '-czf', archivePath, '-C', parentDir, dirName, ], { stdio: 'ignore' }); ``` 2. Invoke Node directly without a shell: ```js execFileSync(process.execPath, [ BACKUP_JS, 'restore', filePath, '--dry-run', ], { encoding: 'utf8', timeout: 180000, }); ``` 3. Do not use `echo yes | ...`. Add a non-interactive restore flag intended for the server, and require the server to perform its own authorization and confirmation checks before passing that flag. 4. Apply a conservative filename policy, for example: ```js const SAFE_ARCHIVE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}\.(?:tar\.gz|zip)$/; ``` 5. Resolve all configured and requested paths with `path.resolve()` and verify that they remain under the intended backup directory using `path.relative()`. 6. On Windows, invoke PowerShell through `execFileSync()` with separately supplied arguments, or use a Node archive library that does not require command construction. 7. Add regression tests using filenames containing quotes, semicolons, command substitutions, spaces, Unicode characters, and path separators. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/server.js:129
Finding
Stored Cross-Site Scripting Through Uploaded Archive Filenames<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.js:129-165` **Related Location**: `scripts/ui.html:91-95` **Vulnerability Type**: Stored cross-site scripting **Risk Level**: High ### Vulnerable Code ```js const headerStr = body.slice(start, headerEnd).toString('utf8'); const fnm = headerStr.match(/filename="([^"]+)"/); if (!fnm) return reject(new Error('No filename in multipart')); const filename = path.basename(fnm[1]); // sanitize const dataStart = headerEnd + 4; // Find end boundary const endBoundary = Buffer.from('\r\n--' + boundary); let dataEnd = body.indexOf(endBoundary, dataStart); if (dataEnd === -1) dataEnd = body.length; resolve({ filename, data: body.slice(dataStart, dataEnd) }); ``` The filename is then placed directly into HTML text and attributes: ```js function serveUI(req, res) { const uiPath = path.join(SKILL_DIR, 'scripts', 'ui.html'); let html = fs.existsSync(uiPath) ? fs.readFileSync(uiPath, 'utf8') : '<h1>UI not found</h1>'; const backups = listBackups(); const rows = backups.map(b => `<tr><td><span class="tag">${b.filename.endsWith('.zip') ? 'zip' : 'tar.gz'}</span> ${b.filename}</td>` + `<td>${b.sizeHuman}</td>` + `<td>${new Date(b.createdAt).toLocaleString()}</td>` + `<td class="actions"><a class="btn btn-gray" href="${b.downloadUrl}" download>Download</a>` + `<button class="btn btn-success restore-btn" data-file="${b.filename}">👁 Dry Run</button>` + `<button class="btn btn-danger restore-btn" data-file="${b.filename}" data-confirm="1">♻️ Restore</button></td></tr>` ).join(''); html = html .replace('{{TOKEN}}', TOKEN) .replace('{{BACKUP_COUNT}}', String(backups.length)) .replace('{{BACKUP_ROWS}}', backups.length ? rows : '<tr><td colspan="4" class="empty">No backups yet.</td></tr>'); res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', ...SECURE_HEADERS }); res.end(html); } ``` The same response embeds the authentication token in executable ...[truncated 2242 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never concatenate untrusted filenames into HTML. Use a template engine with automatic contextual escaping or construct the table through DOM APIs with `textContent` and `setAttribute()`. 2. Enforce a conservative filename allowlist before writing an upload: ```js const SAFE_ARCHIVE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}\.(?:tar\.gz|zip)$/; if (!SAFE_ARCHIVE.test(file.filename)) { return json(res, 400, { error: 'Invalid archive filename' }); } ``` 3. Do not embed the bearer token into generated HTML or global JavaScript. Use a secure authentication mechanism such as an `HttpOnly`, `Secure`, `SameSite=Strict` session cookie established through a protected login flow. 4. Return download URLs without query-string credentials. The browser should authenticate downloads through the protected session. 5. Add a restrictive Content Security Policy. Move scripts to a static file and prohibit inline script and inline event handlers: ```http Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none' ``` 6. Add automated tests that render filenames containing `"`, `'`, `<`, `>`, ampersands, and event-handler payloads, and verify that they appear only as text. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/server.js:67
Finding
Cleartext Public HTTP Service Exposes Credential-Bearing Backups and Bearer Tokens<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.js:67-90` **Related Location**: `scripts/server.js:291-295` **Vulnerability Type**: Cleartext transmission and insecure token handling **Risk Level**: High ### Vulnerable Code ```js function listBackups() { return fs.readdirSync(BACKUP_DIR) .filter(f => (f.startsWith('openclaw-backup_') || f.startsWith('pre-restore_')) && (f.endsWith('.tar.gz') || f.endsWith('.zip'))) .map(f => { try { const stat = fs.statSync(path.join(BACKUP_DIR, f)); return { filename: f, size: stat.size, sizeHuman: fmt(stat.size), createdAt: stat.mtime.toISOString(), downloadUrl: `/download/${encodeURIComponent(f)}?token=${TOKEN}`, }; } catch { return null; } }) .filter(Boolean) .sort((a, b) => b.createdAt.localeCompare(a.createdAt)); } function checkAuth(req) { const q = new URL('http://x' + req.url).searchParams.get('token'); const h = (req.headers['authorization'] || '').replace('Bearer ', '').trim(); return q === TOKEN || h === TOKEN; } ``` The service listens on every interface without TLS: ```js server.listen(PORT, '0.0.0.0', () => { console.log(`\n🦞 MyOpenClaw Backup Restore Server v3.0`); console.log(` Token protected | Localhost-only for /backup and /restore`); console.log(` http://localhost:${PORT}/?token=${TOKEN}`); console.log(` ⚠️ Do not expose this port to the internet without TLS.\n`); }); ``` ### Technical Analysis The service binds to `0.0.0.0`, making it reachable on all available network interfaces where host firewall rules permit access. It uses Node's plain HTTP server and does not provide transport encryption. Both the bearer token and backup contents are therefore exposed to passive network observers and active man-in-the-middle attackers. This is especially serious because project documentation confirms that archives contain bot tokens, API ke ...[truncated 1570 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind to loopback by default: ```js const HOST = arg('--host', '127.0.0.1'); server.listen(PORT, HOST, ...); ``` 2. Require an explicit high-visibility option such as `--allow-remote` before accepting a non-loopback address. 3. For remote access, require TLS using `https.createServer()` or document and enforce operation behind an authenticated TLS reverse proxy. Refuse remote binding unless TLS configuration is supplied. 4. Remove query-string token authentication. Accept credentials only through the `Authorization` header or an `HttpOnly`, `Secure`, `SameSite=Strict` session cookie. 5. Do not return token-bearing `downloadUrl` values. Return a relative URL without credentials and let the normal authentication mechanism protect the request. 6. Use `crypto.timingSafeEqual()` for fixed-length token comparison after validating buffer lengths. 7. Generate high-entropy tokens, implement token rotation or expiry, rate-limit authentication failures, and redact secrets from startup output and logs. 8. Add `Referrer-Policy: no-referrer` and apply the existing secure headers to archive responses as defense in depth. 9. Update the documentation so its remote-access guidance matches the enforced transport controls. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/backup-restore.js:793
Finding
Untrusted Backup Archives Can Install and Automatically Execute Restored Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/backup-restore.js:793-851` **Related Locations**: `scripts/backup-restore.js:274-307`, `scripts/server.js:231-240` **Vulnerability Type**: Unsafe restoration and execution of untrusted archive content **Risk Level**: Critical ### Vulnerable Code Restored scripts are copied into the OpenClaw directory and made executable: ```js // 11. Scripts (guardian/watchdog — make executable on non-Windows) info('Restoring scripts...'); const backupScripts = path.join(backupDir, 'scripts'); if (fs.existsSync(backupScripts)) { for (const f of fs.readdirSync(backupScripts)) { const dest = path.join(OPENCLAW_DIR, f); fs.copyFileSync(path.join(backupScripts, f), dest); harden(dest, 0o755); } } // 12. Cron info('Restoring cron jobs...'); restoreDir(path.join(backupDir, 'cron'), path.join(OPENCLAW_DIR, 'cron')); // 13. ClawHub restoreDir(path.join(backupDir, '.clawhub'), path.join(OPENCLAW_DIR, '.clawhub')); // 14. Delivery queue restoreDir(path.join(backupDir, 'delivery-queue'), path.join(OPENCLAW_DIR, 'delivery-queue')); // 15. Memory index restoreDir(path.join(backupDir, 'memory'), path.join(OPENCLAW_DIR, 'memory')); ``` The restored startup script is then selected and executed automatically: ```js // ── Restart gateway ────────────────────────────────────────────────── console.log(''); info('Starting OpenClaw Gateway...'); try { const startCmd = IS_WIN ? 'cmd /c "openclaw gateway start"' : (fs.existsSync(path.join(OPENCLAW_DIR, 'start-gateway.sh')) ? `bash "${path.join(OPENCLAW_DIR, 'start-gateway.sh')}" &` : 'openclaw gateway start'); execSync(startCmd, { stdio: 'ignore', timeout: 15000 }); info(' Gateway started'); } catch { warn(' Could not auto-start gateway. Run manually: openclaw gateway start'); } ``` The server permits authenticated remote upload without archive validation: ```js if (req.method === 'POST' && urlPath === '/upload') { try { con ...[truncated 3185 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never execute a script restored from an archive. Restart the gateway only through a known trusted executable installed outside the restored data: ```js execFileSync('openclaw', ['gateway', 'start'], { stdio: 'ignore', timeout: 15000, }); ``` 2. Divide restoration into passive data and active components. Require separate, explicit approval before restoring: - Skills - Extensions - Shell scripts - Cron definitions - Executable hooks - Agent instructions or persistent memory 3. Sign backups with a trusted key and verify the signature before extraction or restoration. A checksum stored inside the same archive is not sufficient because an attacker can replace both content and checksum. 4. Enumerate archive entries before extraction and reject: - Absolute paths - `..` path components - Paths outside the approved top-level backup directory - Symbolic links and hard links - Device nodes, FIFOs, sockets, and other special entries - Unexpected top-level files or directories - Duplicate entries and excessively large expansion ratios 5. Extract with a library that exposes entry metadata and supports controlled destination validation rather than invoking general-purpose extraction commands blindly. 6. Validate `MANIFEST.json` against a strict schema, impose file-count and expanded-size limits, and verify every extracted path using canonical path containment checks. 7. Change dry-run into a security inspection that reports executable files, active configuration, scripts, cron jobs, skills, extensions, links, permissions, hashes, and signature status. 8. Keep restored scripts non-executable by default and place them in a quarantine directory for manual review. 9. Require local reauthentication and an explicit per-component confirmation before applying active state from an uploaded archive. ]]>
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 (30)

Hidden Instructions

High
Category
Prompt Injection
Content
# 🦞 MyOpenClaw Backup Restore �?跨平台备份还原工�?
> �?[MyClaw.ai](https://myclaw.ai) 开源技能生态提�?
**一个命令备份,一个命令还原。支�?Windows、macOS、Linux 互相备份还原�?*
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill description claims a built-in HTTP server and 'only requires Node.js,' while the analysis says the code relies on external commands such as tar, unzip, PowerShell, bash, and openclaw CLI and may not implement the advertised server behavior. This mismatch is security-relevant because operators may deploy the skill under false assumptions about its attack surface, dependencies, portability, and review requirements.

Ae1

High
Category
analysis-evasion
Content
node scripts/backup-restore.js backup
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/backup-restore.js backup
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/backup-restore.js backup
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/backup-restore.js backup
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/backup-restore.js backup
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/backup-restore.js backup
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/backup-restore.js backup
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/server.js --token <secret> [--port 7373] [--backup-dir <dir>]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The quick-start section presents restore commands immediately after backup/list commands, and although a dry-run is shown, it does not prominently warn that restore modifies local state and may overwrite credentials, configuration, and conversation data. In a tool explicitly handling highly sensitive OpenClaw state, understated warning language increases the chance of unsafe operator use and accidental destructive restoration.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The HTTP management server section advertises browser-based upload and download of backup archives without placing an immediate privacy warning beside those features. Because the same README elsewhere states backups contain bot tokens, API keys, credentials, and full conversation history, omitting that warning in the network-transfer section materially increases the risk of accidental exposure or insecure sharing.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill metadata grants filesystem read/write and network-listen access, but the static finding indicates the documented tool scope does not accurately disclose shell/env capabilities used by the implementation. Undeclared execution capability is dangerous because it expands what the skill can do beyond what reviewers and users can reasonably infer, especially for a backup/restore tool that handles secrets and system state.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: myopenclaw-backup-restore
description: "Cross-platform backup and restore for OpenClaw. Works on Windows, macOS, and Linux — backups created on any OS can be restored on any other OS. Use when user wants to create a snapshot, restore from backup, migrate to a new machine, or protect against data loss. Supports dry-run preview, automatic pre-restore snapshots, gateway token preservation, credential permission hardening, and a built-in HTTP server for browser-based management. Only requires Node.js (no bash/rsync/python needed)."
metadata:
  openclaw:
    requires:
Confidence
82% confidence
Finding
This skill is explicitly designed to preserve and migrate session state, credentials, tokens, and other persistent agent data across machines. That behavior is expected for backup/restore, but it is still sensitive because compromise of archives or misuse of restore operations can transfer active secrets and long-lived access between environments.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
node scripts/backup-restore.js backup [--output-dir <dir>]
```

Creates `openclaw-backup_{agent}_{timestamp}.tar.gz` in `~/openclaw-backups/`. Auto-prunes (keeps last 7). On non-Windows: `chmod 600` applied.

### restore
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
node scripts/backup-restore.js backup [--output-dir <dir>]
```

Creates `openclaw-backup_{agent}_{timestamp}.tar.gz` in `~/openclaw-backups/`. Auto-prunes (keeps last 7). On non-Windows: `chmod 600` applied.

### restore
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- **Pre-restore snapshot**: Auto-saves current state before overwriting
- **Gateway token preservation**: Keeps new server's token (prevents Control UI mismatch)
- **Interactive confirmation**: Must type `yes`
- **Credential hardening**: `chmod 700/600` on non-Windows
- **Auto-restart**: Starts gateway after restore
- **Legacy compatibility**: Handles v1 (bash) and v2 archive structures
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- **Pre-restore snapshot**: Auto-saves current state before overwriting
- **Gateway token preservation**: Keeps new server's token (prevents Control UI mismatch)
- **Interactive confirmation**: Must type `yes`
- **Credential hardening**: `chmod 700/600` on non-Windows
- **Auto-restart**: Starts gateway after restore
- **Legacy compatibility**: Handles v1 (bash) and v2 archive structures
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- **Pre-restore snapshot**: Auto-saves current state before overwriting
- **Gateway token preservation**: Keeps new server's token (prevents Control UI mismatch)
- **Interactive confirmation**: Must type `yes`
- **Credential hardening**: `chmod 700/600` on non-Windows
- **Auto-restart**: Starts gateway after restore
- **Legacy compatibility**: Handles v1 (bash) and v2 archive structures
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
*
 * SECURITY:
 *   - Archives contain bot tokens, API keys, and session credentials
 *   - Permissions hardened on non-Windows (chmod 600 for archive, 700/600 for credentials)
 *   - Pre-restore snapshot always created before overwriting
 *   - Gateway token preserved by default (prevents Control UI mismatch)
 *   - Interactive confirmation required before destructive restore
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
*
 * SECURITY:
 *   - Archives contain bot tokens, API keys, and session credentials
 *   - Permissions hardened on non-Windows (chmod 600 for archive, 700/600 for credentials)
 *   - Pre-restore snapshot always created before overwriting
 *   - Gateway token preserved by default (prevents Control UI mismatch)
 *   - Interactive confirmation required before destructive restore
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
*
 * SECURITY:
 *   - Archives contain bot tokens, API keys, and session credentials
 *   - Permissions hardened on non-Windows (chmod 600 for archive, 700/600 for credentials)
 *   - Pre-restore snapshot always created before overwriting
 *   - Gateway token preserved by default (prevents Control UI mismatch)
 *   - Interactive confirmation required before destructive restore
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
*
 * SECURITY:
 *   - Archives contain bot tokens, API keys, and session credentials
 *   - Permissions hardened on non-Windows (chmod 600 for archive, 700/600 for credentials)
 *   - Pre-restore snapshot always created before overwriting
 *   - Gateway token preserved by default (prevents Control UI mismatch)
 *   - Interactive confirmation required before destructive restore
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The script invokes external programs via execSync for version detection, archive creation/extraction, and service control, which expands trust beyond local file operations. Because some command arguments incorporate file paths and restored content later influences execution flow, a crafted archive or attacker-controlled environment can turn restore into code execution or unsafe command behavior.

Session Persistence

Medium
Category
Rogue Agent
Content
// ── Interactive confirmation ─────────────────────────────────────────
    console.log('');
    console.log(c.red('⚠️  WARNING: This will OVERWRITE ~/.openclaw/ with backup data.'));
    console.log(`   Backup:  ${path.basename(archive)}`);
    console.log(`   Target:  ${OPENCLAW_DIR}`);
    if (currentToken && !overwriteToken) {
Confidence
60% 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.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.secret_argv_exposure

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/backup-restore.js:119

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/server.js:198

Instructions pass high-value credentials through process argv.

Critical
Code
suspicious.secret_argv_exposure
Location
SKILL.md:82