Version: 4.1 Update
This commit is contained in:
@@ -2,6 +2,7 @@ package hae.component.board;
|
||||
|
||||
import burp.api.montoya.MontoyaApi;
|
||||
import hae.Config;
|
||||
import hae.cache.DataQueryCache;
|
||||
import hae.component.board.message.MessageTableModel;
|
||||
import hae.component.board.message.MessageTableModel.MessageTable;
|
||||
import hae.component.board.table.Datatable;
|
||||
@@ -23,19 +24,17 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Databoard extends JPanel {
|
||||
private static Boolean isMatchHost = false;
|
||||
private final MontoyaApi api;
|
||||
private final ConfigLoader configLoader;
|
||||
private final MessageTableModel messageTableModel;
|
||||
|
||||
private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
|
||||
private final JComboBox hostComboBox = new JComboBox(comboBoxModel);
|
||||
private JTextField hostTextField;
|
||||
private JTabbedPane dataTabbedPane;
|
||||
private JSplitPane splitPane;
|
||||
private MessageTable messageTable;
|
||||
|
||||
private static Boolean isMatchHost = false;
|
||||
private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
|
||||
private final JComboBox hostComboBox = new JComboBox(comboBoxModel);
|
||||
|
||||
private JProgressBar progressBar;
|
||||
private SwingWorker<Map<String, List<String>>, Void> handleComboBoxWorker;
|
||||
private SwingWorker<Void, Void> applyHostFilterWorker;
|
||||
|
||||
@@ -47,13 +46,25 @@ public class Databoard extends JPanel {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
public static void setProgressBar(boolean status, JProgressBar progressBar, String showString) {
|
||||
progressBar.setIndeterminate(status);
|
||||
if (!status) {
|
||||
progressBar.setMaximum(100);
|
||||
progressBar.setString("OK");
|
||||
progressBar.setStringPainted(true);
|
||||
progressBar.setValue(progressBar.getMaximum());
|
||||
} else {
|
||||
progressBar.setString(showString);
|
||||
progressBar.setStringPainted(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
setLayout(new GridBagLayout());
|
||||
((GridBagLayout) getLayout()).columnWidths = new int[]{25, 0, 0, 0, 20, 0};
|
||||
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0};
|
||||
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0, 0};
|
||||
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4};
|
||||
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 1.0E-4};
|
||||
|
||||
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 0.0, 1.0E-4};
|
||||
JLabel hostLabel = new JLabel("Host:");
|
||||
|
||||
JButton clearButton = new JButton("Clear");
|
||||
@@ -81,7 +92,7 @@ public class Databoard extends JPanel {
|
||||
|
||||
clearButton.addActionListener(this::clearActionPerformed);
|
||||
|
||||
|
||||
progressBar = new JProgressBar();
|
||||
splitPane.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
@@ -90,6 +101,7 @@ public class Databoard extends JPanel {
|
||||
});
|
||||
|
||||
splitPane.setVisible(false);
|
||||
progressBar.setVisible(false);
|
||||
|
||||
add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
||||
new Insets(8, 0, 5, 5), 0, 0));
|
||||
@@ -98,9 +110,12 @@ public class Databoard extends JPanel {
|
||||
add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
||||
new Insets(8, 0, 5, 5), 0, 0));
|
||||
|
||||
add(splitPane, new GridBagConstraints(1, 1, 3, 2, 0.0, 1.0,
|
||||
add(splitPane, new GridBagConstraints(1, 1, 3, 1, 0.0, 1.0,
|
||||
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
||||
new Insets(0, 5, 0, 5), 0, 0));
|
||||
add(progressBar, new GridBagConstraints(1, 2, 3, 1, 1.0, 0.0,
|
||||
GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
|
||||
new Insets(0, 5, 0, 5), 0, 0));
|
||||
hostComboBox.setMaximumRowCount(5);
|
||||
add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
||||
new Insets(8, 0, 5, 5), 0, 0));
|
||||
@@ -120,6 +135,10 @@ public class Databoard extends JPanel {
|
||||
columnModel.getColumn(5).setPreferredWidth((int) (totalWidth * 0.1));
|
||||
}
|
||||
|
||||
private void setProgressBar(boolean status) {
|
||||
setProgressBar(status, progressBar, "Loading ...");
|
||||
}
|
||||
|
||||
private void setAutoMatch() {
|
||||
hostComboBox.setSelectedItem(null);
|
||||
hostComboBox.addActionListener(this::handleComboBoxAction);
|
||||
@@ -155,6 +174,8 @@ public class Databoard extends JPanel {
|
||||
String selectedHost = hostComboBox.getSelectedItem().toString();
|
||||
|
||||
if (getHostByList().contains(selectedHost)) {
|
||||
progressBar.setVisible(true);
|
||||
setProgressBar(true);
|
||||
hostTextField.setText(selectedHost);
|
||||
|
||||
if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) {
|
||||
@@ -193,6 +214,8 @@ public class Databoard extends JPanel {
|
||||
|
||||
hostComboBox.setPopupVisible(false);
|
||||
applyHostFilter(selectedHost);
|
||||
|
||||
setProgressBar(false);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
@@ -230,6 +253,12 @@ public class Databoard extends JPanel {
|
||||
}
|
||||
|
||||
private Map<String, List<String>> getSelectedMapByHost(String selectedHost) {
|
||||
// 先尝试从缓存获取结果
|
||||
Map<String, List<String>> cachedResult = DataQueryCache.getHostQueryResult(selectedHost);
|
||||
if (cachedResult != null) {
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
|
||||
Map<String, List<String>> selectedDataMap;
|
||||
|
||||
@@ -255,6 +284,11 @@ public class Databoard extends JPanel {
|
||||
selectedDataMap = dataMap.get(selectedHost);
|
||||
}
|
||||
|
||||
// 将结果存入缓存
|
||||
if (selectedDataMap != null) {
|
||||
DataQueryCache.putHostQueryResult(selectedHost, selectedDataMap);
|
||||
}
|
||||
|
||||
return selectedDataMap;
|
||||
}
|
||||
|
||||
@@ -314,19 +348,31 @@ public class Databoard extends JPanel {
|
||||
}
|
||||
|
||||
private List<String> getHostByList() {
|
||||
if (!Config.globalDataMap.keySet().isEmpty()) {
|
||||
return new ArrayList<>(Config.globalDataMap.keySet());
|
||||
// 先尝试从缓存获取结果
|
||||
List<String> cachedResult = DataQueryCache.getHostFilterResult("all_hosts");
|
||||
if (cachedResult != null) {
|
||||
return cachedResult;
|
||||
}
|
||||
return new ArrayList<>();
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
if (!Config.globalDataMap.isEmpty()) {
|
||||
result = new ArrayList<>(Config.globalDataMap.keySet());
|
||||
// 将结果存入缓存
|
||||
DataQueryCache.putHostFilterResult("all_hosts", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void clearActionPerformed(ActionEvent e) {
|
||||
// 清除缓存
|
||||
DataQueryCache.clearCache();
|
||||
int retCode = JOptionPane.showConfirmDialog(this, "Do you want to clear data?", "Info",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
String host = hostTextField.getText();
|
||||
if (retCode == JOptionPane.YES_OPTION && !host.isEmpty()) {
|
||||
dataTabbedPane.removeAll();
|
||||
splitPane.setVisible(false);
|
||||
progressBar.setVisible(false);
|
||||
|
||||
Config.globalDataMap.keySet().parallelStream().forEach(key -> {
|
||||
if (StringProcessor.matchesHostPattern(key, host) || host.equals("*")) {
|
||||
@@ -353,8 +399,8 @@ public class Databoard extends JPanel {
|
||||
|
||||
keysToRemove.forEach(Config.globalDataMap::remove);
|
||||
|
||||
if (Config.globalDataMap.keySet().size() == 1 && Config.globalDataMap.keySet().stream().anyMatch(key -> key.equals("*"))) {
|
||||
Config.globalDataMap.keySet().remove("*");
|
||||
if (Config.globalDataMap.size() == 1 && Config.globalDataMap.keySet().stream().anyMatch(key -> key.equals("*"))) {
|
||||
Config.globalDataMap.remove("*");
|
||||
}
|
||||
|
||||
messageTableModel.deleteByHost(host);
|
||||
|
||||
@@ -10,7 +10,7 @@ import burp.api.montoya.ui.UserInterface;
|
||||
import burp.api.montoya.ui.editor.HttpRequestEditor;
|
||||
import burp.api.montoya.ui.editor.HttpResponseEditor;
|
||||
import hae.Config;
|
||||
import hae.cache.CachePool;
|
||||
import hae.cache.MessageCache;
|
||||
import hae.utils.ConfigLoader;
|
||||
import hae.utils.DataManager;
|
||||
import hae.utils.string.HashCalculator;
|
||||
@@ -90,7 +90,7 @@ public class MessageTableModel extends AbstractTableModel {
|
||||
messageTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||
|
||||
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||
// 请求/相应文本框
|
||||
// 请求/响应文本框
|
||||
JScrollPane scrollPane = new JScrollPane(messageTable);
|
||||
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
|
||||
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
@@ -98,7 +98,7 @@ public class MessageTableModel extends AbstractTableModel {
|
||||
splitPane.setRightComponent(messageTab);
|
||||
}
|
||||
|
||||
public void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, boolean flag) {
|
||||
public synchronized void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, boolean flag) {
|
||||
synchronized (log) {
|
||||
boolean isDuplicate = false;
|
||||
MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status);
|
||||
@@ -157,6 +157,26 @@ public class MessageTableModel extends AbstractTableModel {
|
||||
|
||||
}
|
||||
|
||||
public synchronized void addBatch(List<Object[]> batchData) {
|
||||
synchronized (log) {
|
||||
for (Object[] data : batchData) {
|
||||
HttpRequestResponse messageInfo = (HttpRequestResponse) data[0];
|
||||
String url = (String) data[1];
|
||||
String method = (String) data[2];
|
||||
String status = (String) data[3];
|
||||
String length = (String) data[4];
|
||||
String comment = (String) data[5];
|
||||
String color = (String) data[6];
|
||||
|
||||
// 复用现有的 add 方法逻辑,但跳过重复检查
|
||||
MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status);
|
||||
log.add(logEntry);
|
||||
}
|
||||
}
|
||||
// 批量更新完成后一次性通知表格更新
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void deleteByHost(String filterText) {
|
||||
filteredLog.clear();
|
||||
List<Integer> rowsToRemove = new ArrayList<>();
|
||||
@@ -317,7 +337,7 @@ public class MessageTableModel extends AbstractTableModel {
|
||||
|
||||
private Map<String, Map<String, Object>> getCacheData(byte[] content) {
|
||||
String hashIndex = HashCalculator.calculateHash(content);
|
||||
return CachePool.get(hashIndex);
|
||||
return MessageCache.get(hashIndex);
|
||||
}
|
||||
|
||||
private boolean areMapsEqual(Map<String, Map<String, Object>> map1, Map<String, Map<String, Object>> map2) {
|
||||
@@ -426,11 +446,11 @@ public class MessageTableModel extends AbstractTableModel {
|
||||
}
|
||||
|
||||
public class MessageTable extends JTable {
|
||||
private MessageEntry messageEntry;
|
||||
private final ExecutorService executorService;
|
||||
private int lastSelectedIndex = -1;
|
||||
private final HttpRequestEditor requestEditor;
|
||||
private final HttpResponseEditor responseEditor;
|
||||
private MessageEntry messageEntry;
|
||||
private int lastSelectedIndex = -1;
|
||||
|
||||
public MessageTable(TableModel messageTableModel, HttpRequestEditor requestEditor, HttpResponseEditor responseEditor) {
|
||||
super(messageTableModel);
|
||||
|
||||
@@ -11,12 +11,41 @@ import java.awt.event.*;
|
||||
|
||||
public class Rules extends JTabbedPane {
|
||||
private final MontoyaApi api;
|
||||
private ConfigLoader configLoader;
|
||||
private final RuleProcessor ruleProcessor;
|
||||
private final JTextField ruleGroupNameTextField;
|
||||
|
||||
private ConfigLoader configLoader;
|
||||
private Component tabComponent;
|
||||
private int selectedIndex;
|
||||
private final Action cancelActionPerformed = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (selectedIndex >= 0) {
|
||||
setTabComponentAt(selectedIndex, tabComponent);
|
||||
|
||||
ruleGroupNameTextField.setVisible(false);
|
||||
ruleGroupNameTextField.setPreferredSize(null);
|
||||
selectedIndex = -1;
|
||||
tabComponent = null;
|
||||
|
||||
requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
};
|
||||
private final Action renameTitleActionPerformed = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String title = ruleGroupNameTextField.getText();
|
||||
if (!title.isEmpty() && selectedIndex >= 0) {
|
||||
String oldName = getTitleAt(selectedIndex);
|
||||
setTitleAt(selectedIndex, title);
|
||||
|
||||
if (!oldName.equals(title)) {
|
||||
ruleProcessor.renameRuleGroup(oldName, title);
|
||||
}
|
||||
}
|
||||
cancelActionPerformed.actionPerformed(null);
|
||||
}
|
||||
};
|
||||
|
||||
public Rules(MontoyaApi api, ConfigLoader configLoader) {
|
||||
this.api = api;
|
||||
@@ -49,43 +78,48 @@ public class Rules extends JTabbedPane {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
int index = getSelectedIndex();
|
||||
Rectangle r = getBoundsAt(index);
|
||||
if (r.contains(e.getPoint()) && index >= 0) {
|
||||
switch (e.getButton()) {
|
||||
case MouseEvent.BUTTON1:
|
||||
if (e.getClickCount() == 2) {
|
||||
selectedIndex = index;
|
||||
tabComponent = getTabComponentAt(selectedIndex);
|
||||
String ruleGroupName = getTitleAt(selectedIndex);
|
||||
int index = indexAtLocation(e.getX(), e.getY());
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!"...".equals(ruleGroupName)) {
|
||||
setTabComponentAt(selectedIndex, ruleGroupNameTextField);
|
||||
ruleGroupNameTextField.setVisible(true);
|
||||
ruleGroupNameTextField.setText(ruleGroupName);
|
||||
ruleGroupNameTextField.selectAll();
|
||||
ruleGroupNameTextField.requestFocusInWindow();
|
||||
ruleGroupNameTextField.setMinimumSize(ruleGroupNameTextField.getPreferredSize());
|
||||
}
|
||||
} else if (e.getClickCount() == 1) {
|
||||
if ("...".equals(getTitleAt(getSelectedIndex()))) {
|
||||
String title = ruleProcessor.newRule();
|
||||
Rule newRule = new Rule(api, configLoader, Config.ruleTemplate, tabbedPane);
|
||||
insertTab(title, null, newRule, null, getTabCount() - 1);
|
||||
setSelectedIndex(getTabCount() - 2);
|
||||
} else {
|
||||
renameTitleActionPerformed.actionPerformed(null);
|
||||
}
|
||||
switch (e.getButton()) {
|
||||
case MouseEvent.BUTTON1:
|
||||
if (e.getClickCount() == 2) {
|
||||
selectedIndex = index;
|
||||
tabComponent = getTabComponentAt(selectedIndex);
|
||||
String ruleGroupName = getTitleAt(selectedIndex);
|
||||
|
||||
if (!"...".equals(ruleGroupName)) {
|
||||
setTabComponentAt(selectedIndex, ruleGroupNameTextField);
|
||||
ruleGroupNameTextField.setVisible(true);
|
||||
ruleGroupNameTextField.setText(ruleGroupName);
|
||||
ruleGroupNameTextField.selectAll();
|
||||
ruleGroupNameTextField.requestFocusInWindow();
|
||||
ruleGroupNameTextField.setMinimumSize(ruleGroupNameTextField.getPreferredSize());
|
||||
}
|
||||
break;
|
||||
case MouseEvent.BUTTON3:
|
||||
if (!"...".equals(getTitleAt(getSelectedIndex()))) {
|
||||
popupMenu.show(e.getComponent(), e.getX(), e.getY());
|
||||
} else if (e.getClickCount() == 1) {
|
||||
String title = getTitleAt(index);
|
||||
if ("...".equals(title)) {
|
||||
// 阻止默认的选中行为
|
||||
e.consume();
|
||||
// 直接创建新标签
|
||||
String newTitle = ruleProcessor.newRule();
|
||||
Rule newRule = new Rule(api, configLoader, Config.ruleTemplate, Rules.this);
|
||||
insertTab(newTitle, null, newRule, null, getTabCount() - 1);
|
||||
setSelectedIndex(getTabCount() - 2);
|
||||
} else {
|
||||
renameTitleActionPerformed.actionPerformed(null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MouseEvent.BUTTON3:
|
||||
if (!"...".equals(getTitleAt(index))) {
|
||||
popupMenu.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -119,38 +153,6 @@ public class Rules extends JTabbedPane {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Action renameTitleActionPerformed = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String title = ruleGroupNameTextField.getText();
|
||||
if (!title.isEmpty() && selectedIndex >= 0) {
|
||||
String oldName = getTitleAt(selectedIndex);
|
||||
setTitleAt(selectedIndex, title);
|
||||
|
||||
if (!oldName.equals(title)) {
|
||||
ruleProcessor.renameRuleGroup(oldName, title);
|
||||
}
|
||||
}
|
||||
cancelActionPerformed.actionPerformed(null);
|
||||
}
|
||||
};
|
||||
|
||||
private final Action cancelActionPerformed = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (selectedIndex >= 0) {
|
||||
setTabComponentAt(selectedIndex, tabComponent);
|
||||
|
||||
ruleGroupNameTextField.setVisible(false);
|
||||
ruleGroupNameTextField.setPreferredSize(null);
|
||||
selectedIndex = -1;
|
||||
tabComponent = null;
|
||||
|
||||
requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user