AI Security OSINT Bug Bounty Python FastAPI

How I Built My Dual-Model AI Security & OSINT Agent

Project Goal

I built this project to create a local AI-powered security research assistant that can help with authorized bug bounty testing, OSINT workflows, and vulnerability verification.

The goal was not just to build a chatbot. I wanted an agent that could:

  • Understand security tasks from natural language
  • Use controlled Python tools
  • Enforce target scope and safety rules
  • Combine GPT-5.5 and Claude Opus
  • Run OSINT tools like Sherlock and Maigret
  • Verify vulnerability hypotheses with evidence
  • Reduce false positives before reporting

1. Create the Main Project Directory

I started by creating a main project folder called:

mkdir ~/ai-agent
cd ~/ai-agent

This directory became the root of the entire agent.

The final structure looks like this:

ai-agent/
├── app.py
├── agent.py
├── config.py
├── schemas.py
├── policies.py
├── llm_clients.py
├── requirements.txt
├── .env
├── tools/
├── tests/
├── docs/
└── examples/

2. Create a Python Virtual Environment

I used a Python virtual environment so the project dependencies stay isolated.

cd ~/ai-agent
python3 -m venv venv
source venv/bin/activate

Then I installed dependencies:

pip install fastapi uvicorn pydantic python-dotenv openai anthropic httpx requests beautifulsoup4 PyYAML

3. Create .env

The .env file stores API keys, model names, and safety settings.

nano .env

Example:

OPENAI_API_KEY=your_openai_key_here
ANTHROPIC_API_KEY=your_anthropic_key_here
OPENAI_MODEL=gpt-5.5
ANTHROPIC_MODEL=claude-opus-4-8
DEFAULT_LLM_PROVIDER=openai
ENABLE_DUAL_LLM=true
ENABLE_RECON=true
ENABLE_ACTIVE_SCANNING=false
PENTEST_ALLOWLIST=nerminzlatanovic.com,www.nerminzlatanovic.com,localhost,127.0.0.1,testphp.vulnweb.com
MAX_CRAWL_DEPTH=2
NUCLEI_SEVERITY=low,medium,high,critical
NUCLEI_RATE_LIMIT=5

Purpose of .env: stores configuration without hardcoding secrets in Python files.

Important: never upload real .env files to GitHub.

4. Create config.py

config.py loads environment variables and makes them available to the rest of the project.

Purpose:

Central configuration file for API keys, model names, safety flags, and tool settings.

Example responsibilities:

  • OPENAI_API_KEY
  • ANTHROPIC_API_KEY
  • OPENAI_MODEL
  • ANTHROPIC_MODEL
  • ENABLE_RECON
  • ENABLE_ACTIVE_SCANNING
  • PENTEST_ALLOWLIST

This file is important because every tool and workflow can read the same configuration.

5. Create schemas.py

schemas.py contains Pydantic models for tool inputs and outputs.

Purpose:

Defines structured input/output formats for every tool.

Example:

class AccessControlVerifierIn(BaseModel):
    url: str
    method: str = "GET"
    account_a_headers: Dict[str, str] = {}
    account_b_headers: Dict[str, str] = {}

Why this matters:

Every tool receives predictable input and returns predictable output. This makes the agent easier to debug, test, and extend.

6. Create policies.py

policies.py controls which tools are allowed and when they can run.

Purpose:

Safety and scope enforcement.

It defines things like:

  • SAFE_TOOLS
  • RECON_TOOLS
  • ACTIVE_TOOLS

It also helps prevent the agent from running dangerous actions against targets that are not authorized.

Example safety behavior:

  • target.com → blocked if not in allowlist
  • nerminzlatanovic.com → allowed if listed in scope

7. Create llm_clients.py

llm_clients.py connects the project to OpenAI and Anthropic.

Purpose:

Provides reusable functions for GPT-5.5, Claude Opus, and dual-model calls.

Functions:

  • call_openai()
  • call_anthropic()
  • call_llm()
  • call_dual_llm()

How I use them:

  • GPT-5.5 = main planner and tool router
  • Claude Opus = second planner, reviewer, and false-positive challenger
  • Dual mode = both models work together on selected tasks

8. Create app.py

app.py is the FastAPI web server.

Purpose:

Expose the agent through a local API endpoint.

Main route:

POST /chat

Example API flow:

Terminal command
↓
FastAPI /chat
↓
agent.py
↓
tool execution
↓
final response

I start the server with:

uvicorn app:app --reload

9. Create agent.py

agent.py is the brain of the project.

Purpose:

Routes user requests to the correct tools.

Handles GPT/Claude workflows.

Formats tool results.

Controls dual-agent mode.

It contains:

  • Tool registry
  • Tool handlers
  • Tool manifest
  • Keyword routing
  • Dual-agent logic
  • Output formatting
  • Safety checks
  • Claude review routing

Example commands handled by agent.py:

ai "verify cors on https://example.com"

ai "username enum @exampleuser"

ai "dual agent: validate headers on https://example.com"

ai "request diff: GET / HTTP/1.1
Host: example.com"

10. Create the tools/ Directory

The tools/ directory contains all custom security and OSINT tools.

mkdir tools

Each file in tools/ is a separate capability.

Example:

tools/access_control_verifier.py
tools/request_diff_engine.py
tools/username_enum.py

11. Create the tests/ Directory

The tests/ directory stores unit tests.

mkdir tests

Purpose:

Make sure tools work after every change.

Run tests:

python3 -m unittest discover -s tests

12. Create the ai Shell Helper

To talk to the agent from terminal, I created an ai() shell function.

I added this to ~/.zshrc:

ai() {
  jq -n --arg message "$*" '{message: $message}' |
  curl -s http://127.0.0.1:8000/chat     -H "Content-Type: application/json"     -d @- |
  jq -r '.result.final'
}

ai-debug() {
  jq -n --arg message "$*" '{message: $message}' |
  curl -s http://127.0.0.1:8000/chat     -H "Content-Type: application/json"     -d @-
}

Then reload:

source ~/.zshrc

Purpose:

  • ai = clean final output
  • ai-debug = full raw JSON response for debugging

13. Dual-Model Workflow

I implemented three modes.

GPT-5.5 Only

Normal commands use GPT-5.5 as the primary agent.

ai "verify cors on https://nerminzlatanovic.com"

Claude Only

Claude can review output directly.

ai "claude review: Username enumeration found several possible profiles. Review confidence."

Dual-Agent Mode

Both GPT-5.5 and Claude Opus plan together.

ai "dual agent: validate headers on https://nerminzlatanovic.com"

Dual-agent flow:

GPT-5.5 Plan
Claude Opus Plan
Tool Mapping
Tool Executed
Raw Evidence Summary
Claude Critique
Final Recommendation

14. Tool Inventory and Purpose

Core Files

app.py

Runs the FastAPI server.

Purpose: receives chat requests and returns agent responses.

agent.py

Main orchestration file.

Purpose: routes commands, executes tools, formats results, and manages dual-model logic.

config.py

Loads environment variables.

Purpose: central place for API keys, models, scope, and feature flags.

schemas.py

Defines tool input/output models.

Purpose: keeps every tool structured and predictable.

policies.py

Controls safety and scope.

Purpose: prevents unsafe or out-of-scope tool execution.

llm_clients.py

Connects to GPT-5.5 and Claude Opus.

Purpose: allows the agent to call OpenAI, Anthropic, or both.

Reconnaissance Tools

advanced_recon.py
Purpose: runs a broader recon workflow against an authorized target.

autonomous_recon.py
Purpose: automates multi-step recon decisions.

recon_workflow.py
Purpose: chains recon tools together into one workflow.

subdomain_enum.py
Purpose: finds subdomains for authorized domains.

dns_lookup.py
Purpose: performs DNS lookups.

whois_lookup.py
Purpose: collects WHOIS information.

http_probe.py
Purpose: checks whether hosts or URLs are alive.

http_get.py
Purpose: fetches a URL safely and returns response metadata.

crawler.py
Purpose: crawls authorized targets for URLs.

browser_crawler.py
Purpose: uses browser-style crawling to discover pages, forms, and JavaScript.

authenticated_browser_crawler.py
Purpose: crawls authenticated areas using a provided session or cookie file.

robots_parser.py
Purpose: parses robots.txt for interesting paths.

sitemap_parser.py
Purpose: parses sitemap.xml for URLs.

tech_fingerprint.py
Purpose: identifies technologies, frameworks, headers, and fingerprints.

JavaScript and API Analysis Tools

js_recon.py
Purpose: analyzes JavaScript assets for endpoints and application behavior.

js_endpoint_extractor.py
Purpose: extracts API paths, routes, and endpoints from JavaScript.

js_secret_scanner.py
Purpose: looks for exposed keys, tokens, and secret-like strings.

param_extract.py
Purpose: extracts query parameters and candidate inputs.

api_schema_mapper.py
Purpose: discovers OpenAPI, Swagger, Redoc, and API documentation paths.

graphql_analyzer.py
Purpose: analyzes GraphQL endpoints for sensitive queries, mutations, and authorization concerns.

jwt_session_analyzer.py
Purpose: analyzes JWT tokens for weak algorithms, long expiration, and risky claims.

custom_sast_analyzer.py
Purpose: performs static analysis on local JavaScript files.

Scanner and External Tool Wrappers

nmap_scan.py
Purpose: runs scoped Nmap scans against authorized targets.

nuclei_scan.py
Purpose: runs Nuclei templates against authorized targets.

nuclei_profile_scan.py
Purpose: runs selected Nuclei profiles based on severity or target type.

nikto_scan.py
Purpose: runs Nikto web server checks against authorized targets.

zap_scan.py
Purpose: runs OWASP ZAP scanning workflows.

zap_passive_scan.py
Purpose: runs passive ZAP analysis without active attack behavior.

Candidate Mapping Tools

idor_candidate_mapper.py
Purpose: finds possible IDOR/BOLA candidate endpoints.

rbac_mapper.py
Purpose: maps role-based access-control testing ideas.

business_logic_mapper.py
Purpose: identifies high-risk business logic flows such as payment, wallet, recovery, transfer, and redirect flows.

exposure_checker.py
Purpose: checks for exposed files, debug pages, and sensitive public resources.

security_headers.py
Purpose: checks security-related HTTP headers.

open_redirect_checker.py
Purpose: finds open redirect candidates.

cors_checker.py
Purpose: checks basic CORS behavior.

Vulnerability Verification Tools

These are the tools that move the project beyond recon.

access_control_verifier.py
Purpose: verifies IDOR/BOLA/RBAC behavior using Account A vs Account B.

  • Status codes
  • Response length
  • Content type
  • Sensitive fields
  • Body similarity
  • Authorization errors

auth_state_verifier.py
Purpose: compares unauthenticated and authenticated responses.

Useful for:

  • Missing authentication
  • Public exposure
  • Auth bypass candidates

open_redirect_verifier.py
Purpose: safely verifies open redirect behavior using example.com payloads.

Checks:

  • Redirect status codes
  • Location header
  • External host match

cors_verifier.py
Purpose: safely verifies risky CORS behavior.

Checks:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Credentials
  • OPTIONS preflight behavior

request_diff_engine.py
Purpose: takes a real raw HTTP request, replays it, applies safe mutations, and compares responses.

This is one of the strongest tools in the project.

It can test:

  • Authorization removal
  • Debug parameter behavior
  • Test parameter behavior
  • Response similarity
  • Missing auth candidates

mass_assignment_verifier.py
Purpose: detects mass assignment candidates from JSON API requests.

It generates candidate fields such as:

{
  "isAdmin": true,
  "role": "admin",
  "verified": true,
  "plan": "enterprise"
}

In safe mode, it generates examples only and does not send state-changing requests.

validator_agent.py
Purpose: general validation tool for headers, CORS, redirects, and other lightweight checks.

OSINT / Trace Labs Tools

case_intake_parser.py
Purpose: parses missing-person case information into structured OSINT fields.

Extracts:

  • Name
  • Alias
  • Username
  • Last seen location
  • Last seen date
  • Source URL
  • Timeline clues

username_pivot.py
Purpose: generates passive username search queries.

username_search.py
Purpose: searches public sources for username/profile leads.

username_enum.py
Purpose: runs Sherlock and Maigret for username enumeration.

social_profile_mapper.py
Purpose: maps possible social profiles into structured OSINT leads.

Looks for:

  • Platform
  • Username
  • Bio clues
  • Location clues
  • Linked accounts
  • Confidence

Reporting and Triage Tools

finding_correlator.py
Purpose: combines related findings into a stronger picture.

vuln_triage.py
Purpose: ranks and classifies potential findings.

report_generator.py
Purpose: generates report-style summaries.

report_writer.py
Purpose: writes report output for later review or submission.

Utility Tools

calc.py
Purpose: simple calculator utility.

file_search.py
Purpose: searches local files or project data.

hash_lookup.py
Purpose: looks up or analyzes hashes.

cve_search.py
Purpose: searches for CVE information.

web_search.py
Purpose: searches the web when configured.

How Someone Can Recreate This Project

Step 1: Create Directory

mkdir ~/ai-agent
cd ~/ai-agent

Step 2: Create Virtual Environment

python3 -m venv venv
source venv/bin/activate

Step 3: Install Dependencies

pip install fastapi uvicorn pydantic python-dotenv openai anthropic httpx requests beautifulsoup4 PyYAML

Step 4: Create Core Files

touch app.py
touch agent.py
touch config.py
touch schemas.py
touch policies.py
touch llm_clients.py
touch requirements.txt
touch .env

Example: How One Python Tool Is Written

Below is a simplified example of how I wrote tools inside the agent. This example shows the structure of a safe verifier tool.

from pydantic import BaseModel, Field
from typing import Dict, List, Optional
import httpx


class HeaderVerifierIn(BaseModel):
    url: str = Field(..., description="Target URL to check")
    method: str = "HEAD"


class HeaderFinding(BaseModel):
    header: str
    status: str
    value: Optional[str] = None


class HeaderVerifierOut(BaseModel):
    tool: str
    target: str
    status_code: int
    verdict: str
    findings: List[HeaderFinding]
    recommendation: str


async def header_verifier(inp: HeaderVerifierIn) -> HeaderVerifierOut:
    async with httpx.AsyncClient(timeout=10, follow_redirects=True) as client:
        response = await client.request(
            inp.method,
            inp.url,
        )

    headers = {
        key.lower(): value
        for key, value in response.headers.items()
    }

    required_headers = [
        "strict-transport-security",
        "content-security-policy",
        "x-content-type-options",
        "x-frame-options",
        "referrer-policy",
        "permissions-policy",
    ]

    findings = []

    for header in required_headers:
        if header in headers:
            findings.append(
                HeaderFinding(
                    header=header,
                    status="present",
                    value=headers[header],
                )
            )
        else:
            findings.append(
                HeaderFinding(
                    header=header,
                    status="missing",
                    value=None,
                )
            )

    missing = [
        finding.header
        for finding in findings
        if finding.status == "missing"
    ]

    if missing:
        verdict = "Manual Verification Needed"
        recommendation = (
            "Review whether missing defensive headers are intentional. "
            "Add appropriate headers where needed."
        )
    else:
        verdict = "False Positive"
        recommendation = "All checked defensive headers were present."

    return HeaderVerifierOut(
        tool="header_verifier",
        target=inp.url,
        status_code=response.status_code,
        verdict=verdict,
        findings=findings,
        recommendation=recommendation,
    )

Explanation of the Code

The file starts by importing the Python libraries needed by the tool.

from pydantic import BaseModel, Field
from typing import Dict, List, Optional
import httpx

pydantic is used to define structured input and output models. httpx is used to send HTTP requests.

The input model defines what the tool accepts:

class HeaderVerifierIn(BaseModel):
    url: str
    method: str = "HEAD"

This means the tool needs a URL and can optionally accept an HTTP method. By default, it uses HEAD because header checks do not need to download the full page body.

The output models define what the tool returns:

class HeaderFinding(BaseModel):
    header: str
    status: str
    value: Optional[str] = None

Each header result includes the header name, whether it was present or missing, and its value if found.

The main tool function is asynchronous:

async def header_verifier(inp: HeaderVerifierIn) -> HeaderVerifierOut:

This allows the FastAPI agent to run the tool efficiently.

The tool sends the request:

response = await client.request(
    inp.method,
    inp.url,
)

Then it normalizes headers to lowercase so checks are consistent:

headers = {
    key.lower(): value
    for key, value in response.headers.items()
}

The tool checks for important defensive headers:

required_headers = [
    "strict-transport-security",
    "content-security-policy",
    "x-content-type-options",
    "x-frame-options",
    "referrer-policy",
    "permissions-policy",
]

For each header, it records whether the header exists:

if header in headers:
    status="present"
else:
    status="missing"

Finally, the tool decides a verdict:

if missing:
    verdict = "Manual Verification Needed"
else:
    verdict = "False Positive"

I intentionally used Manual Verification Needed instead of automatically claiming a vulnerability. Missing headers can be hardening issues, but they are not always reportable vulnerabilities by themselves.

Why I Use This Structure

Every tool follows the same basic pattern:

Input schema
↓
Safety-aware execution
↓
Evidence collection
↓
Verdict
↓
Recommendation
↓
Structured output

This makes the agent easier to expand. When I add a new tool, I create:

  • Tool input model
  • Tool output model
  • Tool function
  • Tool registration in agent.py
  • Policy entry in policies.py
  • Tests

This is how the project stays organized and safe while still allowing the agent to become more powerful.

Step 5: Create Folders

mkdir tools
mkdir tests
mkdir docs
mkdir examples

Step 6: Add API Keys

Create .env:

OPENAI_API_KEY=your_openai_key
ANTHROPIC_API_KEY=your_anthropic_key
OPENAI_MODEL=gpt-5.5
ANTHROPIC_MODEL=claude-opus-4-8
ENABLE_DUAL_LLM=true
ENABLE_RECON=true
ENABLE_ACTIVE_SCANNING=false
PENTEST_ALLOWLIST=localhost,127.0.0.1,yourdomain.com

Step 7: Build Tool Registry

In agent.py, register every tool:

HANDLERS = {
    "cors_verifier": (CorsVerifierIn, cors_verifier),
    "open_redirect_verifier": (OpenRedirectVerifierIn, open_redirect_verifier),
    "request_diff_engine": (RequestDiffEngineIn, request_diff_engine),
}

Step 8: Add Safety Rules

In policies.py, define allowed tools:

SAFE_TOOLS = {
    "username_pivot",
    "case_intake_parser",
    "jwt_session_analyzer",
}

RECON_TOOLS = {
    "http_probe",
    "security_headers",
    "cors_verifier",
    "open_redirect_verifier",
}

Step 9: Start FastAPI

uvicorn app:app --reload

Step 10: Add Shell Helper

Add this to ~/.zshrc:

ai() {
  jq -n --arg message "$*" '{message: $message}' |
  curl -s http://127.0.0.1:8000/chat     -H "Content-Type: application/json"     -d @- |
  jq -r '.result.final'
}

Reload:

source ~/.zshrc

Step 11: Test

ai "verify cors on https://yourdomain.com"

ai "dual agent: validate headers on https://yourdomain.com"

ai "username pivot for @exampleuser"

What Makes This Project Different

This project is different because it combines:

AI reasoning
+
real tools
+
scope enforcement
+
dual-model review
+
evidence-based verification

Instead of saying:

This might be vulnerable.

The goal is to say:

Here is the request. Here is the response. Here is the comparison. Here is the evidence. Here is the confidence. Here is what still needs manual verification.

That is what makes the project useful for real security work.

Final Disclaimer

This project is for:

  • Authorized bug bounty testing
  • Personal lab testing
  • Defensive security research
  • OSINT education
  • Trace Labs-style passive OSINT workflows

It should not be used against systems without permission. All testing must follow laws, program rules, and ethical guidelines.