Back to skill

Security audit

skill-guard

Security checks for vulnerabilities and agentic risk

Overview

This skill is a guarded installer that openly downloads, scans, and installs one requested ClawHub skill, with no evidence of hidden execution or exfiltration beyond the disclosed Snyk scan upload.

Install only if you are comfortable letting this helper download the requested skill, upload its staged text content to Snyk for analysis, and modify your OpenClaw workspace after a clean scan. Use trusted clawhub and uvx binaries, provide SNYK_TOKEN through a secret manager or environment, and review retained .skill-guard quarantine or backup directories if a scan or replacement fails.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (13)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/test_safe_install.py (reported line 116)May include surrounding context.

python
self.sentinel = self.base / "outside-sentinel"
        self.sentinel.write_text("preserve me")
        self.log = self.base / "calls.jsonl"
        self.env = dict(os.environ, PATH=str(self.bin) + os.pathsep + os.environ['PATH'],
                        CLAWHUB_WORKDIR=str(self.workspace), CALL_LOG=str(self.log),
                        SENTINEL=str(self.sentinel), SNYK_TOKEN="mock-authentication-only")

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/verify_live.py (reported line 46)May include surrounding context.

python
check("Required real CLIs are available", all(shutil.which(tool) for tool in ("clawhub", "uvx")))
        with tempfile.TemporaryDirectory(prefix="skill-guard-live-") as temporary:
            work = Path(temporary).resolve()
            env = dict(os.environ)
            downloaded = work / "download"
            downloaded.mkdir(mode=0o700)
            fetched = run(["clawhub", "--workdir", str(downloaded), "--dir", "skills", "--no-input",

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill clearly relies on shell execution, environment variables, and file system access, but the manifest does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization and review gap: consumers cannot easily verify the intended capability boundary, and an agent platform may grant broader access than necessary when running the installer and scanner workflow.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
65% confidence
Finding

uvx/uv tool run commands without ==version create a rug-pull risk.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
65% confidence
Finding

uvx/uv tool run commands without ==version create a rug-pull risk.

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/safe_install.py (reported line 135)May include surrounding context.

python
def run_tool(command, cwd):
    # Reports can quote credentials from skill files. Never echo raw tool output.
    try:
        return subprocess.run(command, cwd=cwd, stdin=subprocess.DEVNULL,
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=300)
    except subprocess.TimeoutExpired:
        raise Blocked("Download or scan timed out; installation blocked.") from None

Tainted flow: 'command' from os.environ.get (line 198, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/safe_install.py (reported line 135)May include surrounding context.

python
def run_tool(command, cwd):
    # Reports can quote credentials from skill files. Never echo raw tool output.
    try:
        return subprocess.run(command, cwd=cwd, stdin=subprocess.DEVNULL,
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=300)
    except subprocess.TimeoutExpired:
        raise Blocked("Download or scan timed out; installation blocked.") from None

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 · tests/test_safe_install.py (reported line 121)May include surrounding context.

python
SENTINEL=str(self.sentinel), SNYK_TOKEN="mock-authentication-only")

    def run_cli(self, *args, scenario="clean"):
        result = subprocess.run([str(ROOT / "scripts/safe-install.sh"), *args],
                                env=dict(self.env, SCENARIO=scenario),
                                capture_output=True, text=True, timeout=10)
        self.assertEqual(self.sentinel.read_text(), "preserve me")

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · tests/test_safe_install.py (reported line 137)May include surrounding context.

python
def test_clean_install_and_exact_tool_arguments(self):
        result = self.run_cli("example-skill", "--version", "1.2.3")
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertTrue((self.workspace / "skills/example-skill/SKILL.md").is_file())
        calls = [json.loads(line) for line in self.log.read_text().splitlines()]
        self.assertEqual(len(calls), 2)
        self.assertIn("--no-input", calls[0])

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · tests/test_safe_install.py (reported line 181)May include surrounding context.

python
def test_clean_install_and_exact_tool_arguments(self):
        result = self.run_cli("example-skill", "--version", "1.2.3")
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertTrue((self.workspace / "skills/example-skill/SKILL.md").is_file())
        calls = [json.loads(line) for line in self.log.read_text().splitlines()]
        self.assertEqual(len(calls), 2)
        self.assertIn("--no-input", calls[0])

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
70% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · tests/test_safe_install.py (reported line 215)May include surrounding context.

python
self.assertEqual(self.run_cli("example-skill").returncode, 1)
        self.assertFalse(self.log.exists())

    def test_world_writable_workspace_is_rejected(self):
        self.workspace.mkdir(mode=0o777)
        self.workspace.chmod(0o777)
        self.assertEqual(self.run_cli("example-skill").returncode, 1)

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · tests/test_safe_install.py (reported line 216)May include surrounding context.

python
self.assertFalse(self.log.exists())

    def test_world_writable_workspace_is_rejected(self):
        self.workspace.mkdir(mode=0o777)
        self.workspace.chmod(0o777)
        self.assertEqual(self.run_cli("example-skill").returncode, 1)
        self.assertFalse(self.log.exists())

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 · tests/verify_live.py (reported line 39)May include surrounding context.

python
def run(command, cwd, env):
        # Keep raw output in memory: even scanner errors can quote sensitive text.
        return subprocess.run(command, cwd=cwd, env=env, stdin=subprocess.DEVNULL,
                              capture_output=True, timeout=300)

    try:

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/test_safe_install.py:15

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/verify_live.py:17