T09 · Insecure Skill Coding Practices
- Location
scripts/task_runner.py:131- Finding
Overly Broad Export Path Validation Allows Sensitive Home-File Overwrites
- Content
View full analysis
Vulnerability Details
File Location:
scripts/task_runner.py, lines 131–166 and 175–211
Vulnerability Type: Arbitrary file overwrite through incomplete path validation
Risk Level: MediumVulnerable Code
python def is_safe_path(filepath): """Check if file path is within safe directories (workspace, home, or /tmp).""" try: path = Path(filepath).expanduser().resolve() workspace = Path.home() / ".openclaw" / "workspace" home = Path.home() tmp = Path("/tmp") # Convert to strings for comparison path_str = str(path) workspace_str = str(workspace.resolve()) home_str = str(home.resolve()) tmp_str = str(tmp.resolve()) # Allow workspace, home, and /tmp in_workspace = path_str.startswith(workspace_str) in_home = path_str.startswith(home_str) in_tmp = path_str.startswith(tmp_str) # Block system paths system_dirs = ["/etc", "/usr", "/var", "/root", "/bin", "/sbin", "/lib", "/lib64", "/opt", "/boot", "/proc", "/sys"] blocked = any(path_str.startswith(d) for d in system_dirs) # Block sensitive dotfiles in home directory sensitive_patterns = [".ssh", ".bashrc", ".zshrc", ".profile", ".bash_profile", ".config/autostart"] for pattern in sensitive_patterns: if pattern in path_str: blocked = True break # Allow workspace, /tmp, or home (excluding blocked paths) allowed = (in_workspace or in_tmp or in_home) and not blocked return allowed except Exception as e: # On any error, deny access (fail-safe) return FalseThe accepted path is subsequently used as a write destination:
python def export_project(project, output_file): """Export a project's tasks to markdown.""" ...[truncated 4516 chars]- Remediation
View remediation
Remediation Suggestions
-
Restrict exports to a dedicated directory, such as
~/.openclaw/workspace/exports, rather than allowing the entire home directory. -
Replace string-prefix comparisons with path-component-aware containment checks:
python def is_within(path, root): try: path.relative_to(root) return True except ValueError: return False def is_safe_path(filepath): try: export_root = ( Path.home() / ".openclaw" / "workspace" / "exports" ).resolve() resolved = Path(filepath).expanduser().resolve(strict=False) return is_within(resolved, export_root) except (OSError, RuntimeError, ValueError): return FalseOn supported Python versions,
resolved.is_relative_to(export_root)can be used instead.-
Avoid blacklist-based protection. A narrow allowlist of export roots is more reliable than attempting to enumerate every sensitive home file.
-
Revalidate the destination immediately before opening it and account for symbolic-link race conditions. Where practical, use directory file descriptors and no-follow semantics supported by the operating system.
-
Refuse to overwrite existing files by default. Require an explicit overwrite option when replacement is intended.
-
Create exported files with restrictive permissions appropriate for potentially private task data.
-
Add tests covering:
- Paths inside the dedicated export directory.
..traversal.- Existing and newly created symbolic links.
- Prefix-confusion paths such as
/tmp-other. - Sensitive home files and arbitrary paths under
~/.config. - Attempts to overwrite existing files.
-
Update
SKILL.mdso its security claims precisely match the implemented policy. If exports are limited to a dedicated directory, document that exact restriction rather than claiming broad home-directory access with sensitive files bl ...[truncated 6 chars]
-
