Back to skill

Security audit

Openclaw Sentinel

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real local skill security scanner, but it can automatically disable or move other skills and has an unvalidated reject path that can delete directories outside the intended workspace.

Install only if you are comfortable giving this skill authority to inspect and modify your local skills workspace. Prefer using scan and inspect first, avoid protect and reject until path validation and confirmation safeguards are added, and run it from a low-privilege account with backups of your skills directory.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/sentinel.py:636
Finding
Arbitrary Directory Deletion Through Unvalidated Skill Path## Vulnerability Details **File Location**: `scripts/sentinel.py`, lines 636-665 **Vulnerability Type**: Path traversal leading to arbitrary recursive directory deletion **Risk Level**: High ### Vulnerable Code ```python def cmd_reject(workspace, skill_name): skills_dir = workspace / "skills" skill_path = skills_dir / skill_name if not skill_path.exists(): qp = skills_dir / f"{QUARANTINE_PREFIX}{skill_name}" if qp.exists(): skill_path = qp else: print(f"Skill not found: {skill_name}"); return 1 if not skill_path.is_dir(): print(f"Not a skill directory: {skill_path}"); return 1 print("=" * 62); print("OPENCLAW SENTINEL FULL — REJECT SKILL"); print("=" * 62) print(f"Skill: {skill_name}\nTimestamp: {now_iso()}\n") tdb = load_threat_db(workspace) all_names = [d.name for d in collect_skill_dirs(workspace)] findings, score = scan_skill(skill_path, workspace, tdb, all_names) print(f" Risk Score: {score}/100 [{risk_label(score)}]\n Findings: {len(findings)}\n") if score < 50: print(f"[BLOCKED] Risk score {score} is below HIGH threshold (50).") print(f" Use 'quarantine {skill_name}' to disable without removal.") print(f" Reject is reserved for HIGH+ risk skills.\n"); return 1 evidence = {"skill": skill_name, "rejected_at": now_iso(), "risk_score": score, "risk_label": risk_label(score), "findings_count": len(findings), "findings": findings[:50], "original_path": str(skill_path), "file_inventory": file_inventory(skill_path)} ev_dir = quarantine_evidence_dir(workspace) save_json(ev_dir / f"{skill_name}-evidence.json", evidence) reject_dest = ev_dir / skill_name if reject_dest.exists(): shutil.rmtree(reject_dest) try: shutil.move(str(skill_path), str(reject_dest)) except OSError as e: print(f"Failed to move skill: {e}"); return 1 ``` ### Technical ...[truncated 3233 chars]
Remediation
## Remediation Suggestions 1. **Restrict the input to a single directory name.** Reject absolute paths, empty values, `.` and `..`, path separators, and names that differ from `Path(skill_name).name`. ```python candidate = Path(skill_name) if ( candidate.is_absolute() or skill_name in {"", ".", ".."} or candidate.name != skill_name ): print("Invalid skill name") return 1 ``` 2. **Resolve and enforce path boundaries before filesystem operations.** ```python skills_dir = (workspace / "skills").resolve() skill_path = (skills_dir / skill_name).resolve() ev_dir = quarantine_evidence_dir(workspace).resolve() reject_dest = (ev_dir / skill_name).resolve() if skill_path.parent != skills_dir: print("Skill path escapes the skills directory") return 1 if reject_dest.parent != ev_dir: print("Destination escapes the evidence directory") return 1 ``` 3. **Ensure source and destination are different.** ```python if skill_path == reject_dest: print("Source and destination must differ") return 1 ``` 4. **Avoid recursively deleting an existing destination automatically.** Fail safely if the archive destination exists, or generate a unique destination name. If replacement is required, verify the resolved destination is a direct child of the evidence directory before deletion. 5. **Use a sanitized identifier for evidence filenames.** Do not interpolate an untrusted path into the filename. Generate a safe identifier or restrict names to a conservative allowlist such as letters, digits, underscores, periods, and hyphens. 6. **Apply the same validation to `quarantine` and `unquarantine`.** Although their current rename behavior does not expose the same direct `rmtree` sink, they also construct filesystem paths from `skill_name` and should enforce identical workspace boundaries. 7. **Add r ...[truncated 170 chars]
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Intent-Code Divergence

High
Confidence
97% confidence
Finding
The docstring explicitly frames the tool as offering scanning/alerting while the implementation includes active quarantine, reject, and automated protection actions that modify or remove other skill directories. This mismatch is dangerous because operators may invoke the tool expecting read-only analysis, but it can perform destructive changes to the workspace and disable unrelated skills.

Credential Access

High
Category
Privilege Escalation
Content
QUARANTINE_PREFIX = ".quarantined-"
STANDARD_DOTFILES = {".gitignore", ".gitattributes", ".gitmodules", ".gitkeep",
    ".editorconfig", ".eslintrc", ".eslintrc.json", ".eslintrc.js",
    ".prettierrc", ".prettierrc.json", ".prettierignore", ".npmrc", ".npmignore",
    ".nvmrc", ".node-version", ".python-version", ".flake8", ".pylintrc",
    ".mypy.ini", ".env.example", ".env.template", ".dockerignore",
    ".browserslistrc", ".claude"}
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Obfuscated Code

High
Category
Supply Chain
Content
r"eval\s*\(\s*(?:base64\.)?b64decode\s*\(", "CRITICAL"),
    ("exec-compile-obfuscated", "exec(compile(...)) with potential obfuscation",
     r"exec\s*\(\s*compile\s*\(", "HIGH"),
    ("dynamic-import-os-system", "__import__('os').system(...) dynamic import chain",
     r"__import__\s*\(\s*['\"]os['\"]\s*\)\s*\.\s*system\s*\(", "CRITICAL"),
    ("dynamic-import-subprocess", "Dynamic import of subprocess module",
     r"__import__\s*\(\s*['\"]subprocess['\"]\s*\)", "HIGH"),
Confidence
85% confidence
Finding
Code contains obfuscation (base64, hex encoding with execution). This is often used to hide malicious functionality.

Missing User Warnings

High
Confidence
96% confidence
Finding
The reject flow deletes any prior archive directory with shutil.rmtree and then moves the target skill into evidence storage, all without confirmation. This can destroy prior forensic data and remove active skills based on scanner output, compounding the blast radius of false positives or misuse.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
## The Problem

You install skills from the community and trust them to run in your workspace. Any skill can contain obfuscated payloads, post-install hooks that execute arbitrary code, or supply chain attacks that silently modify other skills. Existing security tools verify file integrity after the fact — nothing inspects skills for supply chain risks before they run.

## Install
Confidence
80% confidence
Finding
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
## The Problem

You install skills from the community and trust them to run in your workspace. Any skill can contain obfuscated payloads, post-install hooks that execute arbitrary code, or supply chain attacks that silently modify other skills. Existing security tools verify file integrity after the fact — nothing inspects skills for supply chain risks before they run.

## Install
Confidence
80% confidence
Finding
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The README presents these items as supported ('Yes') features, but elsewhere in the same document the usage examples only cover scanning, inspection, threat stats, updates, and status. This creates an intent/documentation divergence because the file advertises stronger active-control capabilities than the documented commands substantiate.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This markdown file advertises behaviors that can affect system integrity and workflow, including automatically quarantining skills and blocking installation. The description does not warn users about possible modification, movement, or prevention of access to skill files, which fits the missing-warning criteria for markdown files.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill advertises and likely requires broad capabilities including environment access, file reads/writes, shell execution, and potentially network-related inspection logic, but it does not declare any explicit tool scope or permissions boundary in the manifest. For a security-scanning skill that inspects other skills and updates a threat database, this creates unnecessary ambiguity and overprivilege risk: users and hosts cannot easily enforce least privilege or understand what the skill is allowed to do before invocation.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
These commands operate on sibling skill directories and can quarantine or reject other installed skills, which exceeds a passive scanning role and creates cross-skill integrity risk. In an agent-skill ecosystem, a skill that can unilaterally rename, move, or remove peers is dangerous even if framed as security tooling, because it becomes a privileged workspace mutator.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The automated protection sweep scans all skills and auto-quarantines any that meet its threshold, without interactive approval at execution time. In this context, centralized unilateral remediation is risky because heuristic or false-positive detections can disrupt legitimate skills across the workspace and create denial-of-service or tampering effects.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The auto-quarantine path renames skill directories immediately once a score threshold is met, with no confirmation or human review step. Because the decision is heuristic, this can disable legitimate components and constitutes an unsafe automated state change affecting other skills.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/sentinel.py:44