Back to skill

Security audit

Clawhub Skill Infra Watchdog

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local monitoring skill, but its SSL checks can falsely report certificates as valid, which is risky for infrastructure monitoring.

Review this carefully before relying on it for certificate or HTTPS security monitoring. It appears locally scoped and not malicious, but its SSL checks are not trustworthy as shipped, and several advertised features are incomplete or inconsistent with the code.

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

Warning
Location
watchdog.py:171
Finding
HTTPS and SSL Monitoring Disables TLS Certificate Validation<![CDATA[ ## Vulnerability Details **File Location**: `watchdog.py:171-174` and `watchdog.py:274-277` **Vulnerability Type**: Improper certificate validation (CWE-295) **Risk Level**: Medium ### Vulnerable Code HTTPS endpoint monitoring disables hostname and certificate verification: ```python ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE req = urllib.request.Request(target, method='HEAD') with urllib.request.urlopen(req, context=ctx, timeout=timeout) as resp: ``` The SSL certificate check repeats the same insecure configuration: ```python ctx = ssl_module.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl_module.CERT_NONE with socket.create_connection((host, 443), timeout=10) as sock: with ctx.wrap_socket(sock, server_hostname=host) as ssock: ``` ### Technical Analysis `ssl.create_default_context()` initially enables secure certificate-chain and hostname validation. The subsequent assignments explicitly disable both controls: - `check_hostname = False` permits a certificate issued for an unrelated hostname. - `verify_mode = ssl.CERT_NONE` permits self-signed, expired, revoked, untrusted, or attacker-generated certificates. Consequently, HTTPS health checks validate only whether an endpoint returns a response; they do not establish that the response came from the intended authenticated server. The SSL-specific monitor similarly establishes an unauthenticated TLS connection, contradicting the documented SSL-validity monitoring purpose. The certificate-expiry implementation also calls `getpeercert_der()`, which is not a standard Python `SSLSocket` method, and attempts to find `notAfter=` inside PEM output. This does not reliably extract or validate the certificate expiration date. Although exceptions are converted into a warning, the monitor cannot provide the advertised trustworthy expiry assessment. ### Attack Path 1. An administrator configures an HTTPS monitor for a sensi ...[truncated 1297 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Preserve the secure defaults from `ssl.create_default_context()` by removing: ```python ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE ``` 2. Perform HTTPS requests using a validated TLS context: ```python ctx = ssl.create_default_context() req = urllib.request.Request(target, method='HEAD') with urllib.request.urlopen(req, context=ctx, timeout=timeout) as resp: ... ``` 3. For certificate-expiry monitoring, establish a verified TLS connection and retrieve the parsed peer certificate: ```python ctx = ssl.create_default_context() with socket.create_connection((host, port), timeout=10) as sock: with ctx.wrap_socket(sock, server_hostname=host) as ssock: cert = ssock.getpeercert() not_after = cert["notAfter"] expires_at = datetime.strptime( not_after, "%b %d %H:%M:%S %Y %Z" ) ``` 4. Parse the configured port instead of always connecting to port 443. 5. Distinguish certificate failures from availability failures in stored results, including hostname mismatch, untrusted issuer, expiration, and connection timeout. 6. If monitoring intentionally needs to support private certificate authorities, provide a configurable CA bundle rather than disabling verification globally. 7. Add automated tests using valid, expired, self-signed, and hostname-mismatched certificates. Tests should confirm that invalid certificates cannot produce an `up` result. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (7)

External Transmission

Medium
Category
Data Exfiltration
Content
infra-watchdog add-monitor \
  --type http \
  --name "Main API" \
  --url https://api.example.com/health \
  --expected-status 200 \
  --timeout 5
```
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def check_docker(self, container_id: str) -> Tuple[str, float, str]:
        """Check Docker container status. Returns (status, 0, details)."""
        try:
            result = subprocess.run(
                ['docker', 'inspect', container_id],
                capture_output=True,
                text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def check_disk(self, mount_point: str = '/') -> Tuple[str, float, str]:
        """Check disk usage. Returns (status, 0, details)."""
        try:
            result = subprocess.run(
                ['df', '-h', mount_point],
                capture_output=True,
                text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def check_memory(self) -> Tuple[str, float, str]:
        """Check memory usage. Returns (status, 0, details)."""
        try:
            result = subprocess.run(
                ['free', '-b'],
                capture_output=True,
                text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The function claims to check SSL certificate expiry, but it disables certificate validation and then returns 'Certificate valid' without actually parsing or comparing the certificate expiration date. This can create a false sense of safety, causing operators to miss expired or invalid certificates and undermining the monitoring system's reliability for security-sensitive infrastructure.

Session Persistence

Medium
Category
Rogue Agent
Content
def cmd_cron_install(args):
    """Install cron job for periodic checks."""
    config = load_config()
    interval = config.get('check_interval_minutes', 5)
Confidence
80% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Low
Confidence
92% confidence
Finding
This code performs network requests to user-supplied targets via urllib without any confirmation prompt or explicit user-facing notice at the point of execution. Although network monitoring is the skill's purpose, the code itself lacks disclosure that running checks will contact configured endpoints and transmit request metadata to them.

Static analysis

No suspicious patterns detected.