Back to skill

Security audit

Website Auditor

Security checks for vulnerabilities and agentic risk

Overview

This website-audit skill is coherent, but it needs review because it can fetch any URL, return full page contents, and send targets to Google without strong scoping.

Install only if you intend to audit public websites or run it inside an environment with outbound network controls. Avoid using it on internal, customer-confidential, pre-release, or token-bearing URLs unless you add URL allowlisting, private-address blocking, redirect validation, response-size limits, and clear controls for raw_html and Google PageSpeed submission.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:40
Finding

Server-Side Request Forgery Through Unrestricted Website Audit URLs

Content
View full analysis
dict: try: r = requests.get(url, headers=HEADERS, timeout=10, allow_redirects=True) return { "status_code": r.status_code, "is_live": r.status_code < 400, "final_url": r.url, "raw_html": r.text if r.status_code < 400 else "", "response_headers": dict(r.headers) } except requests.exceptions.SSLError: return {"status_code": "SSL_ERROR", "is_live": False, "raw_html": "", "has_ssl": False} except requests.exceptions.ConnectionError: return {"status_code": "DEAD", "is_live": False, "raw_html": ""} except requests.exceptions.Timeout: return {"status_code": "TIMEOUT", "is_live": False, "raw_html": ""} ``` ```python def audit_website(url: str) -> dict: """Run all 8 signals. Returns complete audit dict.""" result = {"url": url, "audit_timestamp": datetime.now().isoformat()} ext = tldextract.extract(url) result["domain"] = f"{ext.domain}.{ext.suffix}" # Signal 1: Status + HTML status = check_status(url) result.update(status) if not result.get("is_live"): result["has_ssl"] = False return result # Dead site — score it and move on html = result.get("raw_html", "") headers = result.get("response_headers", {}) ``` ```python async def audit_website_async(session: aiohttp.ClientSession, url: str) -> dict: """Async version — fetch HTML only. Run other checks synchronously after.""" try: async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp: html = await resp.text() headers = dict(resp.headers) status = resp.status except Exception as e: retu ...[truncated 2667 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:342
Finding

TLS Certificate Verification Disabled in the Asynchronous Auditor

Content
View full analysis
list[dict]: connector = aiohttp.TCPConnector(limit=concurrency, ssl=False) headers = {"User-Agent": "Mozilla/5.0 (compatible; LeadScanner/1.0)"} async with aiohttp.ClientSession(connector=connector, headers=headers) as session: tasks = [audit_website_async(session, url) for url in urls] return await asyncio.gather(*tasks, return_exceptions=False) ``` ### Technical Analysis The asynchronous HTTP connector is created with `ssl=False`. In this configuration, TLS certificate validation is disabled for HTTPS requests made through the session. TLS certificate validation normally confirms that the server certificate is issued by a trusted authority, is valid for the requested hostname, and is within its validity period. Disabling this validation allows an endpoint presenting an invalid, self-signed, expired, or hostname-mismatched certificate to be accepted. This contradicts the skill's purpose of auditing website quality and SSL status because the asynchronous path can consume content from an unauthenticated endpoint. ### Attack Path 1. A victim audits an HTTPS website using `audit_batch_async`. 2. An attacker gains a network interception position, manipulates DNS, or otherwise redirects traffic to an attacker-controlled server. 3. The attacker presents an invalid or self-signed TLS certificate. 4. The asynchronous client accepts the certificate because verification is disabled. 5. The attacker returns forged HTML and headers. 6. The forged response is processed as if it came from the intended website and is included in the audit results. ### Impact Assessment An attacker with a suitable network or name-resolution position can compromise the integrity and confidentiality of asynchr ...[truncated 416 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:42
Finding

Unbounded Remote Response Bodies Can Exhaust Process Memory

Content
View full analysis
dict: try: r = requests.get(url, headers=HEADERS, timeout=10, allow_redirects=True) return { "status_code": r.status_code, "is_live": r.status_code < 400, "final_url": r.url, "raw_html": r.text if r.status_code < 400 else "", "response_headers": dict(r.headers) } ``` ```python async def audit_website_async(session: aiohttp.ClientSession, url: str) -> dict: """Async version — fetch HTML only. Run other checks synchronously after.""" try: async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp: html = await resp.text() headers = dict(resp.headers) status = resp.status except Exception as e: return {"url": url, "status_code": "DEAD", "is_live": False, "raw_html": ""} ``` ### Technical Analysis Both implementations read the complete response body into memory. The synchronous request does not use streaming, and `r.text` materializes the body as a decoded string. The asynchronous implementation similarly calls `resp.text()` without imposing a maximum body size. No validation is performed against `Content-Length`, content type, decompressed size, or a configured response limit. A request timeout does not reliably prevent this weakness because a server may transmit a very large response continuously within the timeout window. Compressed responses can further amplify memory consumption after decompression. The asynchronous batch runner creates several concurrent audit tasks, meaning multiple oversized bodies can be buffered simultaneously. ### Attack Path 1. An attacker supplies one or more URLs under the attacker's control. 2. The target servers return very l ...[truncated 897 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:4
Finding

Unpinned and Incomplete Third-Party Dependency Specification

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill returns full raw page HTML and explicitly notes it is reused by another skill, which expands data collection beyond a narrowly scoped website-health audit. This can expose captured page content, embedded tokens, personal data, or proprietary text to downstream components that do not need full-page content, increasing unnecessary data propagation risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The output schema includes full raw HTML from audited sites for downstream reuse, but the skill description does not clearly disclose that entire page content is collected and returned. This can unintentionally capture personal data, secrets embedded in markup, internal page content, or copyrighted material, then spread it to other components.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill sends audited URLs to Google's PageSpeed API, disclosing user-supplied targets to a third party without clearly warning users in the skill description. In some environments, target URLs may be sensitive, internal, pre-release, or customer-confidential, so silent outbound sharing creates a privacy and data-governance risk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The manifest and documentation consistently describe an 8-signal audit with a specific output format, but the PageSpeed function returns pagespeed_accessibility, which is not listed in the declared output or described as one of the eight signals. This is a scope mismatch in the skill's stated behavior, even though it is related to website auditing.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The docstring states 'fetch HTML only' and suggests other checks run synchronously after, but the function immediately computes copyright age, mobile-friendliness, design signals, and last-modified values before returning. This is an active contradiction between the function's documentation and its real behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.