Back to skill

Security audit

Web Monitor

Security checks for vulnerabilities and agentic risk

Overview

This website-monitoring skill is mostly purpose-aligned, but it needs Review because it can fetch arbitrary URL schemes or private destinations and store retrieved content locally.

Review before installing in environments with access to private networks, cloud metadata endpoints, or sensitive local files. Use it only for intended public web pages, avoid authenticated or sensitive pages unless you want their content stored locally, and periodically inspect or delete ~/.web-monitor data.

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/monitor.py:48
Finding

Unrestricted URL Fetching Enables SSRF and Local File Access

Content
View full analysis

Vulnerability Details

File Location: scripts/monitor.py, lines 48-61
Vulnerability Type: Server-Side Request Forgery (SSRF) and unrestricted URI scheme access
Risk Level: High

Vulnerable Code

python
def fetch_content(url: str, selector: str = None, headers: dict = None) -> str:
    req_headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) web-monitor/2.0"
    }
    if headers:
        req_headers.update(headers)
    
    req = Request(url, headers=req_headers)
    try:
        with urlopen(req, timeout=30) as resp:
            raw = resp.read().decode("utf-8", errors="replace")
    except HTTPError as e:
        raise RuntimeError(f"HTTP {e.code}: {e.reason}")

Technical Analysis

The URL accepted by the add command is passed directly to urllib.request.Request and urlopen without validating its scheme, hostname, resolved IP address, port, or redirect destinations.

The implementation does not:

  • Restrict URLs to the http and https schemes.
  • Reject loopback, private, link-local, reserved, multicast, or unspecified IP addresses.
  • Prevent access to supported local-resource schemes such as file://.
  • Validate DNS resolution results.
  • Revalidate destinations reached through HTTP redirects.
  • Restrict access to cloud instance metadata services.
  • Impose a maximum response size before reading the response into memory.

Although outbound network access is necessary for the declared website-monitoring functionality, unrestricted access to local resources and private networks exceeds the minimum privileges needed to monitor public web pages.

There is no evidence that the code deliberately transmits credentials, environment variables, snapshots, or local files to a hardcoded third party. The principal risk is that attacker-controlled URLs can cause the process to retrieve sensitive resources, after which their contents may be stored ...[truncated 1782 chars]

Remediation
View remediation

Remediation Suggestions

  1. Restrict URI schemes

    • Parse URLs with urllib.parse.urlsplit.
    • Permit only explicit http and https schemes.
    • Reject URLs containing unsupported schemes, missing hostnames, or embedded credentials.
  2. Validate resolved destinations

    • Resolve the hostname before making a request.
    • Use Python's ipaddress module to reject every resolved loopback, private, link-local, multicast, reserved, or unspecified address.
    • Explicitly block cloud metadata destinations such as 169.254.169.254.
    • Apply validation to both IPv4 and IPv6 addresses.
  3. Secure redirect handling

    • Disable automatic redirects or implement a custom redirect handler.
    • Parse, resolve, and validate every redirect destination before following it.
    • Set a small maximum redirect count.
  4. Reduce DNS rebinding exposure

    • Ensure that the address validated is the address used for the connection.
    • Where feasible, use an outbound proxy or network policy that blocks private and link-local destinations.
  5. Limit resource consumption

    • Inspect Content-Length when available.
    • Read responses incrementally and stop after a configured maximum number of bytes.
    • Apply limits to snapshot and diff retention.
  6. Apply deployment-level controls

    • Run the Skill under a dedicated, low-privilege account.
    • Deny unnecessary filesystem access.
    • Restrict outbound traffic to approved public destinations where the environment permits it.
    • Consider an explicit hostname allowlist when monitoring targets can be supplied by untrusted parties.
  7. Add security tests

    • Verify rejection of file:// URLs.
    • Verify rejection of loopback, RFC1918, link-local, metadata, IPv6-local, and alternate numeric IP representations.
    • Verify that redirects and DNS changes cannot bypass destination validation.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises capabilities that include network access plus local file and environment access, but it does not declare any explicit tool scope or permissions boundaries. That makes the operational trust model unclear and increases the risk of unintended invocation or overly broad execution in environments that rely on manifest-declared constraints.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The description contains broad trigger phrases like watching websites, tracking changes, checking updates, and monitoring content, which can match many ordinary user requests. Overbroad routing can cause the skill to activate unexpectedly and perform network fetches or local storage actions in contexts where the user did not clearly consent to this skill's behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill text explains functionality but does not clearly warn that fetched page content is stored locally in persistent snapshots and diffs. This can create privacy and data-retention risk if monitored pages contain sensitive, copyrighted, personalized, or session-dependent content that users may not expect to be written to disk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code stores an alert_on_keywords_only flag when a watch is added, and the CLI help says '--keywords-only' means 'Only alert on keyword matches, not all changes'. However, cmd_check never reads this flag and still reports generic page changes whenever content differs, so the implemented behavior does not match the declared feature/intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This markdown file explains that the skill stores tracked URLs, page snapshots, diffs, and alert records under ~/.web-monitor/, but it does not include a warning about the privacy implications of retaining fetched web content locally. Because markdown files should warn about behaviors that may affect user data or privacy, the omission is notable even though the storage location is documented.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The comment states 'Keyword check — only on ADDED text (new content from diff)', but when no prior snapshot exists the code sets added_text = new_content and runs keyword matching against all current page content. That documentation is actively misleading about when alerts can trigger.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.