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

Content
View full analysis

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
View 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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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

Medium
Category
Data Flow
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.

Content

Scanner excerpt · scripts/generate_llms_txt.py (reported line 36)May include surrounding context.

python
"""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:

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.