Back to skill

Security audit

Competitor Intel Monitor

Security checks for vulnerabilities and agentic risk

Overview

This competitor-monitoring skill has a coherent basic purpose, but it overstates its features and handles URLs and local files too broadly for safe installation without review.

Review carefully before installing. Use only trusted competitor names and public http/https URLs, and expect the tool to contact those sites and store page text locally. This should be fixed to validate URLs, constrain storage paths, limit retained data, and remove or implement unsupported claims before use in sensitive environments.

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

Error
Location
scripts/monitor.py:29
Finding
Unrestricted URL Fetching Enables SSRF and Local File Reads## Vulnerability Details **File Location**: `scripts/monitor.py`, lines 29-38 **Vulnerability Type**: Server-Side Request Forgery and unsafe URL scheme handling **Risk Level**: High ### Vulnerable Code ```python def fetch_page(url, timeout=15): """Fetch a URL and return text content.""" try: headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" } req = urllib.request.Request(url, headers=headers) with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.read().decode("utf-8", errors="replace") except Exception as e: print(f"Error fetching {url}: {e}", file=sys.stderr) return None ``` The attacker-controlled URL reaches this function through the competitor configuration: ```python def check_competitor(competitor): """Run all checks for a single competitor.""" name = competitor["name"] changes = [] print(f"\nChecking: {name}") tracks = competitor.get("trackingTypes", ["pricing", "blog"]) if "pricing" in tracks and competitor.get("pricingUrl"): result = check_page_changes(name, competitor["pricingUrl"], "pricing") if result: changes.append(result) if "blog" in tracks: blog_url = competitor.get("blogUrl", competitor["url"] + "/blog") result = check_page_changes(name, blog_url, "blog") if result: changes.append(result) if "changelog" in tracks: changelog_url = competitor.get("changelogUrl", competitor["url"] + "/changelog") result = check_page_changes(name, changelog_url, "changelog") if result: changes.append(result) # Main page check result = check_page_changes(name, competitor["url"], "main") if result: changes.append(result) ``` ### Technical Analysis The `--url` command-line ...[truncated 2258 chars]
Remediation
## Remediation Suggestions 1. Parse every supplied URL with `urllib.parse.urlsplit()` and allow only explicitly required schemes, preferably `https`. 2. Reject URLs containing embedded credentials, malformed hosts, unsupported ports, or missing hostnames. 3. Resolve the hostname before connecting and reject all loopback, private, link-local, multicast, reserved, unspecified, and metadata-service addresses using Python's `ipaddress` module. 4. Protect against DNS rebinding by ensuring the address actually used for the connection remains an approved public address. 5. Disable automatic redirects or validate the scheme, hostname, port, and resolved address of every redirect target before following it. 6. Consider a domain allowlist when the monitored competitors are known in advance. 7. Apply response-size and content-type limits to prevent memory or storage exhaustion. 8. Do not store response content from rejected or partially validated destinations. 9. Add tests for `file://`, localhost, IPv4 and IPv6 loopback, private subnets, link-local addresses, encoded IP representations, redirects to internal hosts, and DNS rebinding scenarios.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/monitor.py:55
Finding
Unsanitized Competitor Names Enable Path Traversal and Arbitrary File Writes## Vulnerability Details **File Location**: `scripts/monitor.py`, lines 55-96 **Vulnerability Type**: Path traversal and out-of-directory file write **Risk Level**: High ### Vulnerable Code ```python def get_snapshot_path(name, track_type): safe_name = name.lower().replace(" ", "-") return DATA_DIR / safe_name / f"{track_type}_latest.txt" def get_history_path(name): safe_name = name.lower().replace(" ", "-") return DATA_DIR / safe_name / "history.json" def save_snapshot(name, track_type, content): path = get_snapshot_path(name, track_type) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content) def load_snapshot(name, track_type): path = get_snapshot_path(name, track_type) if path.exists(): return path.read_text() return None def log_change(name, track_type, summary, old_hash, new_hash): path = get_history_path(name) path.parent.mkdir(parents=True, exist_ok=True) history = [] if path.exists(): try: history = json.loads(path.read_text()) except json.JSONDecodeError: history = [] history.append({ "timestamp": datetime.now().isoformat(), "type": track_type, "summary": summary, "old_hash": old_hash, "new_hash": new_hash, }) path.write_text(json.dumps(history, indent=2)) ``` The untrusted name is accepted directly by the command-line interface: ```python add_p = sub.add_parser("add") add_p.add_argument("--name", required=True) add_p.add_argument("--url", required=True) ``` ### Technical Analysis The variable `safe_name` is not safely normalized. It only lowercases the supplied name and replaces spaces with hyphens. It does not reject: - Absolute paths. - `..` traversal components. - Forward or platform-specific path separators. - Symbolic-link traversal. - Names that resolve outside ...[truncated 2589 chars]
Remediation
## Remediation Suggestions 1. Do not use a display name as a filesystem path component. Assign each competitor an opaque identifier, such as a UUID, and store the display name only as metadata. 2. If readable directory names are required, enforce a strict slug allowlist such as ASCII letters, digits, underscores, and hyphens. 3. Reject empty names, absolute paths, path separators, `.` components, and `..` components. 4. Resolve the base and candidate paths before every read or write and verify that the candidate is a descendant of the resolved `DATA_DIR`. 5. Perform the containment check on the final file path, not only on the parent directory. 6. Avoid following symbolic links. Where supported, use secure descriptor-based file operations and no-follow semantics. 7. Create files with restrictive permissions appropriate for potentially sensitive monitored content. 8. Write updates atomically through a safely created temporary file inside the validated destination directory. 9. Validate existing configuration entries when loading the configuration, because malicious names may already be present even after command-line validation is added. 10. Add tests covering absolute paths, nested traversal, mixed separators, Unicode separator lookalikes, symlink escapes, and preexisting malicious configuration files.
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 (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill claims monitoring, alerting, and scheduling capabilities that are not actually implemented, creating a deceptive operational picture for users or downstream agents. This is dangerous because users may rely on the skill for timely detection of competitor changes or alerts that never occur, causing silent monitoring gaps and incorrect security or business assumptions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises executable commands that read and write local configuration files and perform network access, but it declares no explicit tool scope or permission boundaries. In an agent environment, this can cause the skill to be invoked with broader capabilities than users expect, increasing the risk of unintended file access, persistence, or outbound requests.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The description uses broad invocation language like monitoring websites, social media, pricing, and product changes, which could cause an agent to select this skill for generic research or monitoring tasks outside intended competitor tracking. Over-broad routing can trigger unnecessary network activity, local state changes, or collection of data beyond user intent.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script persistently writes configuration and monitored content under the user's home directory without clearly disclosing what data is stored or how long it is retained. In this skill context, that means competitor URLs, change history, and page content snapshots may accumulate locally and expose browsing targets or collected business intelligence to other local users, backups, or forensic review.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill makes outbound HTTP requests to arbitrary configured competitor URLs and then stores retrieved content locally, but it does not clearly warn the user that external sites will be contacted or that fetched material will be retained. In a competitor-monitoring tool this is functionally expected, but it still creates privacy, compliance, and operational risk because requests can reveal monitoring activity and local storage can retain sensitive collected content.

Static analysis

No suspicious patterns detected.