From 5b54530c63d2bfe45ad2ce8cc21566902cfabb14 Mon Sep 17 00:00:00 2001 From: GangGreenTemperTatum <104169244+GangGreenTemperTatum@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:43:26 -0400 Subject: [PATCH] feat(web-security): add structured findings via produces item types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the platform items subsystem for the web-security capability. Adds two domain-specific Pydantic models alongside the built-in finding and asset types: - WebVulnerability: full-lifecycle vulnerability record with CVSS 3.1 and 4.0 scoring, CWE/OWASP classification, affected endpoint and parameter, evidence, reproduction steps, attacker model, chain suggestions, and validation verdict. Designed for bug bounty triage and professional pentest reporting. - WebEndpoint: discovered attack surface record for endpoints, services, and API surfaces — URL, method, technology fingerprint, auth requirement, parameters, and response characteristics. The produces config in capability.yaml wires these into the runtime's report_item / update_item / link_items tools automatically. Items land in the project Items UI with severity filtering, triage workflow, and cross-item linking. Refs: CAP-1135 --- capabilities/web-security/capability.yaml | 5 + capabilities/web-security/items.py | 365 ++++++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 capabilities/web-security/items.py diff --git a/capabilities/web-security/capability.yaml b/capabilities/web-security/capability.yaml index 58e6049..b70b42d 100644 --- a/capabilities/web-security/capability.yaml +++ b/capabilities/web-security/capability.yaml @@ -12,6 +12,11 @@ description: > archive extraction vulnerability crafting with archivealchemist, and EXIF metadata manipulation via exiftool. +produces: + values: [finding, asset] + web_vulnerability: items:WebVulnerability + web_endpoint: items:WebEndpoint + hooks: - hooks/interrupted_tool_result.py diff --git a/capabilities/web-security/items.py b/capabilities/web-security/items.py new file mode 100644 index 0000000..cb7e48d --- /dev/null +++ b/capabilities/web-security/items.py @@ -0,0 +1,365 @@ +"""Structured item types for the web-security capability. + +These Pydantic models define the schemas for items emitted via +``report_item`` during web application pentesting, bug bounty, and +vulnerability research engagements. They are referenced by the +``produces`` key in ``capability.yaml`` and validated at emit time +so the agent gets immediate feedback on malformed payloads. + +The platform's built-in ``finding`` and ``asset`` types are also +enabled alongside these domain-specific types. + +Design principles +----------------- +- Fields mirror what bug bounty platforms (HackerOne, Bugcrowd), + vuln databases (NVD, CWE/MITRE), and professional pentest reports + actually require — not theoretical nice-to-haves. +- Optional fields are genuinely optional: a black-box tester may not + have source-level detail, and that's fine. +- CVSS vectors are stored as strings so the platform can parse and + render them; scores are floats for filtering/sorting. +- Evidence fields accept markdown so the agent can embed HTTP + request/response pairs, code snippets, and reproduction steps + inline. +""" + +from __future__ import annotations + +import typing as t + +from pydantic import BaseModel, ConfigDict, Field + +# --------------------------------------------------------------------------- +# Shared enums / literals +# --------------------------------------------------------------------------- + +Severity = t.Literal["critical", "high", "medium", "low", "informational"] + +Confidence = t.Literal["confirmed", "firm", "tentative"] +""" +PTES/OWASP-style confidence: +- confirmed: PoC executed, impact demonstrated +- firm: strong evidence, reproduction pending or partial +- tentative: pattern match or behavioral signal, needs validation +""" + +ValidationVerdict = t.Literal[ + "confirmed", + "chain_confirmed", + "partially_confirmed", + "cannot_reproduce", + "false_positive", + "untested", +] +"""Mirrors the finding-validator agent's verdict vocabulary.""" + + +# --------------------------------------------------------------------------- +# Web Vulnerability — the core deliverable +# --------------------------------------------------------------------------- + + +class CvssV31(BaseModel): + """CVSS 3.1 score and vector string.""" + + model_config = ConfigDict(extra="ignore") + + vector: str = Field( + ..., + description=( + "Full CVSS 3.1 vector string, e.g. " + "'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N'." + ), + ) + score: float = Field( + ..., + ge=0.0, + le=10.0, + description="CVSS 3.1 base score (0.0–10.0).", + ) + + +class CvssV40(BaseModel): + """CVSS 4.0 score and vector string.""" + + model_config = ConfigDict(extra="ignore") + + vector: str = Field( + ..., + description=( + "Full CVSS 4.0 vector string, e.g. " + "'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N'." + ), + ) + score: float = Field( + ..., + ge=0.0, + le=10.0, + description="CVSS 4.0 base score (0.0–10.0).", + ) + + +class WebVulnerability(BaseModel): + """A confirmed or suspected web application vulnerability. + + The primary structured output for web pentesting, bug bounty, and + vulnerability research. Captures everything needed for triage, + reporting, and downstream validation — from a quick blind-SSRF + lead to a fully reproduced IDOR chain with CVSS scores. + """ + + model_config = ConfigDict(extra="ignore") + + # -- Identity & classification ---------------------------------------- + + title: str = Field( + ..., + min_length=1, + max_length=512, + description=( + "Short, specific title. Format: " + "'[Vuln Type] in [Component] via [Vector] leading to [Impact]'. " + "Example: 'Stored XSS in Comment Renderer via Markdown Image Alt Text'." + ), + ) + severity: Severity = Field( + ..., + description="Impact-based severity: critical, high, medium, low, or informational.", + ) + confidence: Confidence = Field( + "tentative", + description=( + "How solidly the vulnerability is established. " + "'confirmed' = PoC executed with demonstrated impact. " + "'firm' = strong evidence, reproduction pending or partial. " + "'tentative' = behavioral signal or pattern match, needs validation." + ), + ) + vulnerability_class: str | None = Field( + None, + description=( + "Vulnerability family name. Examples: 'SQL Injection', " + "'Stored XSS', 'IDOR', 'SSRF', 'SSTI', 'Broken Access Control', " + "'Race Condition', 'Path Traversal', 'Open Redirect', " + "'Prompt Injection'." + ), + ) + cwe: str | None = Field( + None, + description="CWE identifier, e.g. 'CWE-89' for SQL Injection.", + ) + owasp_category: str | None = Field( + None, + description=( + "OWASP Top 10 (2021) category, e.g. 'A01:2021 – Broken Access Control'." + ), + ) + + # -- Affected surface ------------------------------------------------- + + affected_endpoint: str = Field( + ..., + description=( + "Primary affected URL or API endpoint, including method if relevant. " + "Example: 'POST https://api.example.com/v2/users/{id}/profile'." + ), + ) + affected_parameter: str | None = Field( + None, + description=( + "The specific parameter, header, cookie, or input field that carries " + "the payload or is misconfigured. Example: 'user_id (path parameter)', " + "'X-Forwarded-Host (header)', 'comment (JSON body field)'." + ), + ) + affected_component: str | None = Field( + None, + description=( + "Application component, feature, or module affected. " + "Example: 'User Profile API', 'Payment Webhook Handler', " + "'Admin Dashboard'." + ), + ) + + # -- Evidence & reproduction ------------------------------------------ + + description: str = Field( + ..., + description=( + "Technical root cause explanation. What is wrong and why it is " + "exploitable. 2–4 paragraphs. Do NOT repeat the title or PoC steps." + ), + ) + evidence: str | None = Field( + None, + description=( + "Concrete proof in markdown: HTTP request/response pairs, payloads, " + "status codes, screenshots references, OOB callback logs, extracted " + "data samples, or timing measurements. Use fenced code blocks for " + "HTTP exchanges." + ), + ) + reproduction_steps: str | None = Field( + None, + description=( + "Numbered step-by-step reproduction instructions. Must be " + "independently reproducible — no assumed context. Include exact " + "URLs, payloads, and expected responses." + ), + ) + + # -- Impact & scoring ------------------------------------------------- + + impact: str | None = Field( + None, + description=( + "What the attacker gains: data exfiltration, account takeover, " + "RCE, privilege escalation, etc. State the before/after delta. " + "Concrete demonstrated impact, not theoretical worst-case." + ), + ) + cvss_v31: CvssV31 | None = Field( + None, + description="CVSS 3.1 base score and vector string.", + ) + cvss_v40: CvssV40 | None = Field( + None, + description="CVSS 4.0 base score and vector string.", + ) + + # -- Attacker model --------------------------------------------------- + + attacker_capability: str | None = Field( + None, + description=( + "What the attacker must already have or be able to do. " + "Examples: 'Unauthenticated network access', " + "'Authenticated as low-privilege user', " + "'Victim must click a crafted link'." + ), + ) + exploit_prerequisites: str | None = Field( + None, + description=( + "Environmental conditions required for exploitation. " + "'default' if no special config is needed. Otherwise: " + "versions, features, deployment mode, etc." + ), + ) + + # -- Validation state ------------------------------------------------- + + validation_verdict: ValidationVerdict = Field( + "untested", + description=( + "Current validation status. Set to 'confirmed' only when PoC " + "is fully reproduced with demonstrated impact." + ), + ) + suggested_chain: str | None = Field( + None, + description=( + "If this finding chains with another (e.g. SSRF → cloud metadata, " + "open redirect → OAuth token theft), describe the second leg and " + "the escalated impact." + ), + ) + + # -- Metadata --------------------------------------------------------- + + metadata: dict[str, t.Any] = Field( + default_factory=dict, + description=( + "Additional machine-readable context. Common keys: " + "'tech_stack', 'waf_detected', 'program_handle', " + "'bug_bounty_platform', 'confidence_trace_id'." + ), + ) + + +# --------------------------------------------------------------------------- +# Web Endpoint — discovered attack surface +# --------------------------------------------------------------------------- + + +class WebEndpoint(BaseModel): + """A discovered web endpoint, service, or API surface worth tracking. + + Use for mapping the attack surface: API endpoints, admin panels, + authenticated routes, interesting parameters, technology + fingerprints, and other assets the agent discovers during recon + or testing that may be relevant to current or future findings. + """ + + model_config = ConfigDict(extra="ignore") + + title: str = Field( + ..., + min_length=1, + max_length=512, + description=( + "Short label for the endpoint or service. " + "Example: 'Admin API — user management', " + "'GraphQL introspection endpoint', " + "'S3 bucket (public-read)'." + ), + ) + url: str = Field( + ..., + description=( + "Full URL or URL pattern. " + "Example: 'https://api.example.com/admin/users', " + "'https://cdn.example.com/assets/*'." + ), + ) + method: str | None = Field( + None, + description="HTTP method if specific. Example: 'POST', 'PUT', 'DELETE'.", + ) + endpoint_type: str | None = Field( + None, + description=( + "Kind of endpoint. Examples: 'api', 'admin_panel', 'webhook', " + "'graphql', 'websocket', 'file_upload', 'auth', 'oauth', " + "'health_check', 'debug', 'static_asset', 'cdn'." + ), + ) + technology: str | None = Field( + None, + description=( + "Detected technology, framework, or server. " + "Example: 'Express.js', 'Spring Boot', 'Next.js', " + "'Apache httpd 2.4', 'Cloudflare'." + ), + ) + auth_required: bool | None = Field( + None, + description="Whether this endpoint requires authentication.", + ) + description: str | None = Field( + None, + description="What this endpoint does and why it is interesting.", + ) + response_fingerprint: str | None = Field( + None, + description=( + "Key response characteristics: status code, notable headers, " + "content type, or distinguishing response features. " + "Example: '200 OK, application/json, x-powered-by: Express'." + ), + ) + parameters: list[str] | None = Field( + None, + description=( + "Interesting parameters, headers, or input fields observed. " + "Example: ['user_id', 'redirect_url', 'X-Forwarded-For']." + ), + ) + metadata: dict[str, t.Any] = Field( + default_factory=dict, + description=( + "Additional machine-readable context. Common keys: " + "'cors_policy', 'csp_header', 'waf', 'cdn', " + "'openapi_spec_url', 'rate_limited'." + ), + )