Back to skill

Security audit

site-monitor

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate website-monitoring purpose, but its implementation gives broad network and file-write authority that users should review before installing.

Install only if you are comfortable with this skill making HTTP requests from your machine and writing a local state file. Run it in a restricted environment, avoid checking internal or metadata-service URLs, and do not pass sensitive paths to --state-file; a safer version would restrict URL targets and store state only in a dedicated private directory.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
scripts/monitor.py:17
Finding
Server-Side Request Forgery Through an Unrestricted Target URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/monitor.py:17` and `scripts/monitor.py:69` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python r = requests.get(url, headers=headers, timeout=15) ``` The requested URL comes directly from a positional command-line argument: ```python p_check.add_argument('url') ``` ### Technical Analysis The skill sends an HTTP request to a caller-controlled URL without validating its scheme, hostname, resolved IP address, port, or redirect destination. Python Requests follows redirects by default, so validating only the initial URL would also be insufficient. An attacker can provide URLs that resolve to loopback, private, link-local, or otherwise restricted network destinations, including: - Services bound to `127.0.0.1` or `localhost` - Private network services reachable from the agent host - Link-local cloud metadata services - Internal administrative HTTP endpoints - Public hosts that redirect to internal destinations The response body is not directly returned to the caller, which limits direct data extraction. However, the script exposes success or failure, HTTP status information, and error behavior. It also computes a hash of successful response content and stores it in the state file. These behaviors can support internal service discovery and blind interaction with internal GET endpoints. ### Attack Path 1. An attacker asks the agent to check a crafted URL, such as an address on the loopback, private, or link-local network. 2. The URL is accepted as the unrestricted `url` positional argument. 3. `requests.get()` sends the request from the agent host and its trusted network context. 4. The attacker observes the reported HTTP status, success message, error, or process result. 5. The attacker repeats the operation against different hosts, ports, and paths to identify reachable services or trigger internal endpoints. 6. Alternatively, the attack ...[truncated 664 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Accept only explicitly supported schemes, normally `http` and `https`. - Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 addresses. - Disable automatic redirects or validate the destination after every redirect. - Prevent DNS rebinding by ensuring the validated address is the address used for the connection. - Prefer an explicit hostname allowlist when the expected monitoring targets are known. - Restrict ports to an approved set, such as 80 and 443, where operationally appropriate. - Apply outbound firewall or proxy controls so the process cannot reach metadata services or sensitive internal networks. - Return only minimal, normalized errors to avoid exposing internal network details. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/monitor.py:24
Finding
Arbitrary File Overwrite and Predictable Temporary-State Symlink Attack<![CDATA[ ## Vulnerability Details **File Location**: `scripts/monitor.py:24-36` and `scripts/monitor.py:70` **Vulnerability Type**: Arbitrary File Overwrite / Unsafe Temporary File **Risk Level**: High ### Vulnerable Code ```python old_state = {} if os.path.exists(state_file): with open(state_file, 'r') as f: old_state = json.load(f) status_code = r.status_code is_changed = old_state.get('hash') != content_hash with open(state_file, 'w') as f: json.dump({ 'hash': content_hash, 'status': status_code, 'last_check': str(__import__('datetime').datetime.now()) }, f) ``` The output path is caller-controlled and otherwise defaults to a predictable file under `/tmp`: ```python p_check.add_argument('--state-file', default='/tmp/monitor-state.json') ``` ### Technical Analysis The `--state-file` option allows the caller to select an arbitrary filesystem path. The file is opened in write mode, which truncates an existing target before writing JSON. No validation confines the path to an application-owned state directory. The file operations also follow symbolic links. Even when the default path is used, another local user in a shared environment may create `/tmp/monitor-state.json` as a symbolic link to a file writable by the monitor process. The separate existence check and subsequent open operations additionally create time-of-check/time-of-use opportunities. The written JSON has a predictable structure, so this primitive primarily provides file destruction or replacement rather than unrestricted content control. Nevertheless, overwriting configuration or data files with invalid JSON content may cause denial of service or alter the behavior of other applications. ### Attack Path **Caller-controlled path scenario:** 1. An attacker causes the skill to run with `--state-file` pointing to a sensitive file writable by the skill process. 2. The target website returns a successful HTTP response. 3. The script opens the sel ...[truncated 1068 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove caller control over the complete state-file path. Derive state filenames internally from a validated identifier. - Store state in a dedicated application directory owned by the service account, with directory permissions set to `0700`. - Avoid a predictable shared path under `/tmp`. - Open files with restrictive permissions and no symbolic-link following, such as `os.open()` with `O_NOFOLLOW`, `O_CREAT`, and appropriate mode flags where supported. - Verify that the destination is a regular file owned by the expected account. - Write to a securely created temporary file in the same protected directory, flush and synchronize it, and atomically replace the destination with `os.replace()`. - If custom paths are operationally necessary, resolve them and enforce that they remain beneath a configured state directory. - Run the skill as a dedicated, unprivileged account with access only to its required state directory. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:29
Finding
Unpinned and Unverified Runtime Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:29-32` **Vulnerability Type**: Insecure Dependency Installation **Risk Level**: Medium ### Vulnerable Code ```bash pip3 install requests ``` ### Technical Analysis The installation command does not pin a reviewed version of `requests` and does not verify package hashes. Consequently, installation behavior may change over time as new package and transitive dependency versions are published. The command also relies on the environment's configured Python package index. If that configuration points to an untrusted or compromised source, an unexpected artifact could be installed. No lock file, constraints file, hash verification, or isolated environment is provided to make dependency resolution reproducible. The package name shown is the legitimate `requests` name rather than an evident typosquat. Therefore, the risk arises from uncontrolled version and source resolution, not from evidence that this repository intentionally specifies a known malicious package. ### Attack Path 1. A user follows the documented installation command. 2. `pip` queries the package index configured in the user's environment. 3. It selects the latest compatible `requests` release and applicable transitive dependencies without checking project-approved hashes. 4. A compromised index, compromised release, or unexpected future dependency version is downloaded. 5. Package installation or later import executes the affected dependency code with the privileges of the user running the command. ### Impact Assessment A compromised dependency may execute code with the privileges of the installer or the account running the monitor. That could expose files and credentials accessible to that account or modify its environment. In the more common non-malicious case, uncontrolled upgrades can still create reproducibility, compatibility, and availability problems. No evidence in the audited files establishes that the current `requests` ...[truncated 32 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin `requests` to a reviewed version rather than resolving the latest available release. - Lock all transitive dependencies using a generated requirements or lock file. - Record and enforce cryptographic hashes with pip's `--require-hashes` option. - Use an explicitly configured, trusted package index or an organization-controlled package mirror. - Install dependencies inside an isolated virtual environment under a non-privileged account. - Add automated dependency vulnerability scanning and a controlled update process. - Document the reviewed Python and dependency versions required by the skill. ]]>
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises executable behavior that can read/write files and perform network access, but it does not declare any explicit tool scope or permissions. That omission weakens review and containment, because operators and automated systems cannot clearly enforce least privilege for a user-invocable skill that monitors arbitrary URLs and stores results.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The natural-language description and usage guidance are presented in Chinese, which effectively forces a specific language for users reading the skill documentation. The file does not offer an opt-in language choice or explain that the skill is intended only for a Chinese-speaking audience, which conflicts with the language/locale policy criteria.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The docstring and user-visible status messages are written in Chinese, including success and error outputs, without offering a language option or documenting a locale-specific requirement. This can violate language or locale policy when users are not given a choice or informed that the tool is region-specific.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
json.dump({
                'hash': content_hash,
                'status': status_code,
                'last_check': str(__import__('datetime').datetime.now())
            }, f)

        return {
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This code writes monitoring state to the path provided by --state-file, overwriting any existing file contents. Although the skill prints the monitoring result, it does not disclose that it will modify a local file, and the code lacks a comment or prompt warning users about this side effect.

Static analysis

No suspicious patterns detected.