<tools>
  <tool name="create_vulnerability_report">
    <description>Create a vulnerability report for a discovered security issue.

IMPORTANT: This tool includes automatic LLM-based deduplication. Reports that describe the same vulnerability (same root cause on the same asset) as an existing report will be rejected.

Use this tool to document a specific fully verified security vulnerability.

DO NOT USE:
- For general security observations without specific vulnerabilities
- When you don't have concrete vulnerability details
- When you don't have a proof of concept, or still not 100% sure if it's a vulnerability
- For tracking multiple vulnerabilities (create separate reports)
- For reporting multiple vulnerabilities at once. Use a separate create_vulnerability_report for each vulnerability.
- To re-report a vulnerability that was already reported (even with different details)

White-box requirement (when you have access to the code): You MUST include code_locations with nested XML, including fix_before/fix_after on locations where a fix is proposed.

DEDUPLICATION: If this tool returns with success=false and mentions a duplicate, DO NOT attempt to re-submit. The vulnerability has already been reported. Move on to testing other areas.

Professional, customer-facing report rules (PDF-ready):
- Do NOT include internal or system details: never mention local or absolute paths (e.g., "/workspace"), internal tools, agents, orchestrators, sandboxes, models, system prompts/instructions, connection issues, internal errors/logs/stack traces, or tester machine environment details.
- Tone and style: formal, objective, third-person, vendor-neutral, concise. No runbooks, checklists, or engineering notes. Avoid headings like "QUICK", "Approach", or "Techniques" that read like internal guidance.
- Use a standard penetration testing report structure per finding:
  1) Overview
  2) Severity and CVSS (vector only)
  3) Affected asset(s)
  4) Technical details
  5) Proof of concept (repro steps plus code)
  6) Impact
  7) Remediation
  8) Evidence (optional request/response excerpts, etc.) in the technical analysis field.
- Numbered steps are allowed ONLY within the proof of concept and remediation sections. Elsewhere, use clear, concise paragraphs suitable for customer-facing reports.
- Language must be precise and non-vague; avoid hedging.
</description>
    <parameters>
      <parameter name="title" type="string" required="true">
        <description>Clear, specific title (e.g., "SQL Injection in /api/users Login Parameter"). But not too long. Don't mention CVE number in the title.</description>
      </parameter>
      <parameter name="description" type="string" required="true">
        <description>Comprehensive description of the vulnerability and how it was discovered</description>
      </parameter>
      <parameter name="impact" type="string" required="true">
        <description>Impact assessment: what attacker can do, business risk, data at risk</description>
      </parameter>
      <parameter name="target" type="string" required="true">
        <description>Affected target: URL, domain, or Git repository</description>
      </parameter>
      <parameter name="technical_analysis" type="string" required="true">
        <description>Technical explanation of the vulnerability mechanism and root cause</description>
      </parameter>
      <parameter name="poc_description" type="string" required="true">
        <description>Step-by-step instructions to reproduce the vulnerability</description>
      </parameter>
      <parameter name="poc_script_code" type="string" required="true">
        <description>Actual proof of concept code, exploit, payload, or script that demonstrates the vulnerability. Python code.</description>
      </parameter>
      <parameter name="remediation_steps" type="string" required="true">
        <description>Specific, actionable steps to fix the vulnerability</description>
      </parameter>
      <parameter name="cvss_breakdown" type="string" required="true">
        <description>CVSS 3.1 base score breakdown as nested XML. All 8 metrics are required.

Each metric element contains a single uppercase letter value:
- attack_vector: N (Network), A (Adjacent), L (Local), P (Physical)
- attack_complexity: L (Low), H (High)
- privileges_required: N (None), L (Low), H (High)
- user_interaction: N (None), R (Required)
- scope: U (Unchanged), C (Changed)
- confidentiality: N (None), L (Low), H (High)
- integrity: N (None), L (Low), H (High)
- availability: N (None), L (Low), H (High)</description>
        <format>
          <attack_vector>N</attack_vector>
          <attack_complexity>L</attack_complexity>
          <privileges_required>N</privileges_required>
          <user_interaction>N</user_interaction>
          <scope>U</scope>
          <confidentiality>H</confidentiality>
          <integrity>H</integrity>
          <availability>N</availability>
        </format>
      </parameter>
      <parameter name="endpoint" type="string" required="false">
        <description>API endpoint(s) or URL path(s) (e.g., "/api/login") - for web vulnerabilities, or Git repository path(s) - for code vulnerabilities</description>
      </parameter>
      <parameter name="method" type="string" required="false">
        <description>HTTP method(s) (GET, POST, etc.) - for web vulnerabilities.</description>
      </parameter>
      <parameter name="cve" type="string" required="false">
        <description>CVE identifier. ONLY the ID, e.g. "CVE-2024-1234" — do NOT include the name or description.
You must be 100% certain of the exact CVE number. Do NOT guess, approximate, or hallucinate CVE IDs.
If web_search is available, use it to verify the CVE exists and matches this vulnerability. If you cannot verify it, omit this field entirely.</description>
      </parameter>
      <parameter name="cwe" type="string" required="false">
        <description>CWE identifier. ONLY the ID, e.g. "CWE-89" — do NOT include the name or parenthetical (wrong: "CWE-89 (SQL Injection)").

You must be 100% certain of the exact CWE number. Do NOT guess or approximate.
If web_search is available and you are unsure, use it to look up the correct CWE. If you cannot be certain, omit this field entirely.
Always prefer the most specific child CWE over a broad parent.
For example, use CWE-89 instead of CWE-74, or CWE-78 instead of CWE-77.

Reference (ID only — names here are just for your reference, do NOT include them in the value):
- Injection: CWE-79 XSS, CWE-89 SQLi, CWE-78 OS Command Injection, CWE-94 Code Injection, CWE-77 Command Injection
- Auth/Access: CWE-287 Improper Authentication, CWE-862 Missing Authorization, CWE-863 Incorrect Authorization, CWE-306 Missing Authentication for Critical Function, CWE-639 Authorization Bypass Through User-Controlled Key
- Web: CWE-352 CSRF, CWE-918 SSRF, CWE-601 Open Redirect, CWE-434 Unrestricted Upload of File with Dangerous Type
- Memory: CWE-787 Out-of-bounds Write, CWE-125 Out-of-bounds Read, CWE-416 Use After Free, CWE-120 Classic Buffer Overflow
- Data: CWE-502 Deserialization of Untrusted Data, CWE-22 Path Traversal, CWE-611 XXE
- Crypto/Config: CWE-798 Use of Hard-coded Credentials, CWE-327 Use of Broken or Risky Cryptographic Algorithm, CWE-311 Missing Encryption of Sensitive Data, CWE-916 Password Hash With Insufficient Computational Effort

Do NOT use broad/parent CWEs like CWE-74, CWE-20, CWE-200, CWE-284, or CWE-693.</description>
      </parameter>
      <parameter name="code_locations" type="string" required="false">
        <description>Nested XML list of code locations where the vulnerability exists. MANDATORY for white-box testing.

CRITICAL — HOW fix_before/fix_after WORK:
fix_before and fix_after are LITERAL BLOCK-LEVEL REPLACEMENTS used directly for GitHub/GitLab PR suggestion blocks. When a reviewer clicks "Accept suggestion", the platform replaces the EXACT lines from start_line to end_line with the fix_after content. This means:

1. fix_before MUST be an EXACT, VERBATIM copy of the source code at lines start_line through end_line. Same whitespace, same indentation, same line breaks. If fix_before does not match the actual file content character-for-character, the suggestion will be wrong or will corrupt the code when accepted.

2. fix_after is the COMPLETE replacement for that entire block. It replaces ALL lines from start_line to end_line. It can be more lines, fewer lines, or the same number of lines as fix_before.

3. start_line and end_line define the EXACT line range being replaced. They must precisely cover the lines in fix_before — no more, no less. If the vulnerable code spans lines 45-48, then start_line=45 and end_line=48, and fix_before must contain all 4 lines exactly as they appear in the file.

MULTI-PART FIXES:
Many fixes require changes in multiple non-contiguous parts of a file (e.g., adding an import at the top AND changing code lower down), or across multiple files. Since each fix_before/fix_after pair covers ONE contiguous block, you MUST create SEPARATE location entries for each part of the fix:

- Each location covers one contiguous block of lines to change
- Use the label field to describe how each part relates to the overall fix (e.g., "Add import for parameterized query library", "Replace string interpolation with parameterized query")
- Order fix locations logically: primary fix first (where the vulnerability manifests), then supporting changes (imports, config, etc.)

COMMON MISTAKES TO AVOID:
- Do NOT guess line numbers. Read the file and verify the exact lines before reporting.
- Do NOT paraphrase or reformat code in fix_before. It must be a verbatim copy.
- Do NOT set start_line=end_line when the vulnerable code spans multiple lines. Cover the full range.
- Do NOT put an import addition and a code change in the same fix_before/fix_after if they are not on adjacent lines. Split them into separate locations.
- Do NOT include lines outside the vulnerable/fixed code in fix_before just to "pad" the range.

Each location element fields:
- file (REQUIRED): Path relative to repository root. No leading slash, no absolute paths, no ".." traversal.
  Correct: "src/db/queries.ts" or "app/routes/users.py"
  Wrong: "/workspace/repo/src/db/queries.ts", "./src/db/queries.ts", "../../etc/passwd"
- start_line (REQUIRED): Exact 1-based line number where the vulnerable/affected code begins. Must be a positive integer. You must be certain of this number — go back and verify against the actual file content if needed.
- end_line (REQUIRED): Exact 1-based line number where the vulnerable/affected code ends. Must be >= start_line. Set equal to start_line ONLY if the code is truly on a single line.
- snippet (optional): The actual source code at this location, copied verbatim from the file.
- label (optional): Short role description for this location. For multi-part fixes, use this to explain the purpose of each change (e.g., "Add import for escape utility", "Sanitize user input before SQL query").
- fix_before (optional): The vulnerable code to be replaced — VERBATIM copy of lines start_line through end_line. Must match the actual source character-for-character including whitespace and indentation.
- fix_after (optional): The corrected code that replaces the entire fix_before block. Must be syntactically valid and ready to apply as a direct replacement.

Locations without fix_before/fix_after are informational context (e.g. showing the source of tainted data).
Locations with fix_before/fix_after are actionable fixes (used directly for PR suggestion blocks).</description>
        <format>
          <location>
            <file>src/db/queries.ts</file>
            <start_line>42</start_line>
            <end_line>45</end_line>
            <snippet>const query = (
    `SELECT * FROM users ` +
    `WHERE id = ${id}`
);</snippet>
            <label>Unsanitized input used in SQL query (sink)</label>
            <fix_before>const query = (
    `SELECT * FROM users ` +
    `WHERE id = ${id}`
);</fix_before>
            <fix_after>const query = 'SELECT * FROM users WHERE id = $1';
const result = await db.query(query, [id]);</fix_after>
          </location>
          <location>
            <file>src/routes/users.ts</file>
            <start_line>15</start_line>
            <end_line>15</end_line>
            <snippet>const id = req.params.id</snippet>
            <label>User input from request parameter (source)</label>
          </location>
        </format>
      </parameter>
    </parameters>
    <returns type="Dict[str, Any]">
      <description>Response containing:
- On success: success=true, message, report_id, severity, cvss_score
- On duplicate detection: success=false, message (with duplicate info), duplicate_of (ID), duplicate_title, confidence (0-1), reason (why it's a duplicate)</description>
    </returns>

    <examples>
<function=create_vulnerability_report>
<parameter=title>Server-Side Request Forgery (SSRF) via URL Preview Feature Enables Internal Network Access</parameter>
<parameter=description>A server-side request forgery (SSRF) vulnerability was identified in the URL preview feature that generates rich previews for user-supplied links.

The application performs server-side HTTP requests to retrieve metadata (title, description, thumbnails). Insufficient validation of the destination allows an attacker to coerce the server into making requests to internal network hosts and link-local addresses that are not directly reachable from the internet.

This issue is particularly high risk in cloud-hosted environments where link-local metadata services may expose sensitive information (e.g., instance identifiers, temporary credentials) if reachable from the application runtime.</parameter>
<parameter=impact>Successful exploitation may allow an attacker to:

- Reach internal-only services (admin panels, service discovery endpoints, unauthenticated microservices)
- Enumerate internal network topology based on timing and response differences
- Access link-local services that should never be reachable from user input paths
- Potentially retrieve sensitive configuration data and temporary credentials in certain hosting environments

Business impact includes increased likelihood of lateral movement, data exposure from internal systems, and compromise of cloud resources if credentials are obtained.</parameter>
<parameter=target>https://app.acme-corp.com</parameter>
<parameter=technical_analysis>The vulnerable behavior occurs when the application accepts a user-controlled URL and fetches it server-side to generate a preview. The response body and/or selected metadata fields are then returned to the client.

Observed security gaps:
- No robust allowlist of approved outbound domains
- No effective blocking of private, loopback, and link-local address ranges
- Redirect handling can be leveraged to reach disallowed destinations if not revalidated after following redirects
- DNS resolution and IP validation appear to occur without normalization safeguards, creating bypass risk (e.g., encoded IPs, mixed IPv6 notation, DNS rebinding scenarios)

As a result, an attacker can supply a URL that resolves to an internal destination. The server performs the request from a privileged network position, and the attacker can infer results via returned preview content or measurable response differences.</parameter>
<parameter=poc_description>To reproduce:

1. Authenticate to the application as a standard user.
2. Navigate to the link preview feature (e.g., “Add Link”, “Preview URL”, or equivalent UI).
3. Submit a URL pointing to an internal resource. Example payloads:

   - http://127.0.0.1:80/
   - http://localhost:8080/
   - http://10.0.0.1:80/
   - http://169.254.169.254/ (link-local)

4. Observe that the server attempts to fetch the destination and returns either:
   - Preview content/metadata from the target, or
   - Error/timing differences that confirm network reachability.

Impact validation:
- Use a controlled internal endpoint (or a benign endpoint that returns a distinct marker) to demonstrate that the request is performed by the server, not the client.
- If the application follows redirects, validate whether an allowlisted URL can redirect to a disallowed destination, and whether the redirected-to destination is still fetched.</parameter>
<parameter=poc_script_code>import json
import time
from urllib.parse import urljoin

import requests

BASE = "https://app.acme-corp.com"
PREVIEW_ENDPOINT = urljoin(BASE, "/api/v1/link-preview")

SESSION_COOKIE = ""  # Set to your authenticated session cookie value if needed

TARGETS = [
    "http://127.0.0.1:80/",
    "http://localhost:8080/",
    "http://10.0.0.1:80/",
    "http://169.254.169.254/",
]


def preview(url: str) -> tuple[int, float, str]:
    headers = {
        "Content-Type": "application/json",
    }
    cookies = {}
    if SESSION_COOKIE:
        cookies["session"] = SESSION_COOKIE

    payload = {"url": url}
    start = time.time()
    resp = requests.post(PREVIEW_ENDPOINT, headers=headers, cookies=cookies, data=json.dumps(payload), timeout=15)
    elapsed = time.time() - start

    body = resp.text
    snippet = body[:500]
    return resp.status_code, elapsed, snippet


def main() -> int:
    print(f"Endpoint: {PREVIEW_ENDPOINT}")
    print("Testing SSRF candidates (server-side fetch behavior):")
    print()

    for url in TARGETS:
        try:
            status, elapsed, snippet = preview(url)
            print(f"URL: {url}")
            print(f"Status: {status}")
            print(f"Elapsed: {elapsed:.2f}s")
            print("Body (first 500 chars):")
            print(snippet)
            print("-" * 60)
        except requests.RequestException as e:
            print(f"URL: {url}")
            print(f"Request failed: {e}")
            print("-" * 60)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())</parameter>
<parameter=remediation_steps>Implement layered SSRF defenses:

1. Explicit allowlist for outbound destinations
   - Only permit fetching from a maintained set of approved domains (and required schemes).
   - Reject all other destinations by default.

2. Robust IP range blocking after DNS resolution
   - Resolve the hostname and block private, loopback, link-local, and reserved ranges for both IPv4 and IPv6.
   - Re-validate on every redirect hop; do not follow redirects to disallowed destinations.

3. URL normalization and parser hardening
   - Normalize and validate the URL using a strict parser.
   - Reject ambiguous encodings and unusual notations that can bypass filters.

4. Network egress controls (defense in depth)
   - Enforce outbound firewall rules so the application runtime cannot reach sensitive internal ranges or link-local addresses.
   - If previews are required, route outbound requests through a dedicated egress proxy with policy enforcement and auditing.

5. Response handling hardening
   - Avoid returning raw response bodies from previews.
   - Strictly limit what metadata is returned and apply size/time limits to outbound fetches.

6. Monitoring and alerting
   - Log and alert on preview attempts to unusual destinations, repeated failures, high-frequency requests, or attempts to access blocked ranges.</parameter>
<parameter=cvss_breakdown>
  <attack_vector>N</attack_vector>
  <attack_complexity>L</attack_complexity>
  <privileges_required>L</privileges_required>
  <user_interaction>N</user_interaction>
  <scope>C</scope>
  <confidentiality>H</confidentiality>
  <integrity>H</integrity>
  <availability>L</availability>
</parameter>
<parameter=endpoint>/api/v1/link-preview</parameter>
<parameter=method>POST</parameter>
<parameter=cwe>CWE-918</parameter>
<parameter=code_locations>
  <location>
    <file>src/services/link-preview.ts</file>
    <start_line>45</start_line>
    <end_line>48</end_line>
    <snippet>  const options = { timeout: 5000 };
  const response = await fetch(userUrl, options);
  const html = await response.text();
  return extractMetadata(html);</snippet>
    <label>Unvalidated user URL passed to server-side fetch (sink)</label>
    <fix_before>  const options = { timeout: 5000 };
  const response = await fetch(userUrl, options);
  const html = await response.text();
  return extractMetadata(html);</fix_before>
    <fix_after>  const validated = await validateAndResolveUrl(userUrl);
  if (!validated) throw new ForbiddenError('URL not allowed');
  const options = { timeout: 5000 };
  const response = await fetch(validated, options);
  const html = await response.text();
  return extractMetadata(html);</fix_after>
  </location>
  <location>
    <file>src/services/link-preview.ts</file>
    <start_line>2</start_line>
    <end_line>2</end_line>
    <snippet>import { extractMetadata } from '../utils/html';</snippet>
    <label>Add import for URL validation utility</label>
    <fix_before>import { extractMetadata } from '../utils/html';</fix_before>
    <fix_after>import { extractMetadata } from '../utils/html';
import { validateAndResolveUrl } from '../utils/url-validator';</fix_after>
  </location>
  <location>
    <file>src/routes/api/v1/links.ts</file>
    <start_line>12</start_line>
    <end_line>12</end_line>
    <snippet>const userUrl = req.body.url</snippet>
    <label>User-controlled URL from request body (source)</label>
  </location>
</parameter>
</function>
    </examples>
  </tool>
</tools>
