{
  "agent": {
    "id": "vulnerability_assessment",
    "name": "Passive Vulnerability Assessment",
    "version": "2.0.0",
    "description": "A passive, OSINT-based website vulnerability assessment agent that accepts a website domain, performs non-intrusive security scanning using purpose-built Python tools, produces letter-grade risk scores (A-F) per category, and delivers a structured vulnerability report with remediation guidance. The user is consulted after scanning to decide whether to investigate further or generate the final report."
  },
  "graph": {
    "id": "vulnerability-researcher-graph",
    "goal_id": "passive-vulnerability-assessment",
    "version": "2.0.0",
    "entry_node": "intake",
    "entry_points": {
      "start": "intake"
    },
    "pause_nodes": [],
    "terminal_nodes": [],
    "conversation_mode": "continuous",
    "identity_prompt": "You are a passive website vulnerability assessment agent. You use purpose-built Python scanning tools to evaluate the security posture of websites. You produce letter-grade risk scores (A-F) per category and deliver actionable remediation guidance written for developers.",
    "nodes": [
      {
        "id": "intake",
        "name": "Intake",
        "description": "Collect the target website domain from the user and confirm the scanning scope",
        "node_type": "event_loop",
        "input_keys": [],
        "output_keys": [
          "target_domain"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "You are the intake specialist for a passive website vulnerability assessment agent.\n\n**STEP 1 \u2014 Greet and collect target (text only, NO tool calls):**\nAsk the user for the website domain they want to assess. If they already provided one, confirm it.\n\nClarify:\n- The exact domain or URL (e.g., example.com, https://app.example.com)\n- Any specific areas of concern (e.g., email security, SSL, exposed services)\n\nExplain briefly that this is a **passive, non-intrusive assessment** \u2014 we only examine publicly available information (SSL certificates, HTTP headers, DNS records, open ports, tech fingerprints, and public subdomain data). No attack payloads or exploit attempts.\n\nKeep it brief. One message, 2-3 questions max.\n\nAfter your message, call ask_user() to wait for the user's response.\n\n**STEP 2 \u2014 After the user responds, call set_output:**\n- set_output(\"target_domain\", \"the confirmed domain/URL to test, e.g. https://example.com\")",
        "tools": [],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 0,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": true,
        "success_criteria": null
      },
      {
        "id": "passive-recon",
        "name": "Passive Reconnaissance",
        "description": "Run all 6 passive scanning tools against the target domain: SSL/TLS, HTTP headers, DNS security, port scanning, tech stack detection, and subdomain enumeration",
        "node_type": "event_loop",
        "input_keys": [
          "target_domain",
          "feedback"
        ],
        "output_keys": [
          "scan_results"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "You are a passive reconnaissance specialist. Given a target domain, run all 6 scanning tools to assess the security posture. These tools are non-intrusive and OSINT-based.\n\nIf feedback is provided (not None/empty), this is a follow-up round \u2014 focus on the areas the user requested. You may skip tools that aren't relevant to the feedback. If feedback is None or empty, this is the first scan \u2014 run ALL 6 tools.\n\n**Run these tools against the target domain:**\n\n1. **ssl_tls_scan(hostname)** \u2014 Checks TLS version, certificate validity, cipher strength\n2. **http_headers_scan(url)** \u2014 Checks OWASP-recommended security headers (HSTS, CSP, X-Frame-Options, etc.)\n3. **dns_security_scan(domain)** \u2014 Checks SPF, DMARC, DKIM, DNSSEC, zone transfer\n4. **port_scan(hostname)** \u2014 TCP connect scan on top 20 common ports, flags exposed database/admin ports\n5. **tech_stack_detect(url)** \u2014 Detects web server, framework, CMS, JS libraries, cookies\n6. **subdomain_enumerate(domain)** \u2014 Queries Certificate Transparency logs for subdomains\n\n**IMPORTANT:**\n- Extract just the hostname/domain from the URL for tools that need it (e.g., \"example.com\" not \"https://example.com\")\n- Use the full URL (with https://) for http_headers_scan and tech_stack_detect\n- Run tools in batches of 2-3 to avoid overwhelming the system\n- If a tool fails, note the error and continue with the remaining tools\n\n**After all tools complete, compile results:**\n\nCombine ALL tool outputs into a single JSON object and store it:\n\nset_output(\"scan_results\", \"<JSON string containing all 6 tool results: {ssl: {...}, headers: {...}, dns: {...}, ports: {...}, tech: {...}, subdomains: {...}}>\")\n\nEach tool returns a grade_input dict \u2014 preserve these as-is, the risk scorer needs them.",
        "tools": [
          "ssl_tls_scan",
          "http_headers_scan",
          "dns_security_scan",
          "port_scan",
          "tech_stack_detect",
          "subdomain_enumerate"
        ],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 0,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": false,
        "success_criteria": null
      },
      {
        "id": "risk-scoring",
        "name": "Risk Scoring",
        "description": "Calculate weighted letter grades (A-F) per security category and overall risk score from scan results",
        "node_type": "event_loop",
        "input_keys": [
          "scan_results"
        ],
        "output_keys": [
          "risk_report"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "You calculate risk scores from scan results.\n\nGiven scan_results (a JSON string with ssl, headers, dns, ports, tech, subdomains sections), call the risk_score tool to produce letter grades.\n\n**Step 1 \u2014 Extract scan results and call risk_score:**\n\nThe risk_score tool accepts JSON strings for each category. Extract the relevant sections from scan_results and pass them:\n\nrisk_score(\n    ssl_results=\"<JSON string of the ssl section from scan_results>\",\n    headers_results=\"<JSON string of the headers section from scan_results>\",\n    dns_results=\"<JSON string of the dns section from scan_results>\",\n    ports_results=\"<JSON string of the ports section from scan_results>\",\n    tech_results=\"<JSON string of the tech section from scan_results>\",\n    subdomain_results=\"<JSON string of the subdomains section from scan_results>\"\n)\n\nIf a category has no results (tool failed), pass an empty string for that parameter.\n\n**Step 2 \u2014 Store the risk report:**\n\nset_output(\"risk_report\", \"<the complete JSON output from risk_score, including overall_score, overall_grade, categories, top_risks, and grade_scale>\")",
        "tools": [
          "risk_score"
        ],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 0,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": false,
        "success_criteria": null
      },
      {
        "id": "findings-review",
        "name": "Findings Review",
        "description": "Present risk grades and security findings to the user, ask whether to continue deeper scanning or generate the final report",
        "node_type": "event_loop",
        "input_keys": [
          "scan_results",
          "risk_report",
          "target_domain"
        ],
        "output_keys": [
          "continue_scanning",
          "feedback",
          "all_findings"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "You present security scan findings and risk grades to the user and ask for their decision.\n\n**STEP 1 \u2014 Present findings (text only, NO tool calls):**\n\nDisplay the results in this format:\n\n1. **Overall Risk Grade** \u2014 Show the letter grade prominently (e.g., \"Overall Grade: C (68/100)\")\n\n2. **Category Breakdown** \u2014 Table showing each category's grade:\n   | Category | Grade | Score | Findings |\n   |----------|-------|-------|----------|\n   | SSL/TLS | B | 85 | 1 issue |\n   | HTTP Headers | D | 45 | 4 issues |\n   | DNS Security | C | 60 | 3 issues |\n   | Network Exposure | C | 70 | 1 issue |\n   | Technology | B | 75 | 2 issues |\n   | Attack Surface | B | 80 | 1 issue |\n\n3. **Top Risks** \u2014 List the most critical findings from the risk report's top_risks field\n\n4. **Grade Scale** \u2014 Show the grade scale so the user understands the scoring:\n   - A (90-100): Excellent security posture\n   - B (75-89): Good, minor improvements needed\n   - C (60-74): Fair, notable security gaps\n   - D (40-59): Poor, significant vulnerabilities\n   - F (0-39): Critical, immediate action required\n\n5. **Options** \u2014 Ask: \"Would you like me to:\n   - **Continue scanning** \u2014 I can focus on specific weak areas for a deeper look\n   - **Generate the report** \u2014 I'll compile a full HTML risk dashboard with all findings and remediation steps\"\n\nAfter your message, call ask_user() to wait for the user's response.\n\n**STEP 2 \u2014 After the user responds, call set_output:**\n\nIf the user wants to continue:\n- set_output(\"continue_scanning\", \"true\")\n- set_output(\"feedback\", \"What the user wants investigated further, or 'focus on weakest categories'\")\n- set_output(\"all_findings\", \"Accumulated findings from all rounds so far as JSON string\")\n\nIf the user wants to stop and get the report:\n- set_output(\"continue_scanning\", \"false\")\n- set_output(\"feedback\", \"\")\n- set_output(\"all_findings\", \"All scan results and risk report combined as JSON string\")",
        "tools": [],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 0,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": true,
        "success_criteria": null
      },
      {
        "id": "final-report",
        "name": "Risk Dashboard Report",
        "description": "Generate an HTML risk dashboard with color-coded grades, category breakdown, detailed findings, and remediation steps",
        "node_type": "event_loop",
        "input_keys": [
          "all_findings",
          "risk_report",
          "target_domain"
        ],
        "output_keys": [
          "report_status"
        ],
        "nullable_output_keys": [],
        "input_schema": {},
        "output_schema": {},
        "system_prompt": "Generate an HTML risk dashboard report and deliver it to the user.\n\n**STEP 1 \u2014 Generate the HTML report (tool calls first):**\n\nCreate a self-contained HTML document with embedded CSS. Use a clean, professional security dashboard design.\n\nReport structure:\n- **Header**: Target domain, scan date, \"Security Risk Assessment\" title\n- **Overall Grade**: Large, color-coded letter grade (A=green, B=blue, C=yellow, D=orange, F=red) with numeric score\n- **Grade Scale Legend**: Show what each grade means (A through F)\n- **Category Breakdown**: 6 cards/panels, each showing:\n  - Category name\n  - Letter grade (color-coded)\n  - Numeric score\n  - Number of findings\n- **Detailed Findings by Category**: For each of the 6 categories:\n  - Category header with grade\n  - List of findings organized by severity (high -> medium -> low -> info)\n  - For each finding:\n    - Title and severity badge (color-coded)\n    - Description of the issue\n    - Why it matters (impact)\n    - **Remediation**: Clear, step-by-step fix instructions for developers\n    - Code examples where relevant (e.g., header configurations, DNS records to add)\n- **Top Risks Summary**: Prioritized action items (fix these first)\n- **Methodology**: \"This assessment used passive, OSINT-based scanning techniques...\"\n- **Disclaimer**: \"This is an automated passive assessment, not a comprehensive penetration test\"\n\nDesign requirements:\n- Every finding MUST have remediation steps\n- Write for developers, not security experts\n- Use severity color coding (red=critical/high, orange=medium, blue=low, gray=info)\n- Responsive layout, works on mobile\n- Self-contained \u2014 no external CSS/JS dependencies\n\nSave and serve:\n- save_data(filename=\"risk_assessment_report.html\", data=<html_content>)\n- serve_file_to_user(filename=\"risk_assessment_report.html\", label=\"Security Risk Assessment Report\")\n\n**STEP 2 \u2014 Present to user (text only, NO tool calls):**\nTell the user the report is ready. Summarize: overall grade, weakest category, top 3 action items.\n\nAfter presenting, call ask_user() to wait for follow-up questions.\n\n**STEP 3 \u2014 After the user responds:**\n- Answer any questions about findings or remediation\n- Call ask_user() again if they have more questions\n- When the user is satisfied: set_output(\"report_status\", \"completed\")",
        "tools": [
          "save_data",
          "serve_file_to_user"
        ],
        "model": null,
        "function": null,
        "routes": {},
        "max_retries": 3,
        "retry_on": [],
        "max_node_visits": 0,
        "output_model": null,
        "max_validation_retries": 2,
        "client_facing": true,
        "success_criteria": null
      }
    ],
    "edges": [
      {
        "id": "intake-to-passive-recon",
        "source": "intake",
        "target": "passive-recon",
        "condition": "on_success",
        "condition_expr": null,
        "priority": 1,
        "input_mapping": {}
      },
      {
        "id": "passive-recon-to-risk-scoring",
        "source": "passive-recon",
        "target": "risk-scoring",
        "condition": "on_success",
        "condition_expr": null,
        "priority": 1,
        "input_mapping": {}
      },
      {
        "id": "risk-scoring-to-findings-review",
        "source": "risk-scoring",
        "target": "findings-review",
        "condition": "on_success",
        "condition_expr": null,
        "priority": 1,
        "input_mapping": {}
      },
      {
        "id": "findings-review-to-passive-recon",
        "source": "findings-review",
        "target": "passive-recon",
        "condition": "conditional",
        "condition_expr": "str(continue_scanning).lower() == 'true'",
        "priority": -1,
        "input_mapping": {}
      },
      {
        "id": "findings-review-to-final-report",
        "source": "findings-review",
        "target": "final-report",
        "condition": "conditional",
        "condition_expr": "str(continue_scanning).lower() != 'true'",
        "priority": 1,
        "input_mapping": {}
      },
      {
        "id": "final-report-to-intake",
        "source": "final-report",
        "target": "intake",
        "condition": "on_success",
        "condition_expr": null,
        "priority": -1,
        "input_mapping": {}
      }
    ],
    "max_steps": 100,
    "max_retries_per_node": 3,
    "description": "A passive, OSINT-based website vulnerability assessment agent that accepts a website domain, performs non-intrusive security scanning using purpose-built Python tools, produces letter-grade risk scores (A-F) per category, and delivers a structured vulnerability report with remediation guidance. The user is consulted after scanning to decide whether to investigate further or generate the final report."
  },
  "goal": {
    "id": "passive-vulnerability-assessment",
    "name": "Passive Website Vulnerability Assessment",
    "description": "A passive, OSINT-based website vulnerability assessment agent that accepts a website domain, performs non-intrusive security scanning using purpose-built Python tools, produces letter-grade risk scores (A-F) per category, and delivers a structured vulnerability report with remediation guidance. The user is consulted after scanning to decide whether to investigate further or generate the final report.",
    "status": "draft",
    "success_criteria": [
      {
        "id": "risk-score-produced",
        "description": "Overall risk grade (A-F) generated from combined scan results",
        "metric": "overall_grade_generated",
        "target": "true",
        "weight": 0.25,
        "met": false
      },
      {
        "id": "category-coverage",
        "description": "At least 5 of 6 security categories scored (SSL/TLS, HTTP Headers, DNS, Network, Technology, Attack Surface)",
        "metric": "categories_scored",
        "target": ">=5",
        "weight": 0.2,
        "met": false
      },
      {
        "id": "vulnerability-discovery",
        "description": "At least 3 security findings identified across different categories",
        "metric": "vulnerabilities_found",
        "target": ">=3",
        "weight": 0.2,
        "met": false
      },
      {
        "id": "remediation-guidance",
        "description": "Every finding includes clear, actionable remediation steps a developer can follow",
        "metric": "findings_with_remediation",
        "target": "100%",
        "weight": 0.2,
        "met": false
      },
      {
        "id": "user-control",
        "description": "User is presented findings with risk grades and given checkpoint to continue deeper scanning or generate report",
        "metric": "user_checkpoints",
        "target": ">=1",
        "weight": 0.15,
        "met": false
      }
    ],
    "constraints": [
      {
        "id": "non-intrusive-only",
        "description": "Never execute active attacks, send exploit payloads, or perform actions that could trigger WAF/IDS systems. Passive and OSINT-based scanning only \u2014 no nmap, sqlmap, or attack payloads.",
        "constraint_type": "hard",
        "category": "safety",
        "check": ""
      },
      {
        "id": "developer-audience",
        "description": "All findings and remediation steps must be written for developers using clear language, not security jargon",
        "constraint_type": "hard",
        "category": "quality",
        "check": ""
      }
    ],
    "context": {},
    "required_capabilities": [],
    "input_schema": {},
    "output_schema": {},
    "version": "2.0.0",
    "parent_version": null,
    "evolution_reason": null
  },
  "required_tools": [
    "ssl_tls_scan",
    "http_headers_scan",
    "dns_security_scan",
    "port_scan",
    "tech_stack_detect",
    "subdomain_enumerate",
    "risk_score",
    "save_data",
    "serve_file_to_user"
  ],
  "metadata": {
    "node_count": 5,
    "edge_count": 6
  }
}
