Back to skill

Security audit

llms.txt File Builder

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent llms.txt builder, but its scripts can contact unexpected web addresses when given URL lists, sitemaps, redirects, or live URL checks.

Review before installing if you may run it on untrusted domains, untrusted URL lists, or in an environment that can reach internal services or cloud metadata endpoints. Use it only with trusted public sites or run it in a network-isolated environment; inspect URL lists and sitemaps before enabling automated generation or live URL checks.

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/generate_llms_txt.py:27
Finding
Unrestricted URL Fetching Enables Server-Side Request Forgery## Vulnerability Details **File Location**: `scripts/generate_llms_txt.py:27-39`, with additional affected flows at `scripts/generate_llms_txt.py:88-111`, `scripts/generate_llms_txt.py:169-179`, and `scripts/generate_llms_txt.py:196-201` **Vulnerability Type**: Server-Side Request Forgery (SSRF) through user-controlled and sitemap-controlled URLs **Risk Level**: High ### Vulnerable Code ```python def __init__(self, domain, timeout=10): self.domain = domain.replace('https://', '').replace('http://', '').rstrip('/') self.base_url = f"https://{self.domain}" self.timeout = timeout self.visited = set() self.pages = [] def fetch(self, path='', full_url=None): """Fetch a URL with error handling.""" url = full_url or urljoin(self.base_url, path) try: resp = requests.get(url, timeout=self.timeout, allow_redirects=True) if resp.status_code == 200: return resp except: pass return None ``` Sitemap URLs are filtered using an unsafe substring comparison and then fetched: ```python def get_sitemap_urls(self): """Fetch URLs from sitemap.xml.""" urls = [] # Try common sitemap locations for path in ['/sitemap.xml', '/sitemap_index.xml', '/sitemap-index.xml']: resp = self.fetch(path) if resp and resp.status_code == 200: # Parse XML import xml.etree.ElementTree as ET try: root = ET.fromstring(resp.text.encode('utf-8')) # Handle both sitemap and urlset for elem in root.iter(): if elem.tag.endswith('loc'): urls.append(elem.text.strip()) except: pass if urls: break # Filter to same domain return [u for u in urls if self.domain in u][:50] # Limit to 50 ``` Interactive and file-based generat ...[truncated 4442 chars]
Remediation
## Remediation Suggestions Apply centralized URL validation before every network request: 1. Parse URLs with `urllib.parse.urlsplit` and permit only explicit `http` and `https` schemes. 2. Reject URLs containing username or password components. 3. Normalize hostnames and compare the parsed hostname exactly against the approved hostname. If subdomains are needed, require either exact equality or a dot-boundary suffix such as `host.endswith("." + approved_host)`. 4. Resolve all destination hostnames before connecting. Reject every resolved IPv4 and IPv6 address that is loopback, private, link-local, multicast, reserved, unspecified, or otherwise non-global. 5. Protect against DNS rebinding by ensuring the validated address is the address actually used for the connection. 6. Disable automatic redirects or process redirects manually. Validate the scheme, hostname, port, and resolved addresses of every redirect target before following it. 7. In sitemap mode, accept only URLs belonging to the exact original site or a narrowly defined allowlist. Replace the substring test with parsed-host comparison. 8. In interactive and URL-list modes, reject off-domain absolute URLs unless the user explicitly enables a documented and appropriately isolated cross-domain mode. 9. Restrict destination ports to `80` and `443` unless other ports are explicitly required and approved. 10. Enforce maximum response sizes and streaming download limits to reduce denial-of-service exposure. 11. Run the generator in a network-isolated environment that cannot reach cloud metadata services, loopback administration endpoints, or sensitive private networks. 12. Return explicit validation errors rather than suppressing all exceptions with a bare `except`, so rejected or suspicious destinations can be audited.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
79% confidence
Finding
The skill description presents generation, validation, and optimization behavior, while the documented workflow also implies outbound crawling/fetching of sitemaps and pages that is not declared as a permission-sensitive action. Mismatches between stated purpose and actual behavior are dangerous because users and orchestrators may authorize the skill under false assumptions, leading to unexpected network access and over-trust in its outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill description presents generation, validation, and optimization behavior, while the documented workflow also implies outbound crawling/fetching of sitemaps and pages that is not declared as a permission-sensitive action. Mismatches between stated purpose and actual behavior are dangerous because users and orchestrators may authorize the skill under false assumptions, leading to unexpected network access and over-trust in its outputs.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill advertises commands that read local files, write output files, and fetch remote website/sitemap content, but it declares no explicit tool scope or permissions. That creates a least-privilege and transparency problem: an invoking agent or reviewer cannot reliably tell that filesystem and network access may occur, increasing the chance of unintended data access or outbound requests.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger text is broad enough to match many generic requests about AI visibility, site understanding, or documentation, which can cause the skill to activate in contexts where the user did not specifically ask for llms.txt generation. Unintended invocation matters more here because the skill may prompt file operations or remote website fetching, expanding the chance of unnecessary data handling or outbound access.

Tainted flow: 'url' from input (line 189, user input) → requests.get (network output)

Medium
Category
Data Flow
Content
"""Fetch a URL with error handling."""
        url = full_url or urljoin(self.base_url, path)
        try:
            resp = requests.get(url, timeout=self.timeout, allow_redirects=True)
            if resp.status_code == 200:
                return resp
        except:
Confidence
88% confidence
Finding
The script fetches attacker-influenced URLs with requests.get, including URLs supplied from a file or interactive input, and follows redirects automatically. This creates an SSRF-style risk: if the tool is run in a trusted environment, a user can cause it to make outbound requests to arbitrary hosts, including internal services or cloud metadata endpoints, and then process the responses.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The optional `check_live_urls` feature makes outbound HTTP requests to URLs embedded in the input file. Because the URLs come from untrusted content, enabling this flag can trigger network access to arbitrary destinations, which creates SSRF-style risk, privacy leakage, and unexpected interaction with internal or sensitive network endpoints. In the context of a validator for local `llms.txt` quality checks, this network behavior is not essential to core validation and increases attack surface.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The CLI exposes `--check-urls`, but the output flow and tool behavior do not prominently disclose that enabling it causes outbound HTTP requests to third-party URLs found in the file. This can surprise users, leak validator IP/user-agent information, and cause unintended contact with attacker-controlled infrastructure. The issue is lower severity than direct exploitation, but it is still a real security/transparency concern.

Static analysis

No suspicious patterns detected.