Back to skill

Security audit

Web Scraper Summarizer

Security checks for vulnerabilities and agentic risk

Overview

This web summarizer is coherent, but it needs Review because its URL fetcher is not scoped and could read local or internal resources before showing or summarizing them.

Install only if you are comfortable with a local command-line scraper that can fetch arbitrary user-supplied locations. Use it on public HTTP or HTTPS pages, avoid authenticated or internal URLs, and be aware that failed summaries may print raw page text and malicious pages may influence the generated summary.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/scrape_and_summarize.py:20
Finding

Unrestricted URL Fetching Enables SSRF and Local File Disclosure

Content
View full analysis

Vulnerability Details

File Location: scripts/scrape_and_summarize.py, lines 20–29 and 109–114
Vulnerability Type: Server-Side Request Forgery (SSRF) and unrestricted URI scheme access
Risk Level: High

Vulnerable Code

python
def fetch_page(url):
    """Fetch web page content using curl."""
    try:
        result = subprocess.run(
            ['curl', '-s', '-L', '-A',
             'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
             '--max-time', '30', url],
            capture_output=True,
            text=True,
            timeout=35,
            encoding='utf-8',
            errors='replace'
        )
        return result.stdout
    except Exception as e:
        return None

The untrusted URL originates from the command line:

python
url = sys.argv[1]
style = sys.argv[2] if len(sys.argv) > 2 else 'brief'

print(f"Fetching: {url}", file=sys.stderr)

html = fetch_page(url)

Technical Analysis

The script passes a user-controlled URL directly to curl without validating its scheme, destination host, resolved IP address, port, or redirect targets. Using an argument array prevents shell metacharacters in the URL from becoming shell commands, but it does not constrain the network and filesystem resources that curl can access.

In particular, curl can support non-web schemes such as file://, while HTTP requests can target loopback interfaces, private networks, link-local services, and cloud instance metadata endpoints. The -L option follows redirects automatically, so an apparently public URL can redirect to an otherwise prohibited internal destination.

The fetched resource is subsequently cleaned and supplied to the summarizer. If Ollama fails, lines 140–145 print up to 2,000 characters of the fetched content, creating a direct disclosure channel:

python
else:
    print("\nError: Failed to generate summary with 
...[truncated 1579 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse the URL before making any request and allow only http and https.
  2. Reject URLs containing embedded credentials, ambiguous host syntax, unsupported ports, or malformed addresses.
  3. Resolve the hostname and reject every resolved loopback, private, link-local, multicast, reserved, unspecified, and documentation-range address for both IPv4 and IPv6.
  4. Explicitly block cloud metadata destinations, including link-local metadata addresses and environment-specific metadata hostnames.
  5. Disable automatic redirects or inspect and validate every redirect destination before following it.
  6. Protect against DNS rebinding by connecting only to the validated resolved address and ensuring subsequent resolutions cannot silently change the destination.
  7. Prefer a constrained HTTP client library configured with explicit protocol, redirect, response-size, and destination policies rather than invoking unrestricted curl.
  8. Apply a maximum response size while downloading instead of truncating only after the complete response has been received.
  9. Remove the raw-content fallback for potentially sensitive resources, or require explicit trusted-user approval before displaying fetched content.
  10. Run the scraper in a sandbox with no access to local secrets, metadata endpoints, or internal networks not required by its declared function.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/scrape_and_summarize.py:50
Finding

Untrusted Web Content Is Embedded Directly into the LLM Prompt

Content
View full analysis

Vulnerability Details

File Location: scripts/scrape_and_summarize.py, lines 50–76
Vulnerability Type: Indirect prompt injection
Risk Level: Medium

Vulnerable Code

python
def build_prompt(content, style, num_headings):
    """Build summarization prompt based on style."""

    style_instructions = {
        'brief': "Provide a 2-3 sentence summary capturing the main topic and key takeaways.",
        'detailed': "Provide a comprehensive summary with:\n- Main topic and purpose\n- Key points and arguments (bullet format)\n- Important details and data\n- Conclusions or takeaways",
        'bullet': "Summarize as bullet points:\n- Main topic\n- 5-7 key points\n- Any important data or numbers\n- Conclusions"
    }

    instruction = style_instructions.get(style, style_instructions['brief'])

    return f"""You are a content analyzer. Analyze the following web page content and provide a summary.

{instruction}

Output in plain text (no markdown formatting needed for brief/detailed, use - for bullets).

WEB PAGE CONTENT:
{content}

SUMMARY:"""

def summarize_with_ollama(content, style):
    """Send content to Ollama for summarization."""
    prompt = build_prompt(content, style, 5)

    try:
        result = subprocess.run(
            ['ollama', 'run', 'llama3.2', prompt],
            capture_output=True,
            text=True,
            timeout=120,
            encoding='utf-8',
            errors='replace'
        )
        return result.stdout if result.returncode == 0 else None
    except Exception:
        return None

Technical Analysis

Text controlled by the fetched web page is interpolated directly into the same prompt as the summarization instructions. There is no strong separation between trusted instructions and untrusted page data, and the prompt does not explicitly direct the model to ignore commands found inside the page.

A malicious page can t ...[truncated 1854 chars]

Remediation
View remediation

Remediation Suggestions

  1. Explicitly state in the trusted prompt that page content is untrusted data and that any instructions, requests, or policy text inside it must not be followed.
  2. Place fetched content inside clear, randomized delimiters and instruct the model to summarize only text within those delimiters.
  3. Use an Ollama interface that supports separate system and user roles, placing security rules in the highest-priority supported instruction channel.
  4. Request a constrained structured output schema and validate the response before presenting it or passing it to another workflow.
  5. Treat every generated summary as untrusted output; do not execute commands, follow URLs, or perform consequential actions based solely on it.
  6. Add detection or warning logic for common prompt-injection phrases in fetched content, while recognizing that filtering is only a defense-in-depth measure.
  7. Display the source URL and an explicit notice that the summary may contain claims influenced by untrusted page content.
  8. For high-assurance use cases, extract factual passages with deterministic parsing and require citations to source excerpts rather than relying on unrestricted generation.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (9)

Ssd 1

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill embeds untrusted web page text directly into the summarization prompt, so page-authored instructions can manipulate the model's behavior instead of being treated purely as content. In an agent setting, this is especially dangerous because hostile pages can inject instructions to exfiltrate secrets, override task intent, or produce deceptive summaries, and the skill performs no prompt-isolation or sanitization.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill advertises shell-based execution (python ...) but does not declare any permissions or allowed-tools scope, so consumers and policy engines cannot clearly constrain what tool access the skill expects. In a skill that fetches arbitrary URLs and invokes local commands (curl, Python, Ollama), missing scope increases the chance of over-privileged execution or unsafe invocation in environments that rely on manifest metadata for enforcement.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The trigger phrases are broad and generic (summarize this article, analyze this web content, extract key points from), which makes accidental or overly eager activation more likely. Because this skill performs remote URL fetching and may emit raw fetched content on failure, broad matching expands the attack surface for unintended network access, content exfiltration into the model context, or use in situations where the user did not explicitly consent to scraping a URL.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill description omits an important behavioral warning: it fetches remote URLs and, if summarization fails, may print raw page content to stdout. That can expose users to unexpected retrieval of untrusted content, disclosure of sensitive page text into logs or downstream consumers, and prompt-injection-laden content being surfaced directly rather than summarized.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/scrape_and_summarize.py (reported line 19)May include surrounding context.

python
def fetch_page(url):
    """Fetch web page content using curl."""
    try:
        result = subprocess.run(
            ['curl', '-s', '-L', '-A', 
             'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
             '--max-time', '30', url],

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest describes a web scraping and summarization skill, which justifies network access and LLM use, but not arbitrary subprocess invocation as an implementation capability. Here the code relies on subprocess.run to invoke curl, which expands the skill's operational power beyond the stated purpose and can introduce execution-surface risks unrelated to summarization itself.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script forwards scraped page content to a separate LLM process without any notice, consent, or data-sensitivity checks. Even if Ollama is local, this can expose private, internal, or access-controlled page content to another component and may violate user expectations or data-handling requirements.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/scrape_and_summarize.py (reported line 71)May include surrounding context.

python
prompt = build_prompt(content, style, 5)
    
    try:
        result = subprocess.run(
            ['ollama', 'run', 'llama3.2', prompt],
            capture_output=True,
            text=True,

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

Using an LLM for summaries is consistent with the manifest, but spawning the ollama CLI is a broader capability than simply calling a summarization service. This creates an execution pathway through local system binaries that is not stated in the skill's intended scope.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.