T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/sync_helper.py:105
- Finding
- Unvalidated iCloud Vault Content Can Replace Active OpenClaw Skills and Configuration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sync_helper.py:105-112`, `scripts/sync_helper.py:324-347`, and `scripts/sync_helper.py:433-477` **Vulnerability Type**: Unvalidated external content and destructive symlink replacement **Risk Level**: Medium ### Vulnerable Code Vaults are considered trusted solely because they contain a `.obsidian` entry: ```python def is_valid_openclaw_vault(self, vault_path: Path) -> bool: """Check if a vault is a valid OpenClaw vault. A valid OpenClaw vault must have: 1. .obsidian directory (identifies it as an Obsidian vault) Note: openclaw.json is recommended but not strictly required. It may exist in iCloud as a template, and locally with machine-specific config. """ return (vault_path / ".obsidian").exists() ``` The selected vault's content is then used to replace local OpenClaw entries: ```python # Create core directory symlinks for dir_name in core_dirs: src = helper.icloud_vault / dir_name if src.exists(): symlink_path = helper.local_vault / dir_name if symlink_path.exists() or symlink_path.is_symlink(): symlink_path.unlink() symlink_path.symlink_to(src) print(f" ✓ Created: {dir_name}") created_count += 1 # Create workspace symlinks for ws in sorted(workspaces): src = helper.icloud_vault / ws symlink_path = helper.local_vault / ws if symlink_path.exists() or symlink_path.is_symlink(): symlink_path.unlink() symlink_path.symlink_to(src) print(f" ✓ Created: {ws}") created_count += 1 # Create config file symlinks if config_files: local_openclaw_dir = helper.local_vault / ".openclaw" local_openclaw_dir.mkdir(exist_ok=True) for config_file in config_files: file_name = config_file.replace(".openclaw/", "") src = openclaw_dir / file_name symlink_path = local_openclaw_dir / file_name if symlink_path.exists() or symlink_path.is_symlink(): ...[truncated 3003 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require explicit trusted-vault enrollment and store the canonical path of each approved vault in a local allowlist. 2. Resolve the selected vault and every source using `Path.resolve(strict=True)`, then verify that every resolved source remains beneath the approved canonical vault root. 3. Reject source entries that are symlinks, have unexpected ownership, or are writable by untrusted users. 4. Validate all synchronized JSON files against strict schemas before modifying local configuration. 5. Present skill and workspace content for review or verify it against trusted hashes or signatures before activation. 6. Never unlink existing regular files implicitly. Create timestamped backups and require an explicit overwrite option for every class of destination, including `.openclaw/*.json`, skills, and workspaces. 7. Prevent `--no-confirm` from authorizing destructive replacement unless a separate explicit overwrite flag is supplied. 8. Construct and validate the complete synchronization plan before making changes. Apply it transactionally and restore backups if any operation fails. 9. Handle existing directories safely instead of calling `unlink()` on them, and fail before making any changes when a destination has an unsupported type. ]]>
