Back to skill

Security audit

WebDAV 备份工具

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a genuine WebDAV backup skill, but its defaults can upload unencrypted OpenClaw workspace/configuration data including stored credentials, and it has local restore/write safety gaps that users should review before installing.

Install only if you are comfortable with a backup tool reading your OpenClaw workspace and configuration and sending archives to your configured WebDAV server. Before use, prefer a dedicated least-privilege WebDAV account, HTTPS-only endpoints, encrypted archives, a local backup directory with private permissions, and avoid including live credential files unless you intentionally accept that exposure. Treat restores and --force as filesystem-changing operations and test them in a separate directory first.

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/backup.py:56
Finding
Credential-bearing configuration is archived and uploaded without encryption<![CDATA[ ## Vulnerability Details **File Location**: `scripts/backup.py:56-62`, `scripts/backup.py:270-299`, `scripts/backup.py:488-505` **Vulnerability Type**: Plaintext storage and transmission of sensitive configuration **Risk Level**: High ### Vulnerable Code ```python { 'path': os.path.join(OPENCLAW_ROOT, 'openclaw.json'), 'arcname': 'openclaw/openclaw.json', 'required': False, 'label': '主配置 openclaw.json', }, ``` ```python with tarfile.open(backup_path, 'w:gz') as tar: if include_defaults and not source_dir: print('🧩 使用默认备份清单:workspace + 基础配置') manifest = [] for item in DEFAULT_BACKUP_ITEMS: item_path = Path(item['path']).expanduser() if item_path.exists(): added = add_path_to_tar( tar, item_path, item['arcname'], DEFAULT_EXCLUDE_PATTERNS ) ``` ```python remote_url = WEBDAV_URL.rstrip('/') + '/' + remote_name ensure_webdav_directory() opener = create_webdav_opener() try: with open(local_file, 'rb') as f: data = f.read() req = urllib.request.Request( remote_url, data=data, method='PUT' ) req.add_header('Content-Type', 'application/octet-stream') ``` ### Technical Analysis The default backup set includes `~/.openclaw/openclaw.json`. The Skill documentation also recommends storing `WEBDAV_URL`, `WEBDAV_USERNAME`, and `WEBDAV_PASSWORD` in that file. As a result, a default backup can contain the same credentials used to access its remote backup destination, along with other sensitive OpenClaw configuration. The archive uses gzip compression but no encryption. It is retained locally or uploaded as an ordinary `tar.gz` file. Anyone who obtains the archive can extract its contents without an additional secret. The code does not explicitly restrict local archive permissions to owner-only access. Effective permissions therefore d ...[truncated 1637 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not archive the live `openclaw.json` by default when it may contain credentials. 2. Generate a sanitized configuration copy that removes passwords, tokens, cookies, private keys, and other secret fields before adding it to the archive. 3. Add an explicit opt-in option for including secrets, accompanied by a clear warning. 4. Encrypt every archive before local retention or upload using authenticated encryption. Obtain the encryption key from a separate secret source rather than storing it in the archived configuration. 5. Create local archives with owner-only permissions, such as mode `0600`, and verify that the output directory is not accessible to other users. 6. Reject non-HTTPS WebDAV URLs unless the host is a verified loopback address and the user explicitly permits local cleartext transport. 7. Recommend a dedicated, least-privilege WebDAV account restricted to the backup directory. 8. Document that exposure of an existing backup containing credentials requires immediate credential rotation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/backup.py:258
Finding
User-controlled backup names permit writes outside the selected output directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/backup.py:258-270` **Vulnerability Type**: Path traversal and arbitrary-path file creation **Risk Level**: Medium ### Vulnerable Code ```python timestamp = datetime.datetime.now().strftime('%Y%m%d-%H%M%S') if backup_name: backup_file = f"{backup_name}-{timestamp}.tar.gz" else: backup_file = f"openclaw-backup-{timestamp}.tar.gz" output_path = Path(output_dir).expanduser() output_path.mkdir(parents=True, exist_ok=True) backup_path = output_path / backup_file print(f"📦 正在创建备份: {backup_file}") with tarfile.open(backup_path, 'w:gz') as tar: ``` The attacker-controlled value originates from the `--name` argument: ```python parser.add_argument( '--name', '-n', default='openclaw-backup', help='备份文件名前缀' ) ``` ### Technical Analysis The `--name` value is used as part of a filesystem path without validation. The implementation does not reject: - Absolute paths - Parent-directory components such as `..` - Forward or backward path separators - Platform-specific path syntax With `pathlib`, joining an absolute `backup_file` to `output_path` discards `output_path`. Relative traversal components can similarly escape the selected backup directory. `tarfile.open(..., 'w:gz')` then creates or truncates the calculated destination file. The timestamp suffix prevents selecting an entirely arbitrary final filename, but it does not prevent writing to an attacker-selected directory. If an attacker can predict the execution second or prepare a matching symbolic link, the primitive may also be used to truncate or replace another file writable by the Skill process. This is especially relevant when an AI agent constructs command-line arguments from untrusted instructions or external content. ### Attack Path 1. An attacker influences the value supplied to `--name`. 2. The attacker supplies an absolute prefix such as `/attacker/chosen/path/backup` or a traversal prefix such as `../../chosen/p ...[truncated 1013 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict `--name` to a safe filename-prefix allowlist, for example: ```python if not re.fullmatch(r'[A-Za-z0-9._-]+', backup_name): raise ValueError('Invalid backup name') ``` 2. Explicitly reject absolute paths, `..`, forward slashes, backward slashes, null bytes, and platform-specific drive syntax. 3. Resolve both the output directory and final path, then verify that the final path remains beneath the output directory: ```python output_path = Path(output_dir).expanduser().resolve() backup_path = (output_path / backup_file).resolve() if output_path not in backup_path.parents: raise ValueError('Backup path escapes the output directory') ``` 4. Open the destination defensively to prevent symbolic-link following and unintended replacement. On supported platforms, use secure low-level flags such as `O_NOFOLLOW`, `O_CREAT`, and `O_EXCL`. 5. Create the archive with owner-only permissions. 6. Treat command-line arguments derived from Agent messages or external content as untrusted input. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill documents and requires sensitive capabilities (environment secrets, file access, and network/WebDAV upload) but does not declare an explicit tool scope such as permissions or allowed-tools. That creates a policy gap: an agent may use broader-than-intended capabilities to read local data and transmit it remotely, which is especially sensitive here because the skill is explicitly designed to back up workspace contents and configuration files to a remote server.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation shows a curl command that places credentials directly on the command line (`curl -u $WEBDAV_USERNAME:$WEBDAV_PASSWORD ...`) without warning that command-line arguments may be exposed via shell history, process listings, debugging logs, or monitoring tools. In a backup/configuration guide, users are likely to copy-paste this verbatim, which increases the chance of credential disclosure for the WebDAV service.

Session Persistence

Medium
Category
Rogue Agent
Content
### 每日自动备份
```bash
# 添加到 crontab
crontab -e
# 添加行(根据实际路径调整):
0 2 * * * /usr/bin/python3 ~/.openclaw/workspace/skills/openclaw-webdav-backup/scripts/backup.py >> /tmp/webdav-backup.log 2>&1
```
Confidence
85% 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.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The script restores arbitrary tar.gz archives into a user-chosen directory and does not validate that extracted member paths remain within the intended destination. Although tar.extract(..., filter='data') blocks some dangerous metadata, it does not by itself guarantee protection against path traversal or all overwrite scenarios across Python/runtime variations. In a backup skill context, restore is a high-risk write operation because users are likely to trust backup archives and run it against important directories.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The restore path is created if needed and archive members are extracted into it, which modifies the local filesystem and may overwrite files when --force is used. The script prints progress messages, but it does not provide a clear warning in help text or comments that restore is a filesystem-changing operation with overwrite risk.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script automatically deletes older local backups and remote WebDAV backups as part of normal execution. Although deletion is logged when it occurs, the command-line interface does not clearly warn users beforehand that running a backup may trigger retention-based deletion of existing backups.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest states the tool 'automatically retains the most recent 7 local backups and 20 remote backups,' which suggests a simple count-based retention policy. The implementation instead keeps remote backups newer than 60 days and also preserves at least 20 by count, so remote retention behavior is broader and semantically different from the stated description.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script uploads a generated backup archive to a remote WebDAV server, and the default backup set includes the workspace plus configuration files. While there is logging that an upload is happening, there is no user-facing warning that potentially sensitive local data will be transmitted off-host to a remote service.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
This markdown file contains user-facing natural language only in Chinese across the substantive changelog entries. Under the policy rule for language or locale violations, forcing a specific language without user opt-in or a documented justification is a reportable issue.

Natural-Language Policy Violations

Low
Confidence
72% confidence
Finding
The file presents all operational guidance in Chinese and does not indicate that the skill is region-specific or that users may choose another language. Under the stated policy, forcing a specific language without opt-in can be a natural-language policy issue unless the locale constraint is explicitly justified.

Natural-Language Policy Violations

Low
Confidence
97% confidence
Finding
Docstrings, status messages, errors, and CLI help are consistently presented in Chinese, which imposes a specific language on all users. There is no indication that the tool is region-specific or that users can opt into another language, so this appears to violate the language/locale policy.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The docstring for cleanup_old_backups states '保留3个月内或者最近20个备份' (keep backups within 3 months or latest 20), but the actual cutoff is RETENTION_DAYS = 60, i.e. about 2 months. This is an active contradiction between inline documentation and implemented retention behavior.

Static analysis

No suspicious patterns detected.