Back to skill

Security audit

OpenClaw Hardener

Security checks for vulnerabilities and agentic risk

Overview

This hardening skill is mostly purpose-aligned, but its default check path can execute workspace-controlled shell code and its file traversal can reach outside the intended scope.

Install only if you are comfortable reviewing and running a high-authority local hardening script. Do not run the default workspace check on untrusted repositories unless the repo-local audit execution is removed or made explicitly opt-in, and avoid fix mode until symlink containment and target-root validation are addressed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/hardener.py:199
Finding

Workspace-Controlled Shell Script Execution During Security Checks

Content
View full analysis
List[Finding]: """Run the repo-local security audit (hybrid orchestrator).""" sh = workspace_root / "hybrid_orchestrator" / "scripts" / "audit" / "security_audit.sh" if not sh.exists(): return [Finding("warn", "workspace_audit_missing", "Workspace security_audit.sh not found", str(sh))] try: rc, out, err = run(["bash", str(sh), "--target", str(workspace_root)], timeout_s=900) except Exception as e: return [Finding("fail", "workspace_audit_failed", f"Failed to run workspace audit: {e}")] combined = redact((out + "\n" + err).strip()) sev = "info" if rc == 0 else "warn" return [Finding(sev, "workspace_security_audit", combined)] ``` The function is reached through the default workspace-check path: ```python if args.cmd == "check": do_openclaw = args.all or args.openclaw or (not args.openclaw and not args.workspace and not args.all) do_workspace = args.all or args.workspace or (not args.openclaw and not args.workspace and not args.all) if do_openclaw: findings.extend(check_openclaw_audit(deep=True)) if do_workspace: findings.extend(check_workspace_security_audit(ws)) findings.extend(check_unexpected_exec_bits(ws)) findings.extend(check_dotenv(ws)) findings.extend(check_risky_code_patterns(ws)) ``` ### Technical Analysis The security checker locates `security_audit.sh` inside the workspace being assessed and executes it through `bash`. The existence check does not establish that the script belongs to the trusted skill package, has an approved cryptographic digest, or comes from another trusted source. A repository under attacker control can therefore su ...[truncated 1617 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/hardener.py:53
Finding

Symbolic Links Permit File Operations Outside the Intended Workspace

Content
View full analysis
Iterable[Path]: skip_dirs = {".git", "node_modules", "third_party", "__pycache__", ".venv", ".venv3"} for p in root.rglob("*"): try: rel_parts = set(p.relative_to(root).parts) except Exception: rel_parts = set() if any(d in rel_parts for d in skip_dirs): continue if p.is_file(): yield p ``` Returned paths are subsequently dereferenced by operations such as: ```python def fix_unexpected_exec_bits(root: Path) -> List[Finding]: changed: List[Finding] = [] for p in iter_files(root): if p.suffix.lower() not in NONEXEC_EXTS: continue try: st = p.stat() except Exception: continue if st.st_mode & stat.S_IXUSR: new_mode = st.st_mode & ~stat.S_IXUSR & ~stat.S_IXGRP & ~stat.S_IXOTH try: os.chmod(p, new_mode) changed.append(Finding("info", "chmod", "Removed executable bit", str(p))) except Exception as e: changed.append(Finding("warn", "chmod_failed", f"Failed to chmod: {e}", str(p))) ``` The risky-pattern scanner can also read through such links: ```python for p in iter_files(sr): if p.suffix.lower() not in {".py", ".sh", ".js", ".ts"}: continue try: txt = p.read_text("utf-8", errors="replace") except Exception: continue ``` ### Technical Analysis `Path.is_file()`, `Path.stat()`, `Path.read_text()`, and `os.chmod()` normally follow symbolic links. The code checks that the lexical ...[truncated 2088 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/hardener.py:48
Finding

Fixed Parent-Depth Calculation Can Select a Directory Above the Project

Content
View full analysis
Path: # .../skills_live/openclaw-hardener/scripts/hardener.py -> workspace root return Path(__file__).resolve().parents[3] ``` The resulting path is used by the check and fix operations: ```python args = ap.parse_args() ws = workspace_root_from_script() home = Path.home() ``` ```python if do_workspace: findings.extend(check_workspace_security_audit(ws)) findings.extend(check_unexpected_exec_bits(ws)) findings.extend(check_dotenv(ws)) findings.extend(check_risky_code_patterns(ws)) ``` ```python if do_workspace: findings.extend(fix_unexpected_exec_bits(ws)) ``` ### Technical Analysis The workspace root is derived by moving exactly three parent directories above the script. This assumes a specific installation layout but does not verify that the selected directory is actually the intended repository. In the audited project layout, the script is located at `/scripts/hardener.py`. Moving three parents upward selects a directory above ``. If the skill is installed under any layout other than the one encoded in the comment, recursive scans and fixes can therefore target a broader directory tree containing unrelated projects and files. No repository marker, configured boundary, manifest path, or user-confirmed root is checked before the selected directory is passed to recursive file traversal and permission-changing functions. ### Attack Path 1. The skill is installed or extracted with `scripts/hardener.py` at a directory depth different from the assumed `skills_live/openclaw-hardener/scripts/` layout. 2. The user invokes a workspace check or fix. 3. `workspace_root_from_script()` moves three levels upward without validating the result. 4. The returned path points above the act ...[truncated 826 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

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 · scripts/hardener.py (reported line 120)May include surrounding context.

python
findings: List[Finding] = []
    for p in iter_files(root):
        name = p.name
        if name == ".env" or name.startswith(".env."):
            # presence is warn; if tracked by git, treat as fail
            findings.append(Finding("warn", "dotenv_present", "Found dotenv file (ensure it is not committed)", str(p)))
    # If git is available, check tracked dotenv

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 · scripts/hardener.py (reported line 125)May include surrounding context.

python
findings: List[Finding] = []
    for p in iter_files(root):
        name = p.name
        if name == ".env" or name.startswith(".env."):
            # presence is warn; if tracked by git, treat as fail
            findings.append(Finding("warn", "dotenv_present", "Found dotenv file (ensure it is not committed)", str(p)))
    # If git is available, check tracked dotenv

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill invokes a Python script that performs security audits, workspace scanning, permission changes, and gateway calls, which implies shell, file-read, and possibly environment access. Declaring no explicit tool scope means the skill does not constrain or document those capabilities, increasing the chance of over-privileged execution and making abuse or unintended data access harder to govern.

Content

No source excerpt is available for this finding.

Scope Creep

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest advertises expected writes only under ~/.openclaw, but its declared filesystem permissions allow writing anywhere in the repository as well. This mismatch weakens reviewer and runtime trust assumptions: a skill presented as a home-directory hardener can also modify source files, configs, hooks, or CI assets in the workspace, increasing the chance of unintended tampering or persistence.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/hardener.py (reported line 55)May include surrounding context.

python
def run(cmd: List[str], timeout_s: int = 300) -> Tuple[int, str, str]:
    p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=timeout_s)
    return p.returncode, p.stdout, p.stderr

Static analysis

No suspicious patterns detected.