mirror of
https://github.com/th30d4y/BURP-AI.git
synced 2026-05-26 11:35:52 +00:00
1379 lines
53 KiB
Python
1379 lines
53 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import print_function
|
|
"""
|
|
BurpAI NATIVE REPEATER MODE - Burp Suite Extension
|
|
Production-grade Jython extension with FIXED imports, clean UI, no crashes
|
|
"""
|
|
|
|
import sys
|
|
import traceback
|
|
import threading
|
|
import json
|
|
import time
|
|
|
|
# ===== BURP IMPORTS =====
|
|
from burp import IBurpExtender, ITab, IHttpListener, IContextMenuFactory, IMessageEditorController
|
|
|
|
# ===== JAVA/SWING IMPORTS (EXPLICIT - NO java. PREFIX) =====
|
|
from javax.swing import JPanel
|
|
from javax.swing import JScrollPane
|
|
from javax.swing import JSplitPane
|
|
from javax.swing import JTabbedPane
|
|
from javax.swing import JTable
|
|
from javax.swing import JTextArea
|
|
from javax.swing import JTextField
|
|
from javax.swing import JButton
|
|
from javax.swing import JLabel
|
|
from javax.swing import JComboBox
|
|
from javax.swing import JCheckBox
|
|
from javax.swing import JMenuItem
|
|
from javax.swing import SwingUtilities
|
|
from javax.swing import BorderFactory
|
|
from javax.swing import BoxLayout
|
|
from javax.swing import Box
|
|
from javax.swing import ListSelectionModel
|
|
from javax.swing.table import DefaultTableModel
|
|
from javax.swing.event import ListSelectionListener
|
|
|
|
# ===== JAVA AWT IMPORTS (EXPLICIT - NO java. PREFIX) =====
|
|
from java.awt import BorderLayout
|
|
from java.awt import FlowLayout
|
|
from java.awt import Dimension
|
|
from java.awt import Color
|
|
from java.awt import Font
|
|
from java.awt import Insets
|
|
|
|
# ===== JAVA AWT EVENT IMPORTS =====
|
|
from java.awt.event import ActionListener
|
|
from java.awt.event import KeyListener
|
|
from java.awt.event import KeyEvent
|
|
|
|
# ===== JAVA LANG IMPORTS =====
|
|
from java.lang import Runnable
|
|
|
|
# ===== URLLIB COMPATIBILITY =====
|
|
try:
|
|
from urllib2 import Request, urlopen, HTTPError
|
|
except ImportError:
|
|
from urllib.request import Request, urlopen
|
|
from urllib.error import HTTPError
|
|
|
|
|
|
# ===== MESSAGE EDITOR CONTROLLER =====
|
|
|
|
class MessageEditorController(IMessageEditorController):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def getHttpService(self):
|
|
return None
|
|
|
|
def getRequest(self):
|
|
return None
|
|
|
|
def setRequest(self, message):
|
|
pass
|
|
|
|
def getResponse(self):
|
|
return None
|
|
|
|
def setResponse(self, message):
|
|
pass
|
|
|
|
def getEditorName(self):
|
|
return "BurpAI"
|
|
|
|
|
|
# ===== EVENT LISTENERS =====
|
|
|
|
class SendButtonListener(ActionListener):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def actionPerformed(self, event):
|
|
try:
|
|
self.ext.send_chat_message()
|
|
except Exception as e:
|
|
print("[!] SendButtonListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
|
|
class InputKeyListener(KeyListener):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def keyPressed(self, e):
|
|
try:
|
|
if e.getKeyCode() == KeyEvent.VK_ENTER:
|
|
self.ext.send_chat_message()
|
|
except Exception as e:
|
|
print("[!] InputKeyListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
def keyReleased(self, e):
|
|
pass
|
|
|
|
def keyTyped(self, e):
|
|
pass
|
|
|
|
|
|
class SaveAPIListener(ActionListener):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def actionPerformed(self, event):
|
|
try:
|
|
self.ext.save_api_key()
|
|
except Exception as e:
|
|
print("[!] SaveAPIListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
|
|
class UpdateModelListener(ActionListener):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def actionPerformed(self, event):
|
|
try:
|
|
self.ext.update_model()
|
|
except Exception as e:
|
|
print("[!] UpdateModelListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
|
|
class RepeatSendListener(ActionListener):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def actionPerformed(self, event):
|
|
try:
|
|
self.ext.send_request()
|
|
except Exception as e:
|
|
print("[!] RepeatSendListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
|
|
class HistorySelectionListener(ListSelectionListener):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def valueChanged(self, event):
|
|
try:
|
|
if not event.getValueIsAdjusting():
|
|
self.ext.load_from_history()
|
|
except Exception as e:
|
|
print("[!] HistorySelectionListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
|
|
class ContextMenuSendToAIListener(ActionListener):
|
|
def __init__(self, extension, messages):
|
|
self.ext = extension
|
|
self.messages = messages
|
|
|
|
def actionPerformed(self, event):
|
|
try:
|
|
self.ext.forward_to_ai_from_menu(self.messages)
|
|
except Exception as e:
|
|
print("[!] ContextMenuSendToAIListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
|
|
class ContextMenuFactory(IContextMenuFactory):
|
|
def __init__(self, extension):
|
|
self.extension = extension
|
|
|
|
def createMenuItems(self, invocation):
|
|
try:
|
|
menu_items = []
|
|
selected = invocation.getSelectedMessages()
|
|
|
|
if selected and len(selected) > 0:
|
|
item = JMenuItem("Send to BurpAI")
|
|
item.addActionListener(ContextMenuSendToAIListener(self.extension, selected))
|
|
menu_items.append(item)
|
|
|
|
return menu_items if menu_items else None
|
|
except Exception as e:
|
|
print("[!] ContextMenuFactory error: " + str(e))
|
|
traceback.print_exc()
|
|
return None
|
|
|
|
|
|
class AnalyzeButtonListener(ActionListener):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def actionPerformed(self, event):
|
|
try:
|
|
self.ext.analyze_with_ai()
|
|
except Exception as e:
|
|
print("[!] AnalyzeButtonListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
|
|
class AutoAnalyzeListener(ActionListener):
|
|
def __init__(self, extension):
|
|
self.ext = extension
|
|
|
|
def actionPerformed(self, event):
|
|
try:
|
|
self.ext.auto_analyze = self.ext.auto_analyze_checkbox.isSelected()
|
|
print("[*] Auto Analyze: " + str(self.ext.auto_analyze))
|
|
except Exception as e:
|
|
print("[!] AutoAnalyzeListener error: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
|
|
|
|
|
|
|
|
# ===== MAIN EXTENSION CLASS =====
|
|
|
|
class BurpExtender(IBurpExtender, ITab, IHttpListener):
|
|
|
|
def registerExtenderCallbacks(self, callbacks):
|
|
try:
|
|
self.callbacks = callbacks
|
|
self.helpers = callbacks.getHelpers()
|
|
|
|
self.api_key = ""
|
|
self.model = "kimi-k2.5"
|
|
self.traffic_log = []
|
|
self.ai_history = []
|
|
self.max_history = 1000 # Limit history to 1000 entries for performance
|
|
self.current_request = None
|
|
self.current_response = None
|
|
|
|
# Multi-model engine with fallback (Bug Bounty Hunter Mode)
|
|
self.primary_models = [
|
|
"alibaba-qwen3-32b",
|
|
"deepseek-r1-distill-llama-70b",
|
|
"glm-5",
|
|
"kimi-k2.5",
|
|
"llama3-8b-instruct",
|
|
"llama3.3-70b-instruct",
|
|
"minimax-m2.5",
|
|
"mistral-nemo-instruct-2407",
|
|
"nvidia-nemotron-3-super-120b",
|
|
"openai-gpt-oss-120b",
|
|
"openai-gpt-oss-20b"
|
|
]
|
|
self.fallback_models = []
|
|
self.all_models = self.primary_models + self.fallback_models
|
|
self.current_model_index = 0
|
|
|
|
# UI Components
|
|
self.chat_display = None
|
|
self.chat_input = None
|
|
self.api_input = None
|
|
self.status_label = None
|
|
self.model_combo = None
|
|
self.history_table = None
|
|
self.history_model = None
|
|
|
|
# Native Message Editors
|
|
self.request_editor = None
|
|
self.response_editor = None
|
|
self.editor_controller = MessageEditorController(self)
|
|
|
|
# Repeater controls
|
|
self.auto_analyze = False
|
|
self.auto_analyze_checkbox = None
|
|
|
|
self.callbacks.registerHttpListener(self)
|
|
self.callbacks.registerContextMenuFactory(ContextMenuFactory(self))
|
|
self.callbacks.addSuiteTab(self)
|
|
|
|
print("[*] BurpAI loaded successfully - ready to capture requests")
|
|
except Exception as e:
|
|
print("[!] Error in registerExtenderCallbacks: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
def getTabCaption(self):
|
|
try:
|
|
return "BurpAI"
|
|
except Exception as e:
|
|
print("[!] Error in getTabCaption: " + str(e))
|
|
traceback.print_exc()
|
|
return "BurpAI"
|
|
|
|
def getUiComponent(self):
|
|
try:
|
|
return self.build_ui()
|
|
except Exception as e:
|
|
print("[!] Error in getUiComponent: " + str(e))
|
|
traceback.print_exc()
|
|
return JPanel()
|
|
|
|
def build_ui(self):
|
|
"""Build main UI with clean alignment"""
|
|
try:
|
|
main_panel = JPanel(BorderLayout())
|
|
main_panel.setBackground(Color(30, 30, 30))
|
|
|
|
# TOP: Compact toolbar
|
|
toolbar = self.build_toolbar()
|
|
main_panel.add(toolbar, BorderLayout.NORTH)
|
|
|
|
# CENTER: Main horizontal split
|
|
main_split = JSplitPane(JSplitPane.HORIZONTAL_SPLIT)
|
|
main_split.setDividerLocation(400)
|
|
main_split.setBackground(Color(30, 30, 30))
|
|
main_split.setResizeWeight(0.4)
|
|
main_split.setBorder(BorderFactory.createLineBorder(Color(60, 60, 60), 1))
|
|
|
|
left_panel = self.build_chat_panel()
|
|
right_panel = self.build_right_panel()
|
|
|
|
main_split.setLeftComponent(left_panel)
|
|
main_split.setRightComponent(right_panel)
|
|
|
|
main_panel.add(main_split, BorderLayout.CENTER)
|
|
|
|
return main_panel
|
|
except Exception as e:
|
|
print("[!] Error in build_ui: " + str(e))
|
|
traceback.print_exc()
|
|
return JPanel()
|
|
|
|
def build_toolbar(self):
|
|
"""Create compact Burp-style toolbar with proper alignment"""
|
|
try:
|
|
toolbar = JPanel(FlowLayout(FlowLayout.LEFT, 8, 5))
|
|
toolbar.setBackground(Color(40, 40, 40))
|
|
toolbar.setBorder(BorderFactory.createLineBorder(Color(60, 60, 60), 1))
|
|
toolbar.setPreferredSize(Dimension(100, 38))
|
|
|
|
# API Key label
|
|
api_label = JLabel("API Key:")
|
|
api_label.setForeground(Color(180, 180, 180))
|
|
api_label.setFont(Font("Arial", Font.PLAIN, 10))
|
|
toolbar.add(api_label)
|
|
|
|
# API Key input field
|
|
self.api_input = JTextField()
|
|
self.api_input.setBackground(Color(50, 50, 50))
|
|
self.api_input.setForeground(Color(200, 200, 200))
|
|
self.api_input.setFont(Font("Monospaced", Font.PLAIN, 10))
|
|
self.api_input.setPreferredSize(Dimension(200, 26))
|
|
self.api_input.setMaximumSize(Dimension(200, 26))
|
|
self.api_input.setMargin(Insets(2, 4, 2, 4))
|
|
toolbar.add(self.api_input)
|
|
|
|
# Save button
|
|
save_btn = JButton("Save")
|
|
save_btn.setBackground(Color(100, 180, 100))
|
|
save_btn.setForeground(Color.WHITE)
|
|
save_btn.setFont(Font("Arial", Font.BOLD, 9))
|
|
save_btn.setPreferredSize(Dimension(65, 26))
|
|
save_btn.setMaximumSize(Dimension(65, 26))
|
|
save_btn.setMargin(Insets(2, 8, 2, 8))
|
|
save_btn.setFocusPainted(False)
|
|
save_btn.addActionListener(SaveAPIListener(self))
|
|
toolbar.add(save_btn)
|
|
|
|
# Separator
|
|
toolbar.add(JLabel(" | "))
|
|
|
|
# Model label
|
|
model_label = JLabel("Model:")
|
|
model_label.setForeground(Color(180, 180, 180))
|
|
model_label.setFont(Font("Arial", Font.PLAIN, 10))
|
|
toolbar.add(model_label)
|
|
|
|
# Model dropdown
|
|
self.model_combo = JComboBox(self.all_models)
|
|
self.model_combo.setBackground(Color(50, 50, 50))
|
|
self.model_combo.setForeground(Color(200, 200, 200))
|
|
self.model_combo.setFont(Font("Arial", Font.PLAIN, 10))
|
|
self.model_combo.setPreferredSize(Dimension(170, 26))
|
|
self.model_combo.setMaximumSize(Dimension(170, 26))
|
|
self.model_combo.addActionListener(UpdateModelListener(self))
|
|
toolbar.add(self.model_combo)
|
|
|
|
# Separator
|
|
toolbar.add(JLabel(" | "))
|
|
|
|
# Status label
|
|
status_label_text = JLabel("Status:")
|
|
status_label_text.setForeground(Color(180, 180, 180))
|
|
status_label_text.setFont(Font("Arial", Font.PLAIN, 10))
|
|
toolbar.add(status_label_text)
|
|
|
|
self.status_label = JLabel("Ready")
|
|
self.status_label.setForeground(Color(255, 200, 100))
|
|
self.status_label.setFont(Font("Arial", Font.BOLD, 10))
|
|
toolbar.add(self.status_label)
|
|
|
|
return toolbar
|
|
except Exception as e:
|
|
print("[!] Error in build_toolbar: " + str(e))
|
|
traceback.print_exc()
|
|
return JPanel()
|
|
|
|
def build_chat_panel(self):
|
|
"""Build left chat panel"""
|
|
try:
|
|
panel = JPanel(BorderLayout())
|
|
panel.setBackground(Color(30, 30, 30))
|
|
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5))
|
|
|
|
# Chat display
|
|
self.chat_display = JTextArea()
|
|
self.chat_display.setEditable(False)
|
|
self.chat_display.setBackground(Color(30, 30, 30))
|
|
self.chat_display.setForeground(Color(212, 212, 212))
|
|
self.chat_display.setFont(Font("Consolas", Font.PLAIN, 11))
|
|
self.chat_display.setLineWrap(True)
|
|
self.chat_display.setWrapStyleWord(True)
|
|
self.chat_display.setMargin(Insets(8, 8, 8, 8))
|
|
|
|
scroll = JScrollPane(self.chat_display)
|
|
scroll.setBackground(Color(30, 30, 30))
|
|
scroll.setBorder(BorderFactory.createLineBorder(Color(60, 60, 60), 1))
|
|
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED)
|
|
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
|
|
|
|
panel.add(scroll, BorderLayout.CENTER)
|
|
|
|
# Input area
|
|
input_panel = JPanel(BorderLayout())
|
|
input_panel.setBackground(Color(30, 30, 30))
|
|
input_panel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0))
|
|
|
|
self.chat_input = JTextField()
|
|
self.chat_input.setBackground(Color(45, 45, 48))
|
|
self.chat_input.setForeground(Color(212, 212, 212))
|
|
self.chat_input.setFont(Font("Monospaced", Font.PLAIN, 11))
|
|
self.chat_input.setCaretColor(Color(212, 212, 212))
|
|
self.chat_input.setMargin(Insets(4, 6, 4, 6))
|
|
self.chat_input.addKeyListener(InputKeyListener(self))
|
|
|
|
input_panel.add(self.chat_input, BorderLayout.CENTER)
|
|
|
|
send_btn = JButton("Send")
|
|
send_btn.setBackground(Color(51, 122, 183))
|
|
send_btn.setForeground(Color.WHITE)
|
|
send_btn.setFont(Font("Arial", Font.PLAIN, 10))
|
|
send_btn.setFocusPainted(False)
|
|
send_btn.setPreferredSize(Dimension(75, 32))
|
|
send_btn.setMargin(Insets(2, 10, 2, 10))
|
|
send_btn.addActionListener(SendButtonListener(self))
|
|
|
|
input_panel.add(send_btn, BorderLayout.EAST)
|
|
panel.add(input_panel, BorderLayout.SOUTH)
|
|
|
|
return panel
|
|
except Exception as e:
|
|
print("[!] Error in build_chat_panel: " + str(e))
|
|
traceback.print_exc()
|
|
return JPanel()
|
|
|
|
def build_right_panel(self):
|
|
"""Build right panel with history and repeater"""
|
|
try:
|
|
right_split = JSplitPane(JSplitPane.VERTICAL_SPLIT)
|
|
right_split.setDividerLocation(200)
|
|
right_split.setBackground(Color(30, 30, 30))
|
|
right_split.setResizeWeight(0.35)
|
|
right_split.setBorder(BorderFactory.createLineBorder(Color(60, 60, 60), 1))
|
|
|
|
history_panel = self.build_history_panel()
|
|
repeater_panel = self.build_repeater_panel()
|
|
|
|
right_split.setTopComponent(history_panel)
|
|
right_split.setBottomComponent(repeater_panel)
|
|
|
|
return right_split
|
|
except Exception as e:
|
|
print("[!] Error in build_right_panel: " + str(e))
|
|
traceback.print_exc()
|
|
return JPanel()
|
|
|
|
def build_history_panel(self):
|
|
"""Build history table panel with data binding"""
|
|
try:
|
|
panel = JPanel(BorderLayout())
|
|
panel.setBackground(Color(30, 30, 30))
|
|
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5))
|
|
|
|
# Title
|
|
title_label = JLabel("Request History")
|
|
title_label.setForeground(Color(150, 150, 150))
|
|
title_label.setFont(Font("Arial", Font.BOLD, 10))
|
|
|
|
title_panel = JPanel()
|
|
title_panel.setBackground(Color(30, 30, 30))
|
|
title_panel.add(title_label)
|
|
panel.add(title_panel, BorderLayout.NORTH)
|
|
|
|
# Create history table model with columns
|
|
self.history_model = DefaultTableModel(
|
|
["#", "Method", "Host", "Path", "Status"],
|
|
0
|
|
)
|
|
|
|
# Bind model to table
|
|
self.history_table = JTable(self.history_model)
|
|
self.history_table.setBackground(Color(45, 45, 48))
|
|
self.history_table.setForeground(Color(212, 212, 212))
|
|
self.history_table.setFont(Font("Monospaced", Font.PLAIN, 9))
|
|
self.history_table.setGridColor(Color(60, 60, 60))
|
|
self.history_table.setRowHeight(22)
|
|
self.history_table.setSelectionBackground(Color(66, 110, 165))
|
|
|
|
# Add selection listener to load requests
|
|
self.history_table.getSelectionModel().addListSelectionListener(HistorySelectionListener(self))
|
|
|
|
# Set column widths
|
|
self.history_table.getColumnModel().getColumn(0).setPreferredWidth(30)
|
|
self.history_table.getColumnModel().getColumn(1).setPreferredWidth(50)
|
|
self.history_table.getColumnModel().getColumn(2).setPreferredWidth(80)
|
|
self.history_table.getColumnModel().getColumn(3).setPreferredWidth(100)
|
|
self.history_table.getColumnModel().getColumn(4).setPreferredWidth(40)
|
|
|
|
# Add scroll pane
|
|
scroll = JScrollPane(self.history_table)
|
|
scroll.setBackground(Color(30, 30, 30))
|
|
scroll.setBorder(BorderFactory.createLineBorder(Color(60, 60, 60), 1))
|
|
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED)
|
|
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
|
|
|
|
panel.add(scroll, BorderLayout.CENTER)
|
|
|
|
print("[*] History table initialized: model=" + str(self.history_model) + ", table=" + str(self.history_table))
|
|
|
|
return panel
|
|
except Exception as e:
|
|
print("[!] Error in build_history_panel: " + str(e))
|
|
traceback.print_exc()
|
|
return JPanel()
|
|
|
|
def build_repeater_panel(self):
|
|
"""Build native repeater panel"""
|
|
try:
|
|
panel = JPanel(BorderLayout())
|
|
panel.setBackground(Color(30, 30, 30))
|
|
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5))
|
|
|
|
# Title
|
|
title_label = JLabel("Repeater / Response")
|
|
title_label.setForeground(Color(150, 150, 150))
|
|
title_label.setFont(Font("Arial", Font.BOLD, 10))
|
|
|
|
title_panel = JPanel()
|
|
title_panel.setBackground(Color(30, 30, 30))
|
|
title_panel.add(title_label)
|
|
panel.add(title_panel, BorderLayout.NORTH)
|
|
|
|
# Native Burp Message Editors
|
|
try:
|
|
self.request_editor = self.callbacks.createMessageEditor(self.editor_controller, True)
|
|
self.response_editor = self.callbacks.createMessageEditor(self.editor_controller, False)
|
|
except Exception as editor_err:
|
|
print("[!] Error creating message editors: " + str(editor_err))
|
|
traceback.print_exc()
|
|
self.request_editor = None
|
|
self.response_editor = None
|
|
|
|
# Tabs for request/response
|
|
tabs = JTabbedPane()
|
|
tabs.setBackground(Color(40, 40, 40))
|
|
tabs.setForeground(Color(180, 180, 180))
|
|
|
|
if self.request_editor:
|
|
request_component = self.request_editor.getComponent()
|
|
tabs.addTab("Request", request_component)
|
|
|
|
if self.response_editor:
|
|
response_component = self.response_editor.getComponent()
|
|
tabs.addTab("Response", response_component)
|
|
|
|
panel.add(tabs, BorderLayout.CENTER)
|
|
|
|
# Control buttons panel
|
|
btn_panel = JPanel()
|
|
btn_panel.setLayout(FlowLayout(FlowLayout.LEFT, 8, 5))
|
|
btn_panel.setBackground(Color(30, 30, 30))
|
|
btn_panel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0))
|
|
|
|
repeat_send = JButton("Send Request")
|
|
repeat_send.setBackground(Color(100, 200, 100))
|
|
repeat_send.setForeground(Color.WHITE)
|
|
repeat_send.setFont(Font("Arial", Font.BOLD, 10))
|
|
repeat_send.setPreferredSize(Dimension(120, 30))
|
|
repeat_send.setMargin(Insets(4, 15, 4, 15))
|
|
repeat_send.setFocusPainted(False)
|
|
repeat_send.addActionListener(RepeatSendListener(self))
|
|
btn_panel.add(repeat_send)
|
|
|
|
# Separator
|
|
btn_panel.add(JLabel(" | "))
|
|
|
|
# Analyze button
|
|
analyze_btn = JButton("Analyze with AI")
|
|
analyze_btn.setBackground(Color(51, 122, 183))
|
|
analyze_btn.setForeground(Color.WHITE)
|
|
analyze_btn.setFont(Font("Arial", Font.BOLD, 10))
|
|
analyze_btn.setPreferredSize(Dimension(130, 30))
|
|
analyze_btn.setMargin(Insets(4, 15, 4, 15))
|
|
analyze_btn.setFocusPainted(False)
|
|
analyze_btn.addActionListener(AnalyzeButtonListener(self))
|
|
btn_panel.add(analyze_btn)
|
|
|
|
# Auto Analyze checkbox
|
|
try:
|
|
self.auto_analyze_checkbox = JCheckBox()
|
|
self.auto_analyze_checkbox.setText("Auto Analyze")
|
|
self.auto_analyze_checkbox.setBackground(Color(30, 30, 30))
|
|
self.auto_analyze_checkbox.setForeground(Color(212, 212, 212))
|
|
self.auto_analyze_checkbox.setFont(Font("Arial", Font.PLAIN, 10))
|
|
self.auto_analyze_checkbox.setFocusPainted(False)
|
|
self.auto_analyze_checkbox.addActionListener(AutoAnalyzeListener(self))
|
|
btn_panel.add(self.auto_analyze_checkbox)
|
|
except Exception as checkbox_err:
|
|
print("[!] Error creating auto analyze checkbox: " + str(checkbox_err))
|
|
|
|
panel.add(btn_panel, BorderLayout.SOUTH)
|
|
|
|
return panel
|
|
except Exception as e:
|
|
print("[!] Error in build_repeater_panel: " + str(e))
|
|
traceback.print_exc()
|
|
return JPanel()
|
|
|
|
# ===== CORE METHODS =====
|
|
|
|
def save_api_key(self):
|
|
try:
|
|
key = self.api_input.getText().strip()
|
|
|
|
if not key:
|
|
self.add_chat_message("System", "Error: API key cannot be empty")
|
|
return
|
|
|
|
self.api_key = key
|
|
self.callbacks.saveExtensionSetting("burpaai_api_key", key)
|
|
|
|
self.add_chat_message("System", "API key saved successfully")
|
|
self.status_label.setText("Connected")
|
|
self.status_label.setForeground(Color(100, 200, 100))
|
|
except Exception as e:
|
|
print("[!] Error saving API key: " + str(e))
|
|
traceback.print_exc()
|
|
self.add_chat_message("System", "Error saving API key: " + str(e))
|
|
|
|
def update_model(self):
|
|
try:
|
|
self.model = str(self.model_combo.getSelectedItem())
|
|
print("[*] Model updated: " + self.model)
|
|
except Exception as e:
|
|
print("[!] Error updating model: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
def send_chat_message(self):
|
|
try:
|
|
text = self.chat_input.getText().strip()
|
|
|
|
if not text:
|
|
return
|
|
|
|
if not self.api_key:
|
|
self.add_chat_message("System", "Error: API key not configured")
|
|
return
|
|
|
|
self.add_chat_message("You", text)
|
|
self.chat_input.setText("")
|
|
|
|
thread = threading.Thread(target=lambda: self._send_chat_async(text))
|
|
thread.daemon = True
|
|
thread.start()
|
|
except Exception as e:
|
|
print("[!] Error in send_chat_message: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
def _send_chat_async(self, text):
|
|
try:
|
|
response = self.call_ai(text)
|
|
SwingUtilities.invokeLater(lambda: self.add_chat_message("AI", response))
|
|
except Exception as e:
|
|
print("[!] Error in _send_chat_async: " + str(e))
|
|
traceback.print_exc()
|
|
SwingUtilities.invokeLater(lambda: self.add_chat_message("System", "Error: " + str(e)))
|
|
|
|
def add_chat_message(self, sender, text):
|
|
def update_ui():
|
|
try:
|
|
timestamp = time.strftime("%H:%M:%S")
|
|
current = self.chat_display.getText()
|
|
|
|
# Format message based on sender type
|
|
if sender == "BugBounty":
|
|
prefix = "[" + timestamp + "] [ANALYSIS]: "
|
|
elif sender == "AI":
|
|
prefix = "[" + timestamp + "] [AI]: "
|
|
elif sender == "You":
|
|
prefix = "[" + timestamp + "] [YOU]: "
|
|
else:
|
|
prefix = "[" + timestamp + "] [" + sender + "]: "
|
|
|
|
formatted_text = prefix + text
|
|
|
|
if current:
|
|
new_text = current + "\n" + formatted_text
|
|
else:
|
|
new_text = formatted_text
|
|
|
|
self.chat_display.setText(new_text)
|
|
self.chat_display.setCaretPosition(len(new_text))
|
|
except Exception as e:
|
|
print("[!] Error in add_chat_message: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
SwingUtilities.invokeLater(update_ui)
|
|
def send_request(self):
|
|
"""Send request via Burp's native HTTP handler"""
|
|
try:
|
|
# Get request from native editor
|
|
request_bytes = self.request_editor.getMessage()
|
|
|
|
if not request_bytes or len(request_bytes) == 0:
|
|
self.add_chat_message("System", "Error: Request editor is empty")
|
|
return
|
|
|
|
# Parse request to extract host, port, protocol
|
|
request_str = self.helpers.bytesToString(request_bytes)
|
|
|
|
# Use Burp's analyzeRequest to get proper structure
|
|
analyzed = self.helpers.analyzeRequest(request_bytes)
|
|
|
|
# Get HTTP service details
|
|
host = analyzed.getUrl().getHost()
|
|
port = analyzed.getUrl().getPort()
|
|
protocol = analyzed.getUrl().getProtocol()
|
|
|
|
use_https = (protocol.lower() == "https")
|
|
|
|
if not host:
|
|
self.add_chat_message("System", "Error: Could not parse host from request")
|
|
return
|
|
|
|
# Build HTTP service
|
|
http_service = self.helpers.buildHttpService(host, port, use_https)
|
|
|
|
# Send request and get response
|
|
response_bytes = self.callbacks.makeHttpRequest(http_service, request_bytes)
|
|
|
|
if response_bytes:
|
|
# Load response into native editor
|
|
self.response_editor.setMessage(response_bytes, False)
|
|
self.add_chat_message("System", "[RESPONSE] Received from " + host)
|
|
|
|
# Store in history
|
|
entry = {
|
|
"method": analyzed.getMethod(),
|
|
"host": host,
|
|
"path": analyzed.getUrl().getPath(),
|
|
"request": request_bytes,
|
|
"response": response_bytes,
|
|
"timestamp": time.time()
|
|
}
|
|
self.ai_history.append(entry)
|
|
|
|
# Update table thread-safe
|
|
def update_table():
|
|
try:
|
|
status_code = self._extract_status_code(response_bytes)
|
|
row_num = len(self.ai_history)
|
|
self.history_model.addRow([
|
|
str(row_num),
|
|
analyzed.getMethod(),
|
|
host[:30],
|
|
analyzed.getUrl().getPath()[:40],
|
|
status_code
|
|
])
|
|
print("[+] Request added to history: " + analyzed.getMethod() + " " + host)
|
|
except Exception as e:
|
|
print("[!] Error updating table: " + str(e))
|
|
|
|
SwingUtilities.invokeLater(update_table)
|
|
|
|
# Auto Analyze if enabled
|
|
if self.auto_analyze:
|
|
print("[*] Auto Analyze triggered")
|
|
thread = threading.Thread(target=self.analyze_with_ai)
|
|
thread.daemon = True
|
|
thread.start()
|
|
else:
|
|
self.add_chat_message("System", "Error: No response from server")
|
|
|
|
except Exception as e:
|
|
print("[!] Error in send_request: " + str(e))
|
|
traceback.print_exc()
|
|
self.add_chat_message("System", "Error: " + str(e))
|
|
|
|
def load_from_history(self):
|
|
"""Load request/response from history table on row selection"""
|
|
try:
|
|
if not self.history_table:
|
|
print("[!] History table not initialized")
|
|
return
|
|
|
|
row = self.history_table.getSelectedRow()
|
|
print("[*] History row selected: " + str(row))
|
|
|
|
if row < 0:
|
|
print("[!] No row selected (row < 0)")
|
|
return
|
|
|
|
if row >= len(self.ai_history):
|
|
print("[!] Row index out of bounds: " + str(row) + " >= " + str(len(self.ai_history)))
|
|
return
|
|
|
|
entry = self.ai_history[row]
|
|
print("[*] Loading from history - row: " + str(row) + ", method: " + entry.get("method", "?"))
|
|
|
|
# Load request
|
|
if "request" in entry and entry["request"]:
|
|
if self.request_editor:
|
|
self.request_editor.setMessage(entry["request"], True)
|
|
print("[+] Request loaded into editor")
|
|
else:
|
|
print("[!] Request editor not available")
|
|
|
|
# Load response
|
|
if "response" in entry and entry["response"]:
|
|
if self.response_editor:
|
|
self.response_editor.setMessage(entry["response"], False)
|
|
print("[+] Response loaded into editor")
|
|
else:
|
|
print("[!] Response editor not available")
|
|
|
|
except Exception as e:
|
|
print("[!] Error loading from history: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
def add_row_to_history(self, row_data):
|
|
"""Thread-safe method to add row to history table"""
|
|
def add_row_ui():
|
|
try:
|
|
if self.history_model:
|
|
self.history_model.addRow(row_data)
|
|
print("[+] Row added to history: " + str(row_data[0]))
|
|
except Exception as e:
|
|
print("[!] Error adding row to history: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
SwingUtilities.invokeLater(add_row_ui)
|
|
|
|
def _extract_status_code(self, response_bytes):
|
|
"""Extract HTTP status code from response"""
|
|
try:
|
|
response_str = self.helpers.bytesToString(response_bytes)
|
|
first_line = response_str.split('\n')[0]
|
|
parts = first_line.split(' ')
|
|
if len(parts) >= 2:
|
|
return parts[1]
|
|
except:
|
|
pass
|
|
return "?"
|
|
|
|
def _clean_reasoning_content(self, text):
|
|
"""Remove thinking/reasoning text from AI response and extract key payload sections"""
|
|
try:
|
|
# Remove common reasoning patterns
|
|
lines = text.split('\n')
|
|
cleaned = []
|
|
skip_section = False
|
|
|
|
for line in lines:
|
|
line_lower = line.lower().strip()
|
|
|
|
# Detect and preserve section headers
|
|
if any(header in line_lower for header in ["[vulns]", "[payloads]", "[attack", "[exploit", "[test", "[issues]", "[vectors]"]):
|
|
cleaned.append(line)
|
|
skip_section = False
|
|
continue
|
|
|
|
# Skip extended thinking patterns
|
|
if any(pattern in line_lower for pattern in [
|
|
"the user is asking",
|
|
"the user is requesting",
|
|
"i need to",
|
|
"let me think",
|
|
"first, let me",
|
|
"to summarize",
|
|
"in conclusion",
|
|
"thinking about",
|
|
"considering the request",
|
|
"<thinking>",
|
|
"</thinking>",
|
|
"analyze the request"
|
|
]):
|
|
continue
|
|
|
|
# Skip lines that are just thinking markers
|
|
if line_lower.startswith("<") and line_lower.endswith(">"):
|
|
continue
|
|
|
|
if line.strip():
|
|
cleaned.append(line)
|
|
|
|
result = '\n'.join(cleaned).strip()
|
|
|
|
# Limit to 800 chars for comprehensive analysis
|
|
if len(result) > 800:
|
|
result = result[:797] + "..."
|
|
|
|
return result if result else "Processing complete"
|
|
except:
|
|
return text
|
|
|
|
def _parse_vulnerability_response(self, response_text):
|
|
"""Parse AI response to extract and structure vulnerability data"""
|
|
try:
|
|
lines = response_text.split('\n')
|
|
parsed = {
|
|
"vulns": [],
|
|
"attack_points": [],
|
|
"payloads": [],
|
|
"exploit_idea": [],
|
|
"raw": response_text
|
|
}
|
|
|
|
current_section = None
|
|
|
|
for line in lines:
|
|
line_stripped = line.strip()
|
|
if not line_stripped:
|
|
continue
|
|
|
|
# Detect section headers
|
|
if "[VULNS]" in line_stripped.upper():
|
|
current_section = "vulns"
|
|
continue
|
|
elif "[ATTACK" in line_stripped.upper():
|
|
current_section = "attack_points"
|
|
continue
|
|
elif "[PAYLOADS]" in line_stripped.upper():
|
|
current_section = "payloads"
|
|
continue
|
|
elif "[EXPLOIT" in line_stripped.upper():
|
|
current_section = "exploit_idea"
|
|
continue
|
|
elif "[" in line_stripped and "]" in line_stripped:
|
|
current_section = None
|
|
continue
|
|
|
|
# Add content to current section
|
|
if current_section and line_stripped:
|
|
if line_stripped.startswith("-"):
|
|
line_stripped = line_stripped[1:].strip()
|
|
parsed[current_section].append(line_stripped)
|
|
|
|
return parsed
|
|
except:
|
|
return {"vulns": [], "attack_points": [], "payloads": [], "exploit_idea": [], "raw": response_text}
|
|
|
|
def _classify_vulnerability_severity(self, vuln_text):
|
|
"""Classify vulnerability severity based on keywords"""
|
|
vuln_lower = vuln_text.lower()
|
|
severity_map = {
|
|
"CRITICAL": ["rce", "code execution", "remote command", "database dump", "full system compromise"],
|
|
"HIGH": ["idor", "ssrf", "sqli", "auth bypass", "session hijack", "privilege escalation", "admin access"],
|
|
"MEDIUM": ["xss", "csrf", "xxe", "header injection", "cookie manipulation", "weak authentication"],
|
|
"LOW": ["information disclosure", "missing headers", "weak configuration", "cors"]
|
|
}
|
|
|
|
for severity, keywords in severity_map.items():
|
|
if any(kw in vuln_lower for kw in keywords):
|
|
return severity
|
|
|
|
return "MEDIUM"
|
|
|
|
def _format_vulnerability_output(self, parsed_vulns):
|
|
"""Format parsed vulnerability data for display"""
|
|
try:
|
|
output = ""
|
|
|
|
if parsed_vulns["vulns"]:
|
|
output += "[VULNERABILITIES]\n"
|
|
for vuln in parsed_vulns["vulns"]:
|
|
severity = self._classify_vulnerability_severity(vuln)
|
|
output += " - " + vuln + " (" + severity + ")\n"
|
|
output += "\n"
|
|
|
|
if parsed_vulns["attack_points"]:
|
|
output += "[ATTACK VECTORS]\n"
|
|
for point in parsed_vulns["attack_points"]:
|
|
output += " - " + point + "\n"
|
|
output += "\n"
|
|
|
|
if parsed_vulns["payloads"]:
|
|
output += "[TEST PAYLOADS]\n"
|
|
for payload in parsed_vulns["payloads"]:
|
|
output += " " + payload + "\n"
|
|
output += "\n"
|
|
|
|
if parsed_vulns["exploit_idea"]:
|
|
output += "[EXPLOITATION STEPS]\n"
|
|
for idea in parsed_vulns["exploit_idea"]:
|
|
output += " - " + idea + "\n"
|
|
|
|
return output if output else parsed_vulns["raw"]
|
|
except:
|
|
return parsed_vulns["raw"]
|
|
|
|
def analyze_with_ai(self):
|
|
"""Analyze request/response as Elite Bug Bounty Hunter - P1 Focus"""
|
|
try:
|
|
# Validate API key
|
|
if not self.api_key:
|
|
self.add_chat_message("System", "Error: API key not configured - cannot analyze")
|
|
return
|
|
|
|
# Get request from editor
|
|
if not self.request_editor:
|
|
self.add_chat_message("System", "Error: Request editor not available")
|
|
return
|
|
|
|
request_bytes = self.request_editor.getMessage()
|
|
|
|
if not request_bytes or len(request_bytes) == 0:
|
|
self.add_chat_message("System", "Error: Request editor is empty")
|
|
return
|
|
|
|
# Convert request to string
|
|
request_str = self.helpers.bytesToString(request_bytes)
|
|
|
|
# Get response from editor if available
|
|
response_str = ""
|
|
if self.response_editor:
|
|
response_bytes = self.response_editor.getMessage()
|
|
if response_bytes and len(response_bytes) > 0:
|
|
response_str = self.helpers.bytesToString(response_bytes)
|
|
|
|
# Bug bounty hunter analysis prompt - P1 focused with structured output
|
|
if response_str:
|
|
analysis_prompt = """You are an elite bug bounty hunter performing security analysis. Identify CRITICAL (P1/P2) vulnerabilities.
|
|
|
|
STRUCTURED ANALYSIS REQUIRED:
|
|
|
|
PRIORITY VULNERABILITIES:
|
|
1. RCE - Remote code execution, command injection
|
|
2. IDOR - Insecure direct object reference, privilege escalation
|
|
3. SSRF - Server-side request forgery, internal access
|
|
4. SQLi - SQL injection, data extraction
|
|
5. Auth bypass - Session hijacking, weak authentication
|
|
6. Critical misconfiguration - Admin panels, debug endpoints
|
|
|
|
SECONDARY CHECK:
|
|
- Missing security headers (CSP, X-Frame-Options, etc.)
|
|
- Weak cookies (missing HttpOnly, Secure, SameSite)
|
|
- CORS misconfiguration
|
|
- XXE, unsafe deserialization
|
|
- Header manipulation/injection
|
|
|
|
RESPONSE FORMAT (MANDATORY):
|
|
|
|
[VULNS]
|
|
- vulnerability name: severity level (CRITICAL/HIGH/MEDIUM/LOW)
|
|
|
|
[ATTACK POINTS]
|
|
- specific parameter/header/endpoint vulnerable
|
|
|
|
[PAYLOADS]
|
|
- raw, executable test code
|
|
- payload2
|
|
|
|
[EXPLOIT IDEA]
|
|
- step 1: how to trigger
|
|
- step 2: expected result
|
|
|
|
Analyze this request/response:
|
|
|
|
Request:
|
|
""" + request_str[:2000] + "\n\nResponse:\n" + response_str[:2000]
|
|
else:
|
|
analysis_prompt = """You are an elite bug bounty hunter. Analyze this request for CRITICAL (P1) vulnerabilities.
|
|
|
|
FOCUS AREAS:
|
|
1. RCE, IDOR, SSRF, SQLi, Auth bypass
|
|
2. Critical misconfigurations
|
|
3. Any exploitable patterns
|
|
|
|
RESPONSE FORMAT:
|
|
|
|
[VULNS]
|
|
- vulnerability: severity
|
|
|
|
[ATTACK POINTS]
|
|
- vulnerable vector
|
|
|
|
[PAYLOADS]
|
|
- test payload
|
|
|
|
[EXPLOIT IDEA]
|
|
- exploitation method
|
|
|
|
Request to analyze:
|
|
""" + request_str[:2000]
|
|
|
|
print("[*] Elite Bug Bounty Hunter Analysis - P1 FOCUS...")
|
|
self.add_chat_message("System", "Analyzing for P1 vulnerabilities...")
|
|
|
|
# Call AI in background
|
|
ai_response = self.call_ai(analysis_prompt)
|
|
|
|
# Parse and format the response
|
|
try:
|
|
parsed = self._parse_vulnerability_response(ai_response)
|
|
formatted = self._format_vulnerability_output(parsed)
|
|
self.add_chat_message("BugBounty", formatted)
|
|
print("[+] Parsed: " + str(len(parsed["vulns"])) + " vulnerabilities identified")
|
|
except:
|
|
# Fallback to raw response if parsing fails
|
|
self.add_chat_message("BugBounty", ai_response)
|
|
|
|
print("[+] P1 Analysis complete")
|
|
|
|
except Exception as e:
|
|
print("[!] Error in analyze_with_ai: " + str(e))
|
|
traceback.print_exc()
|
|
self.add_chat_message("System", "Error analyzing: " + str(e))
|
|
|
|
# ===== API METHODS =====
|
|
|
|
def call_ai(self, user_input, model=None):
|
|
"""Call DigitalOcean AI API with multi-model fallback engine"""
|
|
if not model:
|
|
model = self.model
|
|
|
|
# Ensure model is in our supported list
|
|
if model not in self.all_models:
|
|
model = self.primary_models[0]
|
|
|
|
try:
|
|
# Find starting index
|
|
try:
|
|
start_index = self.all_models.index(model)
|
|
except ValueError:
|
|
start_index = 0
|
|
|
|
# Try models starting from selected, then fallback chain
|
|
for attempt in range(len(self.all_models)):
|
|
current_model_idx = (start_index + attempt) % len(self.all_models)
|
|
current_model = self.all_models[current_model_idx]
|
|
|
|
try:
|
|
url = "https://inference.do-ai.run/v1/chat/completions"
|
|
|
|
payload = {
|
|
"model": current_model,
|
|
"messages": [
|
|
{
|
|
"role": "user",
|
|
"content": user_input
|
|
}
|
|
],
|
|
"max_tokens": 400
|
|
}
|
|
|
|
headers = {
|
|
"Content-Type": "application/json",
|
|
"Authorization": "Bearer " + self.api_key
|
|
}
|
|
|
|
data_str = json.dumps(payload)
|
|
|
|
try:
|
|
if isinstance(data_str, unicode):
|
|
data_str = data_str.encode('utf-8')
|
|
except NameError:
|
|
pass
|
|
|
|
req = Request(url, data=data_str, headers=headers)
|
|
|
|
try:
|
|
response = urlopen(req, timeout=15)
|
|
resp_data = response.read()
|
|
|
|
if isinstance(resp_data, bytes):
|
|
resp_data = resp_data.decode('utf-8')
|
|
|
|
print("[*] API Response from " + current_model + ": " + resp_data[:80] + "...")
|
|
|
|
resp_json = json.loads(resp_data)
|
|
|
|
if 'choices' in resp_json and len(resp_json['choices']) > 0:
|
|
choice = resp_json['choices'][0]
|
|
|
|
if 'message' in choice:
|
|
msg = choice['message']
|
|
|
|
# Try content first
|
|
if 'content' in msg and msg['content'] and msg['content'].strip():
|
|
return msg['content'].strip()
|
|
|
|
# Fallback to reasoning_content if available
|
|
if 'reasoning_content' in msg and msg['reasoning_content']:
|
|
cleaned = self._clean_reasoning_content(msg['reasoning_content'])
|
|
if cleaned and cleaned.strip():
|
|
return cleaned
|
|
|
|
# Response received but empty - try next model
|
|
print("[!] Empty response from " + current_model + " - trying next model")
|
|
continue
|
|
|
|
except HTTPError as e:
|
|
error_code = e.code
|
|
print("[!] HTTPError " + str(error_code) + " from " + current_model + " - trying next model")
|
|
|
|
if error_code == 401:
|
|
return "API authentication failed - check API key"
|
|
|
|
# For other errors, continue to next model
|
|
continue
|
|
|
|
except Exception as e:
|
|
print("[!] Exception from " + current_model + ": " + str(e) + " - trying next model")
|
|
continue
|
|
|
|
except Exception as e:
|
|
print("[!] Error preparing request for " + current_model + ": " + str(e))
|
|
continue
|
|
|
|
# All models failed
|
|
return "AI service temporarily unavailable - tried all models"
|
|
|
|
except Exception as e:
|
|
print("[!] Exception in call_ai: " + str(e))
|
|
traceback.print_exc()
|
|
return "AI service error"
|
|
|
|
def forward_to_ai_from_menu(self, messages):
|
|
try:
|
|
if not messages or len(messages) == 0:
|
|
return
|
|
|
|
message = messages[0]
|
|
request = message.getRequest()
|
|
response = message.getResponse()
|
|
|
|
if not request:
|
|
return
|
|
|
|
# Load into native repeater editor
|
|
self.request_editor.setMessage(request, True)
|
|
|
|
if response:
|
|
self.response_editor.setMessage(response, False)
|
|
|
|
try:
|
|
# Get request details using analyzeRequest with message object
|
|
analyzed = self.helpers.analyzeRequest(message)
|
|
method = analyzed.getMethod()
|
|
host = analyzed.getUrl().getHost()
|
|
path = analyzed.getUrl().getPath()
|
|
|
|
# Add to history
|
|
entry = {
|
|
"method": method,
|
|
"host": host,
|
|
"path": path,
|
|
"request": request,
|
|
"response": response,
|
|
"timestamp": time.time()
|
|
}
|
|
self.ai_history.append(entry)
|
|
|
|
# Update table on UI thread
|
|
def update_table():
|
|
try:
|
|
row_num = len(self.ai_history)
|
|
status = self._extract_status_code(response)
|
|
self.history_model.addRow([
|
|
str(row_num),
|
|
method,
|
|
host[:25],
|
|
path[:35],
|
|
status
|
|
])
|
|
print("[+] Added request to history: " + method + " " + host + path)
|
|
except Exception as table_err:
|
|
print("[!] Error adding row to history: " + str(table_err))
|
|
traceback.print_exc()
|
|
|
|
SwingUtilities.invokeLater(update_table)
|
|
except Exception as analyze_err:
|
|
print("[!] Error analyzing request: " + str(analyze_err))
|
|
traceback.print_exc()
|
|
|
|
self.add_chat_message("System", "[FORWARDED] Request from Proxy/Repeater/Target")
|
|
except Exception as e:
|
|
print("[!] Error in forward_to_ai_from_menu: " + str(e))
|
|
traceback.print_exc()
|
|
self.add_chat_message("System", "Error forwarding request: " + str(e))
|
|
|
|
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
|
|
"""HTTP listener callback - captures all traffic"""
|
|
try:
|
|
if not messageIsRequest:
|
|
response = messageInfo.getResponse()
|
|
|
|
if response:
|
|
try:
|
|
request = messageInfo.getRequest()
|
|
|
|
# CRITICAL FIX: Use analyzeRequest with messageInfo object, NOT just request bytes
|
|
analyzed = self.helpers.analyzeRequest(messageInfo)
|
|
method = analyzed.getMethod()
|
|
host = analyzed.getUrl().getHost()
|
|
path = analyzed.getUrl().getPath()
|
|
|
|
# Store in history with size limit
|
|
entry = {
|
|
"method": method,
|
|
"host": host,
|
|
"path": path,
|
|
"request": request,
|
|
"response": response,
|
|
"timestamp": time.time()
|
|
}
|
|
self.ai_history.append(entry)
|
|
self.traffic_log.append(messageInfo)
|
|
|
|
# Limit history to max_history entries to prevent memory bloat
|
|
if len(self.ai_history) > self.max_history:
|
|
self.ai_history.pop(0)
|
|
if len(self.traffic_log) > self.max_history:
|
|
self.traffic_log.pop(0)
|
|
|
|
# Update table on UI thread only
|
|
def update_table():
|
|
try:
|
|
status = self._extract_status_code(response)
|
|
row_num = len(self.ai_history)
|
|
if self.history_model:
|
|
self.history_model.addRow([
|
|
str(row_num),
|
|
method,
|
|
host[:25],
|
|
path[:35],
|
|
status
|
|
])
|
|
print("[+] Traffic captured: " + method + " " + host + path + " [" + status + "]")
|
|
except Exception as table_err:
|
|
print("[!] Error updating history table: " + str(table_err))
|
|
|
|
SwingUtilities.invokeLater(update_table)
|
|
|
|
except Exception as e:
|
|
print("[!] Error processing HTTP message: " + str(e))
|
|
traceback.print_exc()
|
|
|
|
except Exception as e:
|
|
print("[!] Error in processHttpMessage: " + str(e))
|
|
traceback.print_exc()
|