371 lines
21 KiB
XML
371 lines
21 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.
|
|
|
|
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.
|
|
- Do NOT duplicate changes across locations. Each location's fix_after must ONLY contain changes for its own line range. Never repeat a change that is already covered by another location.
|
|
|
|
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>
|