Back to skill

Security audit

Exposure Sentinel

Security checks for vulnerabilities and agentic risk

Overview

This skill is a purpose-aligned IP exposure checker, but users should treat its results as advisory rather than definitive.

Install only if you are comfortable with a script making thousands of requests to openclaw.allegro.earth. Treat 'not found' or 'exposed' results as a starting point, especially if network errors occur or the matching IP is in a busy subnet.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/check_ip.py:37
Finding
Incomplete scans are incorrectly reported as safe<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_ip.py:37-43`, `scripts/check_ip.py:54-56`, and `scripts/check_ip.py:129-130` **Vulnerability Type**: Fail-open error handling and misleading security result **Risk Level**: Medium ### Vulnerable Code ```python async def fetch_page(session: aiohttp.ClientSession, page_num: int, semaphore: asyncio.Semaphore): """Fetch a single page""" async with semaphore: url = BASE_URL.format(page_num) try: async with session.get(url, timeout=30) as response: if response.status == 200: return page_num, await response.text() return page_num, None except Exception as e: return page_num, f"ERROR: {e}" ``` ```python if content is None or content.startswith("ERROR:"): errors.append(f"Page {page_num}: {content or 'HTTP error'}") continue ``` ```python else: print(f"\n✅ {ip} - Not found (safe)") ``` ### Technical Analysis Failed HTTP requests and exceptions are recorded in an internal `errors` list, but the normal human-readable result does not consider that list when determining whether an address is safe. The code reports `Not found (safe)` whenever `found_pages` is empty, even if some or all 3,357 pages could not be checked. Consequently, the implementation cannot distinguish between these materially different states: 1. Every page was fetched successfully and the address was not found. 2. One or more relevant pages failed to load. 3. The entire scan failed because of a network, TLS, DNS, timeout, rate-limiting, or server problem. The documentation also describes “Not found” as safe, amplifying the false assurance produced by this fail-open behavior. ### Attack Path 1. A user invokes the Skill to determine whether a target IP appears in the exposure database. 2. Requests to the watchboard are disrupted, rate-limited, blocked, ...[truncated 1126 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Introduce explicit result states such as `exposed`, `not_found`, and `inconclusive`. 2. Return `not_found` only when every expected page was retrieved and processed successfully. 3. If any page fails, prominently report the number and identity of failed pages and classify the result as incomplete or inconclusive. 4. Return a nonzero process exit code for incomplete scans so automated systems cannot treat them as successful. 5. Include error counts and a completion indicator in JSON output. 6. Add bounded retries with exponential backoff for transient HTTP failures and rate limiting. 7. Update the documentation so absence from the database is not described as proof that an endpoint is safe. 8. Add tests covering total network failure, partial page failure, HTTP error responses, and successful complete scans. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/check_ip.py:18
Finding
Overbroad substring matching produces false exposure findings<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_ip.py:18-24` and `scripts/check_ip.py:59-64` **Vulnerability Type**: Improper input matching and insufficient result validation **Risk Level**: Medium ### Vulnerable Code ```python def get_ip_patterns(ip: str): """Generate matching patterns for IP (handling partial masking)""" parts = ip.split('.') return [ ip, # Full IP f"{parts[0]}.{parts[1]}.{parts[2]}.", # First 3 octets f"{parts[0]}.{parts[1]}.{parts[2]}•", # With masking char ] ``` ```python for pattern in patterns: if pattern in content: found_pages.append(page_num) if verbose: print(f"🎯 Found on page {page_num}!") break ``` ### Technical Analysis For every target address, the script generates a pattern containing only the first three octets followed by a period. It then performs an unrestricted substring search over the entire HTML response. For example, a search for `192.0.2.10` includes the pattern `192.0.2.`. That pattern also matches `192.0.2.20`, `192.0.2.200`, or unrelated page content containing the same text. The search is not constrained to a structured IP-address field and does not verify that the matched value uses the watchboard's documented masking format. The exact-address pattern is also evaluated as an unrestricted substring rather than as a token with address boundaries. As a result, it can match a longer textual value in some contexts. The broad three-octet pattern presents the primary false-positive risk. ### Attack Path 1. A user searches for a target such as `192.0.2.10`. 2. The script generates `192.0.2.` as one of the search patterns. 3. A watchboard page contains another address in that subnet, such as `192.0.2.20`, or unrelated text containing the prefix. 4. The raw substring check evaluates as true. 5. The page is added to `found_pages`. 6. The to ...[truncated 952 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the relevant structured HTML element or data field instead of searching the entire response body. 2. Compare full, normalized IP values for exact equality. 3. Apply masked matching only when the source value conforms to a documented and validated masked-address format. 4. Use anchored regular expressions or token boundaries if structured parsing is unavailable. 5. Do not treat a generic three-octet prefix as proof that a specific host address is listed. 6. Record the exact matched source value and verify that it corresponds to the requested address before reporting exposure. 7. Add tests for neighboring addresses in the same `/24`, address-like strings in unrelated HTML content, masked values, and exact matches. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill performs network access against an external site and its documentation explicitly instructs users to run a script that scans thousands of pages, but the skill declares no explicit tool scope or permissions. Missing scope declaration is dangerous because it weakens policy enforcement and reviewability: an agent may be allowed to make broad outbound requests without users or runtime controls clearly understanding that network access is required.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This code checks user-provided IPs by fetching thousands of pages from an external website, making the skill's use of external network access central to its behavior. While the CLI describes the purpose of checking exposure, it does not explicitly warn users that sensitive IP-related queries will be processed via requests to a third-party service.

Static analysis

No suspicious patterns detected.