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

Content
View full analysis

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

Content

No source excerpt is available for this finding.

Missing User Warnings

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

Content

No source excerpt is available for this finding.