Back to skill

Security audit

Web Research

Security checks for vulnerabilities and agentic risk

Overview

This research skill is mostly coherent, but its HTML report builder can embed untrusted web content as executable HTML when a report is opened.

Install only if you are comfortable with the skill searching and fetching web pages and writing research reports into the workspace. Avoid using the HTML output or opening generated HTML reports from untrusted research topics until the report builder escapes dynamic content and validates URLs; Markdown or JSON output is lower risk.

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

Warning
Location
scripts/research.py:259
Finding
Stored HTML and Script Injection in Generated Research Reports## Vulnerability Details **File Location**: `scripts/research.py`, lines 259–316 **Vulnerability Type**: Unescaped untrusted content in HTML output **Risk Level**: Medium ### Vulnerable Code ```python html = f"""<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Research Report: {config.question[:80]}</title> <style> body {{ font-family: system-ui, sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }} h1 {{ color: #1a1a2e; }} h2 {{ border-bottom: 2px solid #eee; padding-bottom: 0.3rem; }} .meta {{ color: #666; margin-bottom: 1rem; }} .quality {{ color: {color}; font-weight: bold; }} table {{ border-collapse: collapse; width: 100%; margin: 1rem 0; }} th, td {{ border: 1px solid #ddd; padding: 8px 12px; text-align: left; }} th {{ background: #f5f5f5; }} .source {{ font-size: 0.9em; color: #555; }} </style> </head> <body> <h1>Research Report</h1> <p class="meta"> <strong>Question:</strong> {config.question}<br> <strong>Date:</strong> {config.date} | <strong>Duration:</strong> ~{config.duration}s | <strong>Quality:</strong> <span class="quality">{avg_quality:.1f}/1.0</span> </p> <h2>Executive Summary</h2> <ul> {''.join([f'<li>{f.get("summary", "No summary.")}' for f in findings[:5]])} </ul> <h2>Key Findings</h2> """ for i, f in enumerate(findings, 1): score = quality_scores[i - 1] if i <= len(quality_scores) else 0 html += f""" <div style="margin: 1rem 0; padding: 1rem; border-left: 3px solid #4a90d9; background: #fafafa;"> <strong>{i}. {f.get('title', f'Finding {i}')} </strong> <span class="quality">({score:.1f})</span> <p>{f.get('details', 'No details.')}</p> </div> """ html += f""" <h2>Quality Assessment</h2> <table> <tr><th>Metric</th><th>Value</th></tr> <tr><td>Average source quality</td><td>{avg_quality:.1f}/1.0</td></tr> <tr><td>Sources after dedup</td><td>{len(sources)}</td></tr> <tr><td>Follow-up rounds</td><td>{config.followups}</td></tr> </table> <h2>Limitations</h2> <ul> <li> ...[truncated 3124 chars]
Remediation
## Remediation Suggestions 1. Apply contextual HTML escaping to every dynamic text value before interpolation: ```python from html import escape question = escape(str(config.question), quote=True) summary = escape(str(f.get("summary", "No summary.")), quote=True) title = escape(str(f.get("title", f"Finding {i}")), quote=True) details = escape(str(f.get("details", "No details.")), quote=True) source_title = escape(str(s.get("title", "Unknown")), quote=True) ``` 2. Validate source URLs with `urllib.parse.urlparse()` and allow only explicitly supported schemes, preferably `https`. Reject dangerous or unexpected schemes such as `javascript:`, `data:`, and `file:`. 3. Render source URLs as properly encoded anchor attributes rather than raw text: ```python from html import escape from urllib.parse import urlparse def safe_https_url(value: str) -> str: value = str(value) parsed = urlparse(value) if parsed.scheme != "https" or not parsed.netloc: return "" return escape(value, quote=True) ``` 4. Prefer a template engine with automatic escaping enabled instead of assembling HTML through f-strings. 5. If reports are hosted, add a restrictive Content Security Policy that disallows inline scripts and limits resource origins. This should be defense in depth rather than a substitute for escaping. 6. Add regression tests covering payloads in every dynamic field, including: ```html <script>alert(1)</script> <img src=x onerror=alert(1)> </title><script>alert(1)</script> ``` Tests should verify that these values appear only as encoded text and never create executable DOM elements. 7. Document that fetched web content is untrusted and must not be interpreted as markup or executable instructions at any stage of report generation.
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 (2)

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
Line L378 states a concrete side effect: saving reports to a workspace path. However, the script only parses arguments, generates query variants, prints a pipeline summary, and defines report-builder functions; it never opens an output file or writes to disk, even though an --output argument exists.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The skill explicitly performs web_fetch operations and writes generated reports into workspace/research files, but the user-facing description does not clearly warn about these side effects where usage and outputs are described. This can lead users to unknowingly trigger network retrieval of third-party content and persistent local file creation, which creates transparency, privacy, and workspace hygiene risks even if the behavior is expected for a research skill.

Static analysis

No suspicious patterns detected.