Replace 12 flat parameters (code_file, code_before, code_after, code_diff, and 8 CVSS fields) with structured nested XML fields: code_locations with co-located fix_before/fix_after per location, cvss_breakdown, and cwe. This enables multi-file vulnerability locations, per-location fixes with precise line numbers, data flow representation (source/sink), CWE classification, and compatibility with GitHub/GitLab PR review APIs.
323 lines
18 KiB
XML
323 lines
18 KiB
XML
<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.
|
|
|
|
Order: first location is where the issue manifests (typically the sink). Additional locations provide data flow context (source → propagation → sink).
|
|
|
|
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 code begins. Must be a positive integer. You must be certain of this number — do not guess or approximate. Go back and verify against the actual file content if needed.
|
|
- end_line (REQUIRED): Exact 1-based line number where the vulnerable code ends. Must be >= start_line. Set equal to start_line if the vulnerability is on a single line.
|
|
- snippet (optional): The actual source code at this location, copied verbatim from the file. Do not paraphrase or summarize code — paste it exactly as it appears.
|
|
- label (optional): Short role description for this location in the data flow, e.g. "User input from request parameter (source)", "Unsanitized input passed to SQL query (sink)".
|
|
- fix_before (optional): The vulnerable code to be replaced, copied verbatim. Must match the actual source exactly — do not paraphrase, summarize, or add/remove whitespace. Only include on locations where a fix is proposed.
|
|
- fix_after (optional): The corrected code that should replace fix_before. Must be syntactically valid and ready to apply as a direct replacement. Only include on locations where a fix is proposed.
|
|
|
|
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 for PR review suggestions).</description>
|
|
<format>
|
|
<location>
|
|
<file>src/db/queries.ts</file>
|
|
<start_line>42</start_line>
|
|
<end_line>42</end_line>
|
|
<snippet>db.query(`SELECT * FROM users WHERE id = ${id}`)</snippet>
|
|
<label>Unsanitized input used in SQL query (sink)</label>
|
|
<fix_before>db.query(`SELECT * FROM users WHERE id = ${id}`)</fix_before>
|
|
<fix_after>db.query('SELECT * FROM users WHERE id = $1', [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>47</start_line>
|
|
<end_line>47</end_line>
|
|
<snippet>const response = await fetch(userUrl)</snippet>
|
|
<label>Unvalidated user URL passed to server-side fetch (sink)</label>
|
|
<fix_before>const response = await fetch(userUrl)</fix_before>
|
|
<fix_after>const validated = await validateAndResolveUrl(userUrl)
|
|
if (!validated) throw new ForbiddenError('URL not allowed')
|
|
const response = await fetch(validated)</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>
|