Version: 4.0 Update

This commit is contained in:
gh0stkey
2024-12-21 15:34:45 +08:00
parent 1f1ca99f10
commit daacb2e146
24 changed files with 469 additions and 1427 deletions

View File

@@ -22,8 +22,6 @@ dependencies {
implementation 'org.yaml:snakeyaml:2.0' implementation 'org.yaml:snakeyaml:2.0'
implementation 'dk.brics.automaton:automaton:1.11-8' implementation 'dk.brics.automaton:automaton:1.11-8'
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8' implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
implementation 'com.google.code.gson:gson:2.11.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
} }
test { test {
@@ -36,4 +34,4 @@ jar {
from { from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

View File

@@ -61,11 +61,6 @@ public class Config {
"gray" "gray"
}; };
public static String prompt = "You are a data security expert in the field of cyber security. Your task is to optimize the information provided by the user and then output it in JSON format. The user-supplied information is data that has been extracted by regular expressions. The user-supplied information is divided into two parts, the first part is RuleName which represents the name of the regular expression and the second part is MarkInfo which represents the data extracted by the regular expression. You need to find the matching or similar data in MarkInfo according to the meaning of RuleName, and output the original rows of these data in JSON format.(garbled and meaningless data rows should be removed)\n" +
"You must ensure that the extracted data is accurately expressed and correctly formatted in the JSON structure. Your output data must comply with the original MarkInfo content rows without modification, and strictly adhere to the following JSON format for return, no other text, code and formatting (e.g., line breaks, carriage returns, indentation, spaces), once the return of other irrelevant content will cause irreparable damage to the user: {\"data\":[\"data1\", \"data2\"]}.";
public static String userTextFormat = "User Input: \r\nRuleName: %s\r\nMarkInfo: %s";
public static Map<String, Object[][]> globalRules = new HashMap<>(); public static Map<String, Object[][]> globalRules = new HashMap<>();
public static ConcurrentHashMap<String, Map<String, List<String>>> globalDataMap = new ConcurrentHashMap<>(); public static ConcurrentHashMap<String, Map<String, List<String>>> globalDataMap = new ConcurrentHashMap<>();

View File

@@ -12,19 +12,21 @@ import hae.instances.editor.ResponseEditor;
import hae.instances.editor.WebSocketEditor; import hae.instances.editor.WebSocketEditor;
import hae.instances.websocket.WebSocketMessageHandler; import hae.instances.websocket.WebSocketMessageHandler;
import hae.utils.ConfigLoader; import hae.utils.ConfigLoader;
import hae.utils.DataManager;
public class HaE implements BurpExtension { public class HaE implements BurpExtension {
@Override @Override
public void initialize(MontoyaApi api) { public void initialize(MontoyaApi api) {
// 设置扩展名称 // 设置扩展名称
String version = "3.4"; String version = "4.0";
api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version)); api.extension().setName("HaE - Highlighter and Extractor");
// 加载扩展后输出的项目信息 // 加载扩展后输出的项目信息
Logging logging = api.logging(); Logging logging = api.logging();
logging.logToOutput("[ HACK THE WORLD - TO DO IT ]"); logging.logToOutput("[ HACK THE WORLD - TO DO IT ]");
logging.logToOutput("[#] Author: EvilChen && 0chencc && vaycore"); logging.logToOutput("[#] Author: EvilChen && 0chencc && vaycore");
logging.logToOutput("[#] Github: https://github.com/gh0stkey/HaE"); logging.logToOutput("[#] Github: https://github.com/gh0stkey/HaE");
logging.logToOutput("[#] Version: " + version);
// 配置文件加载 // 配置文件加载
ConfigLoader configLoader = new ConfigLoader(api); ConfigLoader configLoader = new ConfigLoader(api);
@@ -42,6 +44,10 @@ public class HaE implements BurpExtension {
api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api, configLoader)); api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api, configLoader));
api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api, configLoader)); api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api, configLoader));
// 从BurpSuite里加载数据
DataManager dataManager = new DataManager(api);
dataManager.loadData(messageTableModel);
api.extension().registerUnloadingHandler(new ExtensionUnloadingHandler() { api.extension().registerUnloadingHandler(new ExtensionUnloadingHandler() {
@Override @Override
public void extensionUnloaded() { public void extensionUnloaded() {

View File

@@ -29,7 +29,6 @@ public class Config extends JPanel {
private final ConfigLoader configLoader; private final ConfigLoader configLoader;
private final MessageTableModel messageTableModel; private final MessageTableModel messageTableModel;
private final Rules rules; private final Rules rules;
private final String defaultText = "Enter a new item";
private Registration activeHandler; private Registration activeHandler;
private Registration passiveHandler; private Registration passiveHandler;
@@ -61,22 +60,22 @@ public class Config extends JPanel {
pathTextField.setEditable(false); pathTextField.setEditable(false);
pathTextField.setText(configLoader.getRulesFilePath()); pathTextField.setText(configLoader.getRulesFilePath());
JButton reloadButton = new JButton("Reload"); JButton reloadButton = new JButton("Reload");
JButton updateButton = new JButton("Update"); JButton reinitButton = new JButton("Reinit");
ruleInfoPanel.add(ruleLabel); ruleInfoPanel.add(ruleLabel);
ruleInfoPanel.add(pathTextField, constraints); ruleInfoPanel.add(pathTextField, constraints);
ruleInfoPanel.add(Box.createHorizontalStrut(5)); ruleInfoPanel.add(Box.createHorizontalStrut(5));
ruleInfoPanel.add(reloadButton); ruleInfoPanel.add(reinitButton);
ruleInfoPanel.add(Box.createHorizontalStrut(5)); ruleInfoPanel.add(Box.createHorizontalStrut(5));
ruleInfoPanel.add(updateButton); ruleInfoPanel.add(reloadButton);
reloadButton.addActionListener(this::reloadActionPerformed); reloadButton.addActionListener(this::reloadActionPerformed);
updateButton.addActionListener(this::onlineUpdateActionPerformed); reinitButton.addActionListener(this::reinitActionPerformed);
constraints.gridx = 1; constraints.gridx = 1;
JTabbedPane configTabbedPanel = new JTabbedPane(); JTabbedPane configTabbedPanel = new JTabbedPane();
String[] settingMode = new String[]{"Exclude suffix", "Block host", "Exclude status"}; String[] settingMode = new String[]{"Exclude suffix", "Block host", "Exclude status"};
JPanel settingPanel = createConfigTablePanel(settingMode, "Setting"); JPanel settingPanel = createConfigTablePanel(settingMode);
JPanel northPanel = new JPanel(new BorderLayout()); JPanel northPanel = new JPanel(new BorderLayout());
@@ -105,38 +104,6 @@ public class Config extends JPanel {
settingPanel.add(northPanel, BorderLayout.NORTH); settingPanel.add(northPanel, BorderLayout.NORTH);
configTabbedPanel.add("Setting", settingPanel); configTabbedPanel.add("Setting", settingPanel);
String[] aiMode = new String[]{"Alibaba", "Moonshot"};
JPanel aiPanel = createConfigTablePanel(aiMode, "AI+");
JTextArea promptTextArea = new JTextArea();
promptTextArea.setLineWrap(true);
promptTextArea.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
onTextChange();
}
@Override
public void removeUpdate(DocumentEvent e) {
onTextChange();
}
@Override
public void changedUpdate(DocumentEvent e) {
onTextChange();
}
private void onTextChange() {
String promptText = promptTextArea.getText();
configLoader.setAIPrompt(promptText);
}
});
promptTextArea.setText(configLoader.getAIPrompt());
JScrollPane promptScrollPane = new JScrollPane(promptTextArea);
promptScrollPane.setBorder(new TitledBorder("Prompt"));
promptScrollPane.setPreferredSize(new Dimension(0, 100));
aiPanel.add(promptScrollPane, BorderLayout.NORTH);
configTabbedPanel.add("AI+", aiPanel);
add(ruleInfoPanel, BorderLayout.NORTH); add(ruleInfoPanel, BorderLayout.NORTH);
add(configTabbedPanel, BorderLayout.CENTER); add(configTabbedPanel, BorderLayout.CENTER);
} }
@@ -256,47 +223,8 @@ public class Config extends JPanel {
}; };
} }
private TableModelListener craeteAITableModelListener(JComboBox<String> setTypeComboBox, DefaultTableModel model) {
return new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
String selected = (String) setTypeComboBox.getSelectedItem();
String values = getFirstColumnDataAsString(model);
if (selected.equals("Alibaba")) { private JPanel createConfigTablePanel(String[] mode) {
if (!values.equals(configLoader.getAlibabaAIAPIKey()) && !values.isEmpty()) {
configLoader.setAlibabaAIAPIKey(values);
}
}
if (selected.equals("Moonshot")) {
if (!values.equals(configLoader.getMoonshotAIAPIKey()) && !values.isEmpty()) {
configLoader.setMoonshotAIAPIKey(values);
}
}
}
};
}
private ActionListener createAIActionListener(JComboBox<String> setTypeComboBox, DefaultTableModel model) {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String selected = (String) setTypeComboBox.getSelectedItem();
model.setRowCount(0);
if (selected.equals("Alibaba")) {
addDataToTable(configLoader.getAlibabaAIAPIKey().replaceAll("\\|", "\r\n"), model);
}
if (selected.equals("Moonshot")) {
addDataToTable(configLoader.getMoonshotAIAPIKey().replaceAll("\\|", "\r\n"), model);
}
}
};
}
private JPanel createConfigTablePanel(String[] mode, String type) {
GridBagConstraints constraints = new GridBagConstraints(); GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 1.0; constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL; constraints.fill = GridBagConstraints.HORIZONTAL;
@@ -327,9 +255,9 @@ public class Config extends JPanel {
JComboBox<String> setTypeComboBox = new JComboBox<>(); JComboBox<String> setTypeComboBox = new JComboBox<>();
setTypeComboBox.setModel(new DefaultComboBoxModel<>(mode)); setTypeComboBox.setModel(new DefaultComboBoxModel<>(mode));
model.addTableModelListener(type.equals("AI+") ? craeteAITableModelListener(setTypeComboBox, model) : craeteSettingTableModelListener(setTypeComboBox, model)); model.addTableModelListener(craeteSettingTableModelListener(setTypeComboBox, model));
setTypeComboBox.addActionListener(type.equals("AI+") ? createAIActionListener(setTypeComboBox, model) : createSettingActionListener(setTypeComboBox, model)); setTypeComboBox.addActionListener(createSettingActionListener(setTypeComboBox, model));
setTypeComboBox.setSelectedItem(mode[0]); setTypeComboBox.setSelectedItem(mode[0]);
@@ -346,6 +274,7 @@ public class Config extends JPanel {
buttonPanel.add(clearButton, constraints); buttonPanel.add(clearButton, constraints);
JTextField addTextField = new JTextField(); JTextField addTextField = new JTextField();
String defaultText = "Enter a new item";
UIEnhancer.setTextFieldPlaceholder(addTextField, defaultText); UIEnhancer.setTextFieldPlaceholder(addTextField, defaultText);
inputPanelB.add(addTextField, BorderLayout.CENTER); inputPanelB.add(addTextField, BorderLayout.CENTER);
@@ -390,7 +319,7 @@ public class Config extends JPanel {
JPanel settingMainPanel = new JPanel(new BorderLayout()); JPanel settingMainPanel = new JPanel(new BorderLayout());
settingMainPanel.setBorder(new EmptyBorder(5, 15, 10, 15)); settingMainPanel.setBorder(new EmptyBorder(5, 15, 10, 15));
JScrollPane settingScroller = new JScrollPane(settingPanel); JScrollPane settingScroller = new JScrollPane(settingPanel);
settingScroller.setBorder(new TitledBorder(type.equals("AI+") ? "API Key" : "Setting")); settingScroller.setBorder(new TitledBorder("Setting"));
settingMainPanel.add(settingScroller, BorderLayout.CENTER); settingMainPanel.add(settingScroller, BorderLayout.CENTER);
return settingMainPanel; return settingMainPanel;
@@ -492,16 +421,17 @@ public class Config extends JPanel {
} }
} }
private void onlineUpdateActionPerformed(ActionEvent e) {
// 添加提示框防止用户误触导致配置更新
int retCode = JOptionPane.showConfirmDialog(this, "Do you want to update rules?", "Info", JOptionPane.YES_NO_OPTION);
if (retCode == JOptionPane.YES_OPTION) {
configLoader.initRulesByNet();
reloadActionPerformed(null);
}
}
private void reloadActionPerformed(ActionEvent e) { private void reloadActionPerformed(ActionEvent e) {
rules.reloadRuleGroup(); rules.reloadRuleGroup();
} }
private void reinitActionPerformed(ActionEvent e) {
int retCode = JOptionPane.showConfirmDialog(this, "Do you want to reinitialize rules? This action will overwrite your existing rules.", "Info", JOptionPane.YES_NO_OPTION);
if (retCode == JOptionPane.YES_OPTION) {
boolean ret = configLoader.initRules();
if (ret) {
rules.reloadRuleGroup();
}
}
}
} }

View File

@@ -81,7 +81,6 @@ public class Main extends JPanel {
ImageIcon originalIcon = new ImageIcon(imageURL); ImageIcon originalIcon = new ImageIcon(imageURL);
Image originalImage = originalIcon.getImage(); Image originalImage = originalIcon.getImage();
Image scaledImage = originalImage.getScaledInstance(30, 20, Image.SCALE_FAST); Image scaledImage = originalImage.getScaledInstance(30, 20, Image.SCALE_FAST);
ImageIcon scaledIcon = new ImageIcon(scaledImage); return new ImageIcon(scaledImage);
return scaledIcon;
} }
} }

View File

@@ -2,49 +2,35 @@ package hae.component.board;
import burp.api.montoya.MontoyaApi; import burp.api.montoya.MontoyaApi;
import hae.Config; import hae.Config;
import hae.component.board.message.MessageEntry;
import hae.component.board.message.MessageTableModel; import hae.component.board.message.MessageTableModel;
import hae.component.board.message.MessageTableModel.MessageTable; import hae.component.board.message.MessageTableModel.MessageTable;
import hae.component.board.table.Datatable; import hae.component.board.table.Datatable;
import hae.instances.http.utils.RegularMatcher;
import hae.utils.ConfigLoader; import hae.utils.ConfigLoader;
import hae.utils.UIEnhancer; import hae.utils.UIEnhancer;
import hae.utils.project.ProjectProcessor;
import hae.utils.project.model.HaeFileContent;
import hae.utils.string.StringProcessor; import hae.utils.string.StringProcessor;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter; import javax.swing.table.TableRowSorter;
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
import java.io.File;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class Databoard extends JPanel { public class Databoard extends JPanel {
private final MontoyaApi api; private final MontoyaApi api;
private final ConfigLoader configLoader; private final ConfigLoader configLoader;
private final ProjectProcessor projectProcessor;
private final MessageTableModel messageTableModel; private final MessageTableModel messageTableModel;
private JTextField hostTextField; private JTextField hostTextField;
private JTabbedPane dataTabbedPane; private JTabbedPane dataTabbedPane;
private JSplitPane splitPane; private JSplitPane splitPane;
private MessageTable messageTable; private MessageTable messageTable;
private JProgressBar progressBar;
private static Boolean isMatchHost = false; private static Boolean isMatchHost = false;
private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(); private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
@@ -52,15 +38,10 @@ public class Databoard extends JPanel {
private SwingWorker<Map<String, List<String>>, Void> handleComboBoxWorker; private SwingWorker<Map<String, List<String>>, Void> handleComboBoxWorker;
private SwingWorker<Void, Void> applyHostFilterWorker; private SwingWorker<Void, Void> applyHostFilterWorker;
private SwingWorker<List<Object[]>, Void> exportActionWorker;
private SwingWorker<List<Object[]>, Void> importActionWorker;
private final String defaultText = "Please enter the host";
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) { public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
this.api = api; this.api = api;
this.configLoader = configLoader; this.configLoader = configLoader;
this.projectProcessor = new ProjectProcessor(api);
this.messageTableModel = messageTableModel; this.messageTableModel = messageTableModel;
initComponents(); initComponents();
@@ -69,25 +50,22 @@ public class Databoard extends JPanel {
private void initComponents() { private void initComponents() {
setLayout(new GridBagLayout()); setLayout(new GridBagLayout());
((GridBagLayout) getLayout()).columnWidths = new int[]{25, 0, 0, 0, 20, 0}; ((GridBagLayout) getLayout()).columnWidths = new int[]{25, 0, 0, 0, 20, 0};
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0, 0}; ((GridBagLayout) getLayout()).rowHeights = new int[]{0, 65, 20, 0};
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, 0.0, 1.0E-4}; ((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, 0.0, 1.0E-4}; ((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 1.0, 0.0, 1.0E-4};
JLabel hostLabel = new JLabel("Host:"); JLabel hostLabel = new JLabel("Host:");
JButton clearButton = new JButton("Clear"); JButton clearButton = new JButton("Clear");
JButton exportButton = new JButton("Export");
JButton importButton = new JButton("Import");
JButton actionButton = new JButton("Action"); JButton actionButton = new JButton("Action");
JPanel menuPanel = new JPanel(new GridLayout(3, 1, 0, 5)); JPanel menuPanel = new JPanel(new GridLayout(1, 1, 0, 5));
menuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); menuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
JPopupMenu menu = new JPopupMenu(); JPopupMenu menu = new JPopupMenu();
menuPanel.add(clearButton); menuPanel.add(clearButton);
menuPanel.add(exportButton);
menuPanel.add(importButton);
menu.add(menuPanel); menu.add(menuPanel);
hostTextField = new JTextField(); hostTextField = new JTextField();
String defaultText = "Please enter the host";
UIEnhancer.setTextFieldPlaceholder(hostTextField, defaultText); UIEnhancer.setTextFieldPlaceholder(hostTextField, defaultText);
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
@@ -102,10 +80,7 @@ public class Databoard extends JPanel {
}); });
clearButton.addActionListener(this::clearActionPerformed); clearButton.addActionListener(this::clearActionPerformed);
exportButton.addActionListener(this::exportActionPerformed);
importButton.addActionListener(this::importActionPerformed);
progressBar = new JProgressBar();
splitPane.addComponentListener(new ComponentAdapter() { splitPane.addComponentListener(new ComponentAdapter() {
@Override @Override
@@ -115,7 +90,6 @@ public class Databoard extends JPanel {
}); });
splitPane.setVisible(false); splitPane.setVisible(false);
progressBar.setVisible(false);
add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
@@ -124,12 +98,9 @@ public class Databoard extends JPanel {
add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, add(actionButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
add(splitPane, new GridBagConstraints(1, 1, 3, 1, 0.0, 1.0, add(splitPane, new GridBagConstraints(1, 1, 3, 2, 0.0, 1.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 5, 0, 5), 0, 0)); 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); hostComboBox.setMaximumRowCount(5);
add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
@@ -149,24 +120,6 @@ public class Databoard extends JPanel {
columnModel.getColumn(5).setPreferredWidth((int) (totalWidth * 0.1)); columnModel.getColumn(5).setPreferredWidth((int) (totalWidth * 0.1));
} }
private void setProgressBar(boolean status) {
setProgressBar(status, progressBar, "Loading ...");
}
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 setAutoMatch() { private void setAutoMatch() {
hostComboBox.setSelectedItem(null); hostComboBox.setSelectedItem(null);
hostComboBox.addActionListener(this::handleComboBoxAction); hostComboBox.addActionListener(this::handleComboBoxAction);
@@ -202,8 +155,6 @@ public class Databoard extends JPanel {
String selectedHost = hostComboBox.getSelectedItem().toString(); String selectedHost = hostComboBox.getSelectedItem().toString();
if (getHostByList().contains(selectedHost)) { if (getHostByList().contains(selectedHost)) {
progressBar.setVisible(true);
setProgressBar(true);
hostTextField.setText(selectedHost); hostTextField.setText(selectedHost);
if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) { if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) {
@@ -357,11 +308,6 @@ public class Databoard extends JPanel {
return null; return null;
} }
@Override
protected void done() {
setProgressBar(false);
}
}; };
applyHostFilterWorker.execute(); applyHostFilterWorker.execute();
@@ -374,263 +320,6 @@ public class Databoard extends JPanel {
return new ArrayList<>(); return new ArrayList<>();
} }
private void exportActionPerformed(ActionEvent e) {
String selectedHost = hostTextField.getText().trim();
if (selectedHost.isEmpty()) {
return;
}
String exportDir = selectDirectory(true);
if (exportDir.isEmpty()) {
return;
}
if (exportActionWorker != null && !exportActionWorker.isDone()) {
exportActionWorker.cancel(true);
}
exportActionWorker = new SwingWorker<List<Object[]>, Void>() {
@Override
protected List<Object[]> doInBackground() {
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
return exportData(selectedHost, exportDir, dataMap);
}
@Override
protected void done() {
try {
List<Object[]> taskStatusList = get();
if (!taskStatusList.isEmpty()) {
JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(taskStatusList), "Info", JOptionPane.INFORMATION_MESSAGE);
}
} catch (Exception ignored) {
}
}
};
exportActionWorker.execute();
}
private JScrollPane generateTaskStatusPane(List<Object[]> dataList) {
String[] columnNames = {"#", "Filename", "Status"};
DefaultTableModel taskStatusTableModel = new DefaultTableModel(columnNames, 0);
JTable taskStatusTable = new JTable(taskStatusTableModel);
for (Object[] data : dataList) {
int rowCount = taskStatusTableModel.getRowCount();
int id = rowCount > 0 ? (Integer) taskStatusTableModel.getValueAt(rowCount - 1, 0) + 1 : 1;
Object[] rowData = new Object[data.length + 1];
rowData[0] = id;
System.arraycopy(data, 0, rowData, 1, data.length);
taskStatusTableModel.addRow(rowData);
}
TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<>(taskStatusTableModel);
taskStatusTable.setRowSorter(sorter);
JScrollPane scrollPane = new JScrollPane(taskStatusTable);
scrollPane.setBorder(new TitledBorder("Task status"));
scrollPane.setPreferredSize(new Dimension(500, 300));
int paneWidth = scrollPane.getPreferredSize().width;
taskStatusTable.getColumnModel().getColumn(0).setPreferredWidth((int) (paneWidth * 0.1));
taskStatusTable.getColumnModel().getColumn(1).setPreferredWidth((int) (paneWidth * 0.7));
taskStatusTable.getColumnModel().getColumn(2).setPreferredWidth((int) (paneWidth * 0.2));
return scrollPane;
}
private List<Object[]> exportData(String selectedHost, String exportDir, Map<String, Map<String, List<String>>> dataMap) {
return dataMap.entrySet().stream()
.filter(entry -> selectedHost.equals("*") || StringProcessor.matchesHostPattern(entry.getKey(), selectedHost))
.filter(entry -> !entry.getKey().contains("*"))
.map(entry -> exportEntry(entry, exportDir))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private Object[] exportEntry(Map.Entry<String, Map<String, List<String>>> entry, String exportDir) {
String key = entry.getKey();
Map<String, List<String>> ruleMap = entry.getValue();
if (ruleMap == null || ruleMap.isEmpty()) {
return null;
}
List<MessageEntry> messageEntryList = messageTableModel.getLogs();
Map<MessageEntry, String> entryUUIDMap = messageEntryList.stream()
.collect(Collectors.toMap(
messageEntry -> messageEntry,
messageEntry -> StringProcessor.getRandomUUID(),
(existing, replacement) -> existing
));
Map<String, Map<String, Object>> httpMap = processEntries(
messageEntryList,
key,
entryUUIDMap,
this::createHttpItemMap
);
Map<String, Map<String, Object>> urlMap = processEntries(
messageEntryList,
key,
entryUUIDMap,
this::creteUrlItemMap
);
String hostName = key.replace(":", "_");
String filename = String.format("%s/%s-%s.hae", exportDir, StringProcessor.getCurrentTime(), hostName);
boolean createdStatus = projectProcessor.createHaeFile(filename, key, ruleMap, urlMap, httpMap);
return new Object[]{filename, createdStatus};
}
private Map<String, Map<String, Object>> processEntries(List<MessageEntry> messageEntryList, String key, Map<MessageEntry, String> entryUUIDMap, Function<MessageEntry, Map<String, Object>> mapFunction) {
return messageEntryList.stream()
.filter(messageEntry -> !StringProcessor.getHostByUrl(messageEntry.getUrl()).isEmpty())
.filter(messageEntry -> StringProcessor.getHostByUrl(messageEntry.getUrl()).equals(key))
.collect(Collectors.toMap(
entryUUIDMap::get,
mapFunction,
(existing, replacement) -> existing
));
}
private Map<String, Object> creteUrlItemMap(MessageEntry entry) {
Map<String, Object> urlItemMap = new LinkedHashMap<>();
urlItemMap.put("url", entry.getUrl());
urlItemMap.put("method", entry.getMethod());
urlItemMap.put("status", entry.getStatus());
urlItemMap.put("length", entry.getLength());
urlItemMap.put("comment", entry.getComment());
urlItemMap.put("color", entry.getColor());
urlItemMap.put("size", String.valueOf(entry.getRequestResponse().request().toByteArray().length()));
return urlItemMap;
}
private Map<String, Object> createHttpItemMap(MessageEntry entry) {
Map<String, Object> httpItemMap = new LinkedHashMap<>();
httpItemMap.put("request", entry.getRequestResponse().request().toByteArray().getBytes());
httpItemMap.put("response", entry.getRequestResponse().response().toByteArray().getBytes());
return httpItemMap;
}
private void importActionPerformed(ActionEvent e) {
String exportDir = selectDirectory(false);
if (exportDir.isEmpty()) {
return;
}
if (importActionWorker != null && !importActionWorker.isDone()) {
importActionWorker.cancel(true);
}
importActionWorker = new SwingWorker<List<Object[]>, Void>() {
@Override
protected List<Object[]> doInBackground() {
List<String> filesWithExtension = findFilesWithExtension(new File(exportDir), ".hae");
return filesWithExtension.stream()
.map(Databoard.this::importData)
.collect(Collectors.toList());
}
@Override
protected void done() {
try {
List<Object[]> taskStatusList = get();
if (!taskStatusList.isEmpty()) {
JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(taskStatusList), "Info", JOptionPane.INFORMATION_MESSAGE);
}
} catch (Exception ignored) {
}
}
};
importActionWorker.execute();
}
private Object[] importData(String filename) {
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
HaeFileContent haeFileContent = projectProcessor.readHaeFile(filename);
boolean readStatus = haeFileContent != null;
List<Callable<Void>> tasks = new ArrayList<>();
if (readStatus) {
try {
String host = haeFileContent.getHost();
haeFileContent.getDataMap().forEach((key, value) -> RegularMatcher.putDataToGlobalMap(host, key, value));
haeFileContent.getUrlMap().forEach((key, urlItemMap) -> {
tasks.add(() -> {
String url = urlItemMap.get("url");
String comment = urlItemMap.get("comment");
String color = urlItemMap.get("color");
String length = urlItemMap.get("length");
String method = urlItemMap.get("method");
String status = urlItemMap.get("status");
String path = haeFileContent.getHttpPath();
messageTableModel.add(null, url, method, status, length, comment, color, key, path);
return null;
});
});
executor.invokeAll(tasks);
} catch (Exception e) {
api.logging().logToError("importData: " + e.getMessage());
} finally {
executor.shutdown();
}
}
return new Object[]{filename, readStatus};
}
private List<String> findFilesWithExtension(File directory, String extension) {
List<String> filePaths = new ArrayList<>();
if (directory.isDirectory()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
filePaths.addAll(findFilesWithExtension(file, extension));
} else if (file.isFile() && file.getName().toLowerCase().endsWith(extension)) {
filePaths.add(file.getAbsolutePath());
}
}
}
} else {
filePaths.add(directory.getAbsolutePath());
}
return filePaths;
}
private String selectDirectory(boolean forDirectories) {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new java.io.File(configLoader.getRulesFilePath()));
chooser.setDialogTitle(String.format("Select a Directory%s", forDirectories ? "" : " or File"));
FileNameExtensionFilter filter = new FileNameExtensionFilter(".hae Files", "hae");
chooser.addChoosableFileFilter(filter);
chooser.setFileFilter(filter);
chooser.setFileSelectionMode(forDirectories ? JFileChooser.DIRECTORIES_ONLY : JFileChooser.FILES_AND_DIRECTORIES);
chooser.setAcceptAllFileFilterUsed(!forDirectories);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File selectedDirectory = chooser.getSelectedFile();
return selectedDirectory.getAbsolutePath();
}
return "";
}
private void clearActionPerformed(ActionEvent e) { private void clearActionPerformed(ActionEvent e) {
int retCode = JOptionPane.showConfirmDialog(this, "Do you want to clear data?", "Info", int retCode = JOptionPane.showConfirmDialog(this, "Do you want to clear data?", "Info",
JOptionPane.YES_NO_OPTION); JOptionPane.YES_NO_OPTION);
@@ -638,7 +327,6 @@ public class Databoard extends JPanel {
if (retCode == JOptionPane.YES_OPTION && !host.isEmpty()) { if (retCode == JOptionPane.YES_OPTION && !host.isEmpty()) {
dataTabbedPane.removeAll(); dataTabbedPane.removeAll();
splitPane.setVisible(false); splitPane.setVisible(false);
progressBar.setVisible(false);
Config.globalDataMap.keySet().parallelStream().forEach(key -> { Config.globalDataMap.keySet().parallelStream().forEach(key -> {
if (StringProcessor.matchesHostPattern(key, host) || host.equals("*")) { if (StringProcessor.matchesHostPattern(key, host) || host.equals("*")) {

View File

@@ -11,10 +11,8 @@ public class MessageEntry {
private final String status; private final String status;
private final String color; private final String color;
private final String method; private final String method;
private final String hash;
private final String path;
MessageEntry(HttpRequestResponse requestResponse, String method, String url, String comment, String length, String color, String status, String hash, String path) { MessageEntry(HttpRequestResponse requestResponse, String method, String url, String comment, String length, String color, String status) {
this.requestResponse = requestResponse; this.requestResponse = requestResponse;
this.method = method; this.method = method;
this.url = url; this.url = url;
@@ -22,8 +20,6 @@ public class MessageEntry {
this.length = length; this.length = length;
this.color = color; this.color = color;
this.status = status; this.status = status;
this.hash = hash;
this.path = path;
} }
public String getColor() { public String getColor() {
@@ -53,12 +49,4 @@ public class MessageEntry {
public HttpRequestResponse getRequestResponse() { public HttpRequestResponse getRequestResponse() {
return this.requestResponse; return this.requestResponse;
} }
public String getHash() {
return this.hash;
}
public String getPath() {
return this.path;
}
} }

View File

@@ -5,13 +5,14 @@ import burp.api.montoya.http.message.HttpHeader;
import burp.api.montoya.http.message.HttpRequestResponse; import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest; import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse; import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.persistence.PersistedObject;
import burp.api.montoya.ui.UserInterface; import burp.api.montoya.ui.UserInterface;
import burp.api.montoya.ui.editor.HttpRequestEditor; import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.HttpResponseEditor; import burp.api.montoya.ui.editor.HttpResponseEditor;
import hae.Config; import hae.Config;
import hae.cache.CachePool; import hae.cache.CachePool;
import hae.utils.ConfigLoader; import hae.utils.ConfigLoader;
import hae.utils.project.FileProcessor; import hae.utils.DataManager;
import hae.utils.string.HashCalculator; import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor; import hae.utils.string.StringProcessor;
@@ -97,10 +98,10 @@ public class MessageTableModel extends AbstractTableModel {
splitPane.setRightComponent(messageTab); splitPane.setRightComponent(messageTab);
} }
public void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, String hash, String path) { public void add(HttpRequestResponse messageInfo, String url, String method, String status, String length, String comment, String color, boolean flag) {
synchronized (log) { synchronized (log) {
boolean isDuplicate = false; boolean isDuplicate = false;
MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status, hash, path); MessageEntry logEntry = new MessageEntry(messageInfo, method, url, comment, length, color, status);
byte[] reqByteA = new byte[0]; byte[] reqByteA = new byte[0];
byte[] resByteA = new byte[0]; byte[] resByteA = new byte[0];
@@ -134,6 +135,18 @@ public class MessageTableModel extends AbstractTableModel {
} }
if (!isDuplicate) { if (!isDuplicate) {
if (flag) {
DataManager dataManager = new DataManager(api);
// 数据存储在BurpSuite空间内
PersistedObject persistedObject = PersistedObject.persistedObject();
persistedObject.setHttpRequestResponse("messageInfo", messageInfo);
persistedObject.setString("comment", comment);
persistedObject.setString("color", color);
String uuidIndex = StringProcessor.getRandomUUID();
dataManager.putData("message", uuidIndex, persistedObject);
}
// 添加进日志
log.add(logEntry); log.add(logEntry);
} }
} }
@@ -177,11 +190,10 @@ public class MessageTableModel extends AbstractTableModel {
filteredLog.clear(); filteredLog.clear();
log.forEach(entry -> { log.forEach(entry -> {
MessageEntry finalEntry = getEntryByFile(entry); String host = StringProcessor.getHostByUrl(entry.getUrl());
String host = StringProcessor.getHostByUrl(finalEntry.getUrl());
if (!host.isEmpty()) { if (!host.isEmpty()) {
if (StringProcessor.matchesHostPattern(host, filterText) || filterText.contains("*")) { if (StringProcessor.matchesHostPattern(host, filterText) || filterText.contains("*")) {
filteredLog.add(finalEntry); filteredLog.add(entry);
} }
} }
}); });
@@ -189,34 +201,6 @@ public class MessageTableModel extends AbstractTableModel {
fireTableDataChanged(); fireTableDataChanged();
} }
private MessageEntry getEntryByFile(MessageEntry entry) {
HttpRequestResponse requestResponse = entry.getRequestResponse();
if (requestResponse == null) {
String url = entry.getUrl();
String method = entry.getMethod();
String status = entry.getStatus();
String comment = entry.getComment();
String color = entry.getColor();
String path = entry.getPath();
String hash = entry.getHash();
int length = Integer.parseInt(entry.getLength());
byte[] contents = FileProcessor.readFileContent(path, hash);
if (contents.length > length) {
byte[] response = Arrays.copyOf(contents, length);
byte[] request = Arrays.copyOfRange(contents, length, contents.length);
requestResponse = StringProcessor.createHttpRequestResponse(url, request, response);
int index = log.indexOf(entry);
entry = new MessageEntry(requestResponse, method, url, comment, String.valueOf(length), color, status, "", "");
log.set(index, entry);
}
}
return entry;
}
public void applyMessageFilter(String tableName, String filterText) { public void applyMessageFilter(String tableName, String filterText) {
filteredLog.clear(); filteredLog.clear();
for (MessageEntry entry : log) { for (MessageEntry entry : log) {

View File

@@ -1,157 +0,0 @@
package hae.component.board.table;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.RequestOptions;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import hae.Config;
import hae.utils.ConfigLoader;
import hae.utils.http.HttpUtils;
import okhttp3.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AIPower {
private final MontoyaApi api;
private final HttpUtils httpUtils;
private final ConfigLoader configLoader;
private final String apiAuth;
private final String aiModel;
private final String aiBaseUrl;
public AIPower(MontoyaApi api, ConfigLoader configLoader, String aiModel, String aiBaseUrl, String[] apiKey) {
this.api = api;
this.configLoader = configLoader;
this.httpUtils = new HttpUtils(api, configLoader);
this.aiModel = aiModel;
this.aiBaseUrl = aiBaseUrl;
this.apiAuth = String.format("Bearer %s", apiKey[new Random().nextInt(apiKey.length)]);
}
// Stream Response
public String chatWithAPI(String ruleName, String data) {
OkHttpClient httpClient = new OkHttpClient();
String fileId = uploadFileToAIService(ruleName, data);
Gson gson = new Gson();
if (fileId != null) {
String chatUrl = String.format("%s/chat/completions", aiBaseUrl);
String chatMessage = generateJsonData(configLoader.getAIPrompt(), fileId);
Request request = new Request.Builder()
.url(chatUrl)
.header("Authorization", apiAuth)
.post(RequestBody.create(MediaType.parse("application/json"), chatMessage))
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
StringBuilder chatReturn = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("data: ") && !line.contains("[DONE]")) {
String jsonData = line.substring(6);
Type type = new TypeToken<Map<String, Object>>() {
}.getType();
Map<String, Object> map = gson.fromJson(jsonData, type);
String content = getDeltaContent(map);
if (content != null) {
chatReturn.append(content);
}
}
}
deleteFileOnAIService(fileId);
return chatReturn.toString();
} catch (Exception e) {
return "";
}
}
return "";
}
private String getDeltaContent(Map<String, Object> map) {
List<Map<String, Map<String, String>>> choices = (List<Map<String, Map<String, String>>>) map.get("choices");
if (choices != null && !choices.isEmpty()) {
Map<String, String> delta = choices.get(0).get("delta");
return delta.get("content");
}
return null;
}
private String uploadFileToAIService(String ruleName, String data) {
String uploadUrl = String.format("%s/files", aiBaseUrl);
String uploadParam = "file";
String filename = "hae.txt";
String content = String.format(Config.userTextFormat, ruleName, data);
HttpRequest uploadFileRequest = httpUtils.generateRequestByMultipartUploadMethod(uploadUrl, uploadParam, filename, content).withAddedHeader("Authorization", apiAuth);
HttpRequestResponse uploadFileRequestResponse = api.http().sendRequest(uploadFileRequest, RequestOptions.requestOptions().withUpstreamTLSVerification());
String responseBody = uploadFileRequestResponse.response().bodyToString();
Pattern pattern = Pattern.compile("\"id\":\"(.*?)\",");
Matcher matcher = pattern.matcher(responseBody);
return matcher.find() ? matcher.group(1) : null;
}
private void deleteFileOnAIService(String fileId) {
String deleteFileUrl = String.format("%s/files/%s", aiBaseUrl, fileId);
HttpRequest deleteFileRequest = httpUtils.generateRequestByDeleteMethod(deleteFileUrl).withAddedHeader("Authorization", apiAuth);
api.http().sendRequest(deleteFileRequest, RequestOptions.requestOptions().withUpstreamTLSVerification());
}
private String getFileContentOnAiService(String fileId) {
String getFileContentUrl = String.format("%s/files/%s/content", aiBaseUrl, fileId);
HttpRequest getFileContentRequest = HttpRequest.httpRequestFromUrl(getFileContentUrl).withAddedHeader("Authorization", apiAuth);
HttpRequestResponse getFileRequestResponse = api.http().sendRequest(getFileContentRequest, RequestOptions.requestOptions().withUpstreamTLSVerification());
String responseBody = getFileRequestResponse.response().bodyToString();
Pattern pattern = Pattern.compile("\"content\":\"(.*?)\",\"file_type\"");
Matcher matcher = pattern.matcher(responseBody);
return matcher.find() ? matcher.group(1) : null;
}
private String generateJsonData(String prompt, String fileId) {
Map<String, Object> data = new HashMap<>();
data.put("model", aiModel);
data.put("stream", true);
data.put("messages", new Object[]{
new HashMap<String, Object>() {{
put("role", "system");
put("content", prompt);
}},
new HashMap<String, Object>() {{
put("role", "system");
put("content", aiModel.equals("qwen-long") ? String.format("fileid://%s", fileId) : getFileContentOnAiService(fileId));
}},
new HashMap<String, Object>() {{
put("role", "user");
put("content", "Start");
}}
});
Gson gson = new GsonBuilder().setPrettyPrinting().create();
return gson.toJson(data);
}
}

View File

@@ -1,9 +1,6 @@
package hae.component.board.table; package hae.component.board.table;
import burp.api.montoya.MontoyaApi; import burp.api.montoya.MontoyaApi;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import hae.component.board.Databoard;
import hae.component.board.message.MessageTableModel; import hae.component.board.message.MessageTableModel;
import hae.utils.ConfigLoader; import hae.utils.ConfigLoader;
import hae.utils.UIEnhancer; import hae.utils.UIEnhancer;
@@ -11,22 +8,17 @@ import hae.utils.UIEnhancer;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter; import javax.swing.table.TableRowSorter;
import java.awt.*; import java.awt.*;
import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class Datatable extends JPanel { public class Datatable extends JPanel {
@@ -39,15 +31,12 @@ public class Datatable extends JPanel {
private final TableRowSorter<DefaultTableModel> sorter; private final TableRowSorter<DefaultTableModel> sorter;
private final JCheckBox searchMode = new JCheckBox("Reverse search"); private final JCheckBox searchMode = new JCheckBox("Reverse search");
private final String tabName; private final String tabName;
private final JProgressBar progressBar;
private final JPopupMenu aiEmpoweredMenu;
private final JPanel footerPanel; private final JPanel footerPanel;
public Datatable(MontoyaApi api, ConfigLoader configLoader, String tabName, List<String> dataList) { public Datatable(MontoyaApi api, ConfigLoader configLoader, String tabName, List<String> dataList) {
this.api = api; this.api = api;
this.configLoader = configLoader; this.configLoader = configLoader;
this.tabName = tabName; this.tabName = tabName;
this.progressBar = new JProgressBar();
String[] columnNames = {"#", "Information"}; String[] columnNames = {"#", "Information"};
this.dataTableModel = new DefaultTableModel(columnNames, 0); this.dataTableModel = new DefaultTableModel(columnNames, 0);
@@ -56,15 +45,12 @@ public class Datatable extends JPanel {
this.sorter = new TableRowSorter<>(dataTableModel); this.sorter = new TableRowSorter<>(dataTableModel);
this.searchField = new JTextField(10); this.searchField = new JTextField(10);
this.secondSearchField = new JTextField(10); this.secondSearchField = new JTextField(10);
this.aiEmpoweredMenu = new JPopupMenu();
this.footerPanel = new JPanel(new BorderLayout(0, 5)); this.footerPanel = new JPanel(new BorderLayout(0, 5));
initComponents(dataList); initComponents(dataList);
} }
private void initComponents(List<String> dataList) { private void initComponents(List<String> dataList) {
progressBar.setVisible(false);
// 设置ID排序 // 设置ID排序
sorter.setComparator(0, new Comparator<Integer>() { sorter.setComparator(0, new Comparator<Integer>() {
@Override @Override
@@ -142,57 +128,17 @@ public class Datatable extends JPanel {
JButton settingsButton = new JButton("Settings"); JButton settingsButton = new JButton("Settings");
setMenuShow(settingMenu, settingsButton); setMenuShow(settingMenu, settingsButton);
// AI Empowered按钮
JPanel aiEmpoweredPanel = new JPanel(new GridLayout(2, 1));
aiEmpoweredPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
JButton empoweredByAlibabaButton = new JButton("Alibaba - QwenLong");
empoweredByAlibabaButton.addActionListener(e -> {
aiEmpoweredByAlibabaActionPerformed(e, tabName, getTableData(dataTable));
});
JButton empoweredByMoonshotButton = new JButton("Moonshot - Kimi");
empoweredByMoonshotButton.addActionListener(e -> {
aiEmpoweredByMoonshotActionPerformed(e, tabName, getTableData(dataTable));
});
aiEmpoweredPanel.add(empoweredByAlibabaButton);
aiEmpoweredPanel.add(empoweredByMoonshotButton);
aiEmpoweredMenu.add(aiEmpoweredPanel);
JButton aiEmpoweredButton = new JButton("AI Empowered");
setMenuShow(aiEmpoweredMenu, aiEmpoweredButton);
aiEmpoweredMenu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
empoweredByAlibabaButton.setEnabled(!configLoader.getAlibabaAIAPIKey().isEmpty());
empoweredByMoonshotButton.setEnabled(!configLoader.getMoonshotAIAPIKey().isEmpty());
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
optionsPanel.add(settingsButton); optionsPanel.add(settingsButton);
optionsPanel.add(Box.createHorizontalStrut(5)); optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(searchField); optionsPanel.add(searchField);
optionsPanel.add(Box.createHorizontalStrut(5)); optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(secondSearchField); optionsPanel.add(secondSearchField);
optionsPanel.add(Box.createHorizontalStrut(5));
optionsPanel.add(aiEmpoweredButton);
footerPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3)); footerPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3));
footerPanel.add(optionsPanel, BorderLayout.CENTER); footerPanel.add(optionsPanel, BorderLayout.CENTER);
footerPanel.add(progressBar, BorderLayout.SOUTH);
add(scrollPane, BorderLayout.CENTER); add(scrollPane, BorderLayout.CENTER);
add(footerPanel, BorderLayout.SOUTH); add(footerPanel, BorderLayout.SOUTH);
setProgressBar(false);
} }
private void setMenuShow(JPopupMenu menu, JButton button) { private void setMenuShow(JPopupMenu menu, JButton button) {
@@ -205,9 +151,6 @@ public class Datatable extends JPanel {
}); });
} }
private void setProgressBar(boolean status) {
Databoard.setProgressBar(status, progressBar, "AI+ ...");
}
private void addRowToTable(Object[] data) { private void addRowToTable(Object[] data) {
int rowCount = dataTableModel.getRowCount(); int rowCount = dataTableModel.getRowCount();
@@ -218,60 +161,6 @@ public class Datatable extends JPanel {
dataTableModel.addRow(rowData); dataTableModel.addRow(rowData);
} }
private void aiEmpoweredByAlibabaActionPerformed(ActionEvent e, String ruleName, String data) {
AIPower aiPower = new AIPower(api, configLoader, "qwen-long", "https://dashscope.aliyuncs.com/compatible-mode/v1", configLoader.getAlibabaAIAPIKey().split("\\|"));
aiEmpoweredButtonAction(ruleName, data, aiPower);
}
private void aiEmpoweredByMoonshotActionPerformed(ActionEvent e, String ruleName, String data) {
AIPower aiPower = new AIPower(api, configLoader, "moonshot-v1-128k", "https://api.moonshot.cn/v1", configLoader.getMoonshotAIAPIKey().split("\\|"));
aiEmpoweredButtonAction(ruleName, data, aiPower);
}
private void aiEmpoweredButtonAction(String ruleName, String data, AIPower aiPower) {
progressBar.setVisible(true);
aiEmpoweredMenu.setVisible(true);
setProgressBar(true);
SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
@Override
protected String doInBackground() throws Exception {
return aiPower.chatWithAPI(ruleName, data);
}
@Override
protected void done() {
setProgressBar(false);
try {
String chatReturn = get();
if (!chatReturn.isEmpty()) {
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>() {
}.getType();
Map<String, List<String>> map = gson.fromJson(chatReturn, type);
dataTableModel.setRowCount(0);
for (String item : map.get("data")) {
if (!item.isEmpty()) {
addRowToTable(new Object[]{item});
}
}
JOptionPane.showMessageDialog(Datatable.this, "AI+ has completed the AI empowered work.", "AI+ Info", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(Datatable.this, "AI+ returns null, please check!", "AI+ Info", JOptionPane.WARNING_MESSAGE);
}
} catch (Exception ignored) {
JOptionPane.showMessageDialog(Datatable.this, "AI+ returns error, please check!", "AI+ Info", JOptionPane.ERROR_MESSAGE);
}
}
};
worker.execute();
aiEmpoweredMenu.setVisible(false);
}
private void performSearch() { private void performSearch() {
RowFilter<Object, Object> firstRowFilter = applyFirstSearchFilter(); RowFilter<Object, Object> firstRowFilter = applyFirstSearchFilter();
RowFilter<Object, Object> secondRowFilter = applySecondFilter(); RowFilter<Object, Object> secondRowFilter = applySecondFilter();

View File

@@ -84,7 +84,7 @@ public class HttpMessageActiveHandler implements HttpHandler {
new SwingWorker<Void, Void>() { new SwingWorker<Void, Void>() {
@Override @Override
protected Void doInBackground() { protected Void doInBackground() {
messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, "", ""); messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, true);
return null; return null;
} }
}.execute(); }.execute();

View File

@@ -71,7 +71,7 @@ public class HttpMessagePassiveHandler implements ScanCheck {
new SwingWorker<Void, Void>() { new SwingWorker<Void, Void>() {
@Override @Override
protected Void doInBackground() { protected Void doInBackground() {
messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, "", ""); messageTableModel.add(httpRequestResponse, url, method, status, length, comment, color, true);
return null; return null;
} }
}.execute(); }.execute();

View File

@@ -1,12 +1,15 @@
package hae.instances.http.utils; package hae.instances.http.utils;
import burp.api.montoya.MontoyaApi; import burp.api.montoya.MontoyaApi;
import burp.api.montoya.persistence.PersistedList;
import burp.api.montoya.persistence.PersistedObject;
import dk.brics.automaton.Automaton; import dk.brics.automaton.Automaton;
import dk.brics.automaton.AutomatonMatcher; import dk.brics.automaton.AutomatonMatcher;
import dk.brics.automaton.RegExp; import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton; import dk.brics.automaton.RunAutomaton;
import hae.Config; import hae.Config;
import hae.cache.CachePool; import hae.cache.CachePool;
import hae.utils.DataManager;
import hae.utils.string.HashCalculator; import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor; import hae.utils.string.StringProcessor;
@@ -38,7 +41,7 @@ public class RegularMatcher {
// 多线程执行,一定程度上减少阻塞现象 // 多线程执行,一定程度上减少阻塞现象
String matchContent = ""; String matchContent = "";
// 遍历获取规则 // 遍历获取规则
List<String> result = new ArrayList<>(); List<String> result;
Map<String, Object> tmpMap = new HashMap<>(); Map<String, Object> tmpMap = new HashMap<>();
boolean loaded = (Boolean) objects[0]; boolean loaded = (Boolean) objects[0];
@@ -78,7 +81,7 @@ public class RegularMatcher {
} }
try { try {
result.addAll(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive)); result = new ArrayList<>(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive));
} catch (Exception e) { } catch (Exception e) {
api.logging().logToError(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex)); api.logging().logToError(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex));
api.logging().logToError(e.getMessage()); api.logging().logToError(e.getMessage());
@@ -98,7 +101,7 @@ public class RegularMatcher {
String nameAndSize = String.format("%s (%s)", name, result.size()); String nameAndSize = String.format("%s (%s)", name, result.size());
finalMap.put(nameAndSize, tmpMap); finalMap.put(nameAndSize, tmpMap);
putDataToGlobalMap(host, name, result); putDataToGlobalMap(api, host, name, result, true);
} }
} }
} }
@@ -108,7 +111,7 @@ public class RegularMatcher {
} }
} }
public static void putDataToGlobalMap(String host, String name, List<String> dataList) { public static void putDataToGlobalMap(MontoyaApi api, String host, String name, List<String> dataList, boolean flag) {
// 添加到全局变量中便于Databoard检索 // 添加到全局变量中便于Databoard检索
if (!Objects.equals(host, "") && host != null) { if (!Objects.equals(host, "") && host != null) {
Config.globalDataMap.compute(host, (existingHost, existingMap) -> { Config.globalDataMap.compute(host, (existingHost, existingMap) -> {
@@ -120,6 +123,18 @@ public class RegularMatcher {
return new ArrayList<>(combinedSet); return new ArrayList<>(combinedSet);
}); });
if (flag) {
// 数据存储在BurpSuite空间内
DataManager dataManager = new DataManager(api);
PersistedObject persistedObject = PersistedObject.persistedObject();
gRuleMap.forEach((kName, vList) -> {
PersistedList<String> persistedList = PersistedList.persistedStringList();
persistedList.addAll(vList);
persistedObject.setStringList(kName, persistedList);
});
dataManager.putData("data", host, persistedObject);
}
return gRuleMap; return gRuleMap;
}); });
@@ -128,7 +143,7 @@ public class RegularMatcher {
String anyHost = (splitHost.length > 2 && !StringProcessor.matchHostIsIp(onlyHost)) ? StringProcessor.replaceFirstOccurrence(onlyHost, splitHost[0], "*") : ""; String anyHost = (splitHost.length > 2 && !StringProcessor.matchHostIsIp(onlyHost)) ? StringProcessor.replaceFirstOccurrence(onlyHost, splitHost[0], "*") : "";
if (!Config.globalDataMap.containsKey(anyHost) && anyHost.length() > 0) { if (!Config.globalDataMap.containsKey(anyHost) && !anyHost.isEmpty()) {
// 添加通配符Host实际数据从查询哪里将所有数据提取 // 添加通配符Host实际数据从查询哪里将所有数据提取
Config.globalDataMap.put(anyHost, new HashMap<>()); Config.globalDataMap.put(anyHost, new HashMap<>());
} }

View File

@@ -22,7 +22,7 @@ public class WebSocketMessageHandler implements ProxyMessageHandler {
String message = interceptedTextMessage.payload(); String message = interceptedTextMessage.payload();
List<Map<String, String>> result = messageProcessor.processMessage("", message, true); List<Map<String, String>> result = messageProcessor.processMessage("", message, true);
if (result != null && !result.isEmpty() && result.size() > 0) { if (result != null && !result.isEmpty()) {
interceptedTextMessage.annotations().setHighlightColor(HighlightColor.highlightColor(result.get(0).get("color"))); interceptedTextMessage.annotations().setHighlightColor(HighlightColor.highlightColor(result.get(0).get("color")));
interceptedTextMessage.annotations().setNotes(result.get(1).get("comment")); interceptedTextMessage.annotations().setNotes(result.get(1).get("comment"));
} }

View File

@@ -1,9 +1,6 @@
package hae.utils; package hae.utils;
import burp.api.montoya.MontoyaApi; import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.RequestOptions;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import hae.Config; import hae.Config;
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
@@ -46,7 +43,7 @@ public class ConfigLoader {
File rulesFilePath = new File(this.rulesFilePath); File rulesFilePath = new File(this.rulesFilePath);
if (!(rulesFilePath.exists() && rulesFilePath.isFile())) { if (!(rulesFilePath.exists() && rulesFilePath.isFile())) {
initRulesByRes(); initRules();
} }
Config.globalRules = getRules(); Config.globalRules = getRules();
@@ -137,18 +134,6 @@ public class ConfigLoader {
return rules; return rules;
} }
public String getAlibabaAIAPIKey() {
return getValueFromConfig("AlibabaAIAPIKey", "");
}
public String getMoonshotAIAPIKey() {
return getValueFromConfig("MoonshotAIAPIKey", "");
}
public String getAIPrompt() {
return getValueFromConfig("AIPrompt", Config.prompt);
}
public String getBlockHost() { public String getBlockHost() {
return getValueFromConfig("BlockHost", Config.host); return getValueFromConfig("BlockHost", Config.host);
} }
@@ -191,18 +176,6 @@ public class ConfigLoader {
return defaultValue; return defaultValue;
} }
public void setAlibabaAIAPIKey(String apiKey) {
setValueToConfig("AlibabaAIAPIKey", apiKey);
}
public void setMoonshotAIAPIKey(String apiKey) {
setValueToConfig("MoonshotAIAPIKey", apiKey);
}
public void setAIPrompt(String prompt) {
setValueToConfig("AIPrompt", prompt);
}
public void setExcludeSuffix(String excludeSuffix) { public void setExcludeSuffix(String excludeSuffix) {
setValueToConfig("ExcludeSuffix", excludeSuffix); setValueToConfig("ExcludeSuffix", excludeSuffix);
} }
@@ -250,11 +223,12 @@ public class ConfigLoader {
} }
} }
public void initRulesByRes() { public boolean initRules() {
boolean isCopySuccess = copyRulesToFile(this.rulesFilePath); boolean ret = copyRulesToFile(this.rulesFilePath);
if (!isCopySuccess) { if (!ret) {
api.extension().unload(); api.extension().unload();
} }
return ret;
} }
private boolean copyRulesToFile(String targetFilePath) { private boolean copyRulesToFile(String targetFilePath) {
@@ -277,33 +251,4 @@ public class ConfigLoader {
return false; return false;
} }
public void initRulesByNet() {
Thread t = new Thread() {
public void run() {
pullRules();
}
};
t.start();
try {
t.join(10000);
} catch (Exception ignored) {
}
}
private void pullRules() {
try {
String url = "https://raw.githubusercontent.com/gh0stkey/HaE/gh-pages/Rules.yml";
HttpRequest httpRequest = HttpRequest.httpRequestFromUrl(url);
HttpRequestResponse requestResponse = api.http().sendRequest(httpRequest, RequestOptions.requestOptions().withUpstreamTLSVerification());
String responseBody = requestResponse.response().bodyToString();
if (responseBody.contains("rules")) {
FileOutputStream fileOutputStream = new FileOutputStream(rulesFilePath);
fileOutputStream.write(responseBody.getBytes());
fileOutputStream.close();
}
} catch (Exception ignored) {
api.extension().unload();
}
}
} }

View File

@@ -0,0 +1,85 @@
package hae.utils;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.persistence.PersistedList;
import burp.api.montoya.persistence.PersistedObject;
import burp.api.montoya.persistence.Persistence;
import hae.component.board.message.MessageTableModel;
import hae.instances.http.utils.RegularMatcher;
public class DataManager {
private final MontoyaApi api;
private final Persistence persistence;
public DataManager(MontoyaApi api) {
this.api = api;
this.persistence = api.persistence();
}
private void saveIndex(String indexName, String indexValue) {
PersistedList<String> indexList = persistence.extensionData().getStringList(indexName);
if (indexList != null && !indexList.isEmpty()) {
persistence.extensionData().deleteStringList(indexName);
} else {
indexList = PersistedList.persistedStringList();
}
if (!indexList.contains(indexValue)) {
indexList.add(indexValue);
}
persistence.extensionData().setStringList(indexName, indexList);
}
public void putData(String dataType, String dataName, PersistedObject persistedObject) {
if (persistence.extensionData().getChildObject(dataName) != null) {
persistence.extensionData().deleteChildObject(dataName);
}
persistence.extensionData().setChildObject(dataName, persistedObject);
saveIndex(dataType, dataName);
}
public void loadData(MessageTableModel messageTableModel) {
// 1. 获取索引
PersistedList<String> dataIndex = persistence.extensionData().getStringList("data"); // 数据索引
PersistedList<String> messageIndex = persistence.extensionData().getStringList("message"); // 消息索引
// 2. 从索引获取数据
loadHaEData(dataIndex);
loadMessageData(messageIndex, messageTableModel);
}
private void loadHaEData(PersistedList<String> dataIndex) {
if (dataIndex != null && !dataIndex.isEmpty()) {
dataIndex.parallelStream().forEach(index -> {
PersistedObject dataObj = persistence.extensionData().getChildObject(index);
dataObj.stringListKeys().forEach(dataKey -> {
RegularMatcher.putDataToGlobalMap(api, index, dataKey, dataObj.getStringList(dataKey).stream().toList(), false);
});
});
}
}
private void loadMessageData(PersistedList<String> messageIndex, MessageTableModel messageTableModel) {
if (messageIndex != null && !messageIndex.isEmpty()) {
messageIndex.parallelStream().forEach(index -> {
PersistedObject dataObj = persistence.extensionData().getChildObject(index);
HttpRequestResponse messageInfo = dataObj.getHttpRequestResponse("messageInfo");
String comment = dataObj.getString("comment");
String color = dataObj.getString("color");
HttpRequest request = messageInfo.request();
HttpResponse response = messageInfo.response();
String method = request.method();
String url = request.url();
String status = String.valueOf(response.statusCode());
String length = String.valueOf(response.toByteArray().length());
messageTableModel.add(messageInfo, url, method, status, length, comment, color, false);
});
}
}
}

View File

@@ -41,6 +41,4 @@ public class UIEnhancer {
textField.setText(placeholderText); textField.setText(placeholderText);
textField.putClientProperty("isPlaceholder", true); textField.putClientProperty("isPlaceholder", true);
} }
} }

View File

@@ -3,9 +3,7 @@ package hae.utils.http;
import burp.api.montoya.MontoyaApi; import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.message.HttpRequestResponse; import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest; import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.http.message.requests.HttpTransformation;
import burp.api.montoya.http.message.responses.HttpResponse; import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.utilities.RandomUtils;
import hae.utils.ConfigLoader; import hae.utils.ConfigLoader;
import hae.utils.string.StringProcessor; import hae.utils.string.StringProcessor;
@@ -21,25 +19,6 @@ public class HttpUtils {
this.configLoader = configLoader; this.configLoader = configLoader;
} }
public HttpRequest generateRequestByMultipartUploadMethod(String url, String name, String filename, String content) {
HttpRequest baseRequest = HttpRequest.httpRequestFromUrl(url).withTransformationApplied(HttpTransformation.TOGGLE_METHOD);
String boundary = api.utilities().randomUtils().randomString(32, RandomUtils.CharacterSet.ASCII_LETTERS);
String newBody = String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n\r\n%s\r\n", boundary, name, filename, content) +
String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", boundary, "purpose", "file-extract") +
"--" + boundary + "--\r\n";
baseRequest = baseRequest.withUpdatedHeader("Content-Type", "multipart/form-data; boundary=" + boundary).withBody(newBody);
return baseRequest;
}
public HttpRequest generateRequestByDeleteMethod(String url) {
return HttpRequest.httpRequestFromUrl(url).withMethod("DELETE");
}
public boolean verifyHttpRequestResponse(HttpRequestResponse requestResponse, String toolType) { public boolean verifyHttpRequestResponse(HttpRequestResponse requestResponse, String toolType) {
HttpRequest request = requestResponse.request(); HttpRequest request = requestResponse.request();
HttpResponse response = requestResponse.response(); HttpResponse response = requestResponse.response();

View File

@@ -1,47 +0,0 @@
package hae.utils.project;
import java.io.File;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
public class FileProcessor {
public static void deleteDirectoryWithContents(Path pathToBeDeleted) {
if (pathToBeDeleted != null) {
try {
Files.walk(pathToBeDeleted)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (Exception ignored) {
}
}
}
public static byte[] readFileContent(String basePath, String fileName) {
Path filePath = Paths.get(basePath, fileName);
Path path = Paths.get(basePath);
try {
byte[] fileContent = Files.readAllBytes(filePath);
Files.deleteIfExists(filePath);
boolean isEmpty = isDirectoryEmpty(path);
if (isEmpty) {
Files.deleteIfExists(path);
}
return fileContent;
} catch (Exception e) {
return new byte[0];
}
}
private static boolean isDirectoryEmpty(Path directory) throws Exception {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(directory)) {
return !dirStream.iterator().hasNext();
}
}
}

View File

@@ -1,187 +0,0 @@
package hae.utils.project;
import burp.api.montoya.MontoyaApi;
import hae.utils.project.model.HaeFileContent;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ProjectProcessor {
private final MontoyaApi api;
public ProjectProcessor(MontoyaApi api) {
this.api = api;
}
public boolean createHaeFile(String haeFilePath, String host, Map<String, List<String>> dataMap, Map<String, Map<String, Object>> urlMap, Map<String, Map<String, Object>> httpMap) {
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
List<Callable<Void>> tasks = new ArrayList<>();
ByteArrayOutputStream dataYamlStream = new ByteArrayOutputStream();
ByteArrayOutputStream urlYamlStream = new ByteArrayOutputStream();
Yaml yaml = new Yaml();
yaml.dump(dataMap, new OutputStreamWriter(dataYamlStream, StandardCharsets.UTF_8));
yaml.dump(urlMap, new OutputStreamWriter(urlYamlStream, StandardCharsets.UTF_8));
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(haeFilePath))) {
zipOut.putNextEntry(new ZipEntry("info"));
zipOut.write(host.getBytes(StandardCharsets.UTF_8));
zipOut.closeEntry();
zipOut.putNextEntry(new ZipEntry("data"));
zipOut.write(dataYamlStream.toByteArray());
zipOut.closeEntry();
zipOut.putNextEntry(new ZipEntry("url"));
zipOut.write(urlYamlStream.toByteArray());
zipOut.closeEntry();
for (String httpHash : httpMap.keySet()) {
Map<String, Object> httpItem = httpMap.get(httpHash);
tasks.add(() -> {
try {
ByteArrayOutputStream httpOutStream = new ByteArrayOutputStream();
byte[] request = (byte[]) httpItem.get("request");
byte[] response = (byte[]) httpItem.get("response");
httpOutStream.write(response);
httpOutStream.write(request);
synchronized (zipOut) {
zipOut.putNextEntry(new ZipEntry(String.format("http/%s", httpHash)));
zipOut.write(httpOutStream.toByteArray());
zipOut.closeEntry();
}
} catch (Exception e) {
api.logging().logToError("createHaeFile: " + e.getMessage());
}
return null;
});
}
executor.invokeAll(tasks);
} catch (Exception e) {
api.logging().logToError("createHaeFile: " + e.getMessage());
return false;
} finally {
executor.shutdown();
}
return true;
}
public HaeFileContent readHaeFile(String haeFilePath) {
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
List<Callable<Void>> tasks = new ArrayList<>();
HaeFileContent haeFileContent = new HaeFileContent(api);
LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE);
loaderOptions.setCodePointLimit(Integer.MAX_VALUE);
Yaml yaml = new Yaml(loaderOptions);
Path tempDirectory = null;
try {
if (hasValidStructure(haeFilePath)) {
tempDirectory = Files.createTempDirectory("hae");
haeFileContent.setHttpPath(tempDirectory.toString());
try (ZipFile zipFile = new ZipFile(haeFilePath)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String fileName = entry.getName();
if (fileName.startsWith("http/")) {
Path filePath = tempDirectory.resolve(fileName.substring("http/".length()));
tasks.add(() -> {
try (InputStream in = zipFile.getInputStream(entry)) {
Files.copy(in, filePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
api.logging().logToError("readHaeFile: " + e.getMessage());
}
return null;
});
} else {
try (InputStream in = zipFile.getInputStream(entry)) {
switch (fileName) {
case "info" ->
haeFileContent.setHost(new String(in.readAllBytes(), StandardCharsets.UTF_8));
case "data" ->
haeFileContent.setDataMap(yaml.load(new InputStreamReader(in, StandardCharsets.UTF_8)));
case "url" ->
haeFileContent.setUrlMap(yaml.load(new InputStreamReader(in, StandardCharsets.UTF_8)));
}
}
}
}
executor.invokeAll(tasks);
}
}
} catch (Exception e) {
api.logging().logToError("readHaeFile: " + e.getMessage());
if (tempDirectory != null) {
FileProcessor.deleteDirectoryWithContents(tempDirectory);
}
haeFileContent = null;
} finally {
executor.shutdown();
}
return haeFileContent;
}
private boolean hasValidStructure(String zipFilePath) {
Set<String> requiredRootEntries = new HashSet<>();
requiredRootEntries.add("info");
requiredRootEntries.add("data");
requiredRootEntries.add("url");
boolean hasHttpDirectoryWithFiles = false;
try {
ZipFile zipFile = new ZipFile(zipFilePath);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (!entry.isDirectory() && !name.contains("/")) {
requiredRootEntries.remove(name);
}
if (name.startsWith("http/") && !entry.isDirectory()) {
hasHttpDirectoryWithFiles = true;
}
if (requiredRootEntries.isEmpty() && hasHttpDirectoryWithFiles) {
break;
}
}
zipFile.close();
} catch (Exception ignored) {
}
return requiredRootEntries.isEmpty() && hasHttpDirectoryWithFiles;
}
}

View File

@@ -1,76 +0,0 @@
package hae.utils.project.model;
import burp.api.montoya.MontoyaApi;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HaeFileContent {
private final MontoyaApi api;
private String host;
private String httpPath;
private final Map<String, List<String>> dataMap;
private final Map<String, Map<String, String>> urlMap;
public HaeFileContent(MontoyaApi api) {
this.api = api;
this.dataMap = new HashMap<>();
this.urlMap = new HashMap<>();
}
public String getHost() {
return host;
}
public Map<String, List<String>> getDataMap() {
return dataMap;
}
public Map<String, Map<String, String>> getUrlMap() {
return urlMap;
}
public String getHttpPath() {
return httpPath;
}
public void setHost(String host) {
this.host = host;
}
public void setHttpPath(String path) {
this.httpPath = path;
}
public void setDataMap(Map<String, List<Object>> dataMap) {
for (Map.Entry<String, List<Object>> entry : dataMap.entrySet()) {
List<String> values = new ArrayList<>();
for (Object value : entry.getValue()) {
try {
values.add(new String((byte[]) value, StandardCharsets.UTF_8));
} catch (Exception e) {
values.add(value.toString());
}
}
this.dataMap.put(entry.getKey(), values);
}
}
public void setUrlMap(Map<String, Map<String, Object>> urlMap) {
for (Map.Entry<String, Map<String, Object>> entry : urlMap.entrySet()) {
Map<String, String> newValues = new HashMap<>();
Map<String, Object> values = entry.getValue();
for (String key : values.keySet()) {
try {
newValues.put(key, new String((byte[]) values.get(key), StandardCharsets.UTF_8));
} catch (Exception e) {
newValues.put(key, values.get(key).toString());
}
}
this.urlMap.put(entry.getKey(), newValues);
}
}
}

View File

@@ -1,293 +1,303 @@
rules: rules:
- group: Fingerprint - group: Fingerprint
rule: rule:
- name: Shiro - name: Shiro
loaded: true loaded: true
f_regex: (=deleteMe|rememberMe=) f_regex: (=deleteMe|rememberMe=)
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: green color: green
scope: any header scope: any header
engine: dfa engine: dfa
sensitive: true sensitive: true
- name: JSON Web Token - name: JSON Web Token
loaded: true loaded: true
f_regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,}) f_regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,})
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: green color: green
scope: any scope: any
engine: nfa engine: nfa
sensitive: true sensitive: true
- name: Swagger UI - name: Swagger UI
loaded: true loaded: true
f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion)) f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: red color: red
scope: response body scope: response body
engine: dfa engine: dfa
sensitive: false sensitive: false
- name: Ueditor - name: Ueditor
loaded: true loaded: true
f_regex: (ueditor\.(config|all)\.js) f_regex: (ueditor\.(config|all)\.js)
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: green color: green
scope: response body scope: response body
engine: dfa engine: dfa
sensitive: false sensitive: false
- name: Druid - name: Druid
loaded: true loaded: true
f_regex: (Druid Stat Index) f_regex: (Druid Stat Index)
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: orange color: orange
scope: response body scope: response body
engine: dfa engine: dfa
sensitive: false sensitive: false
- group: Maybe Vulnerability - group: Maybe Vulnerability
rule: rule:
- name: Java Deserialization - name: Java Deserialization
loaded: true loaded: true
f_regex: (javax\.faces\.ViewState) f_regex: (javax\.faces\.ViewState)
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: yellow color: yellow
scope: response body scope: response body
engine: dfa engine: dfa
sensitive: false sensitive: false
- name: Debug Logic Parameters - name: Debug Logic Parameters
loaded: true loaded: true
f_regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=)) f_regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: cyan color: cyan
scope: request scope: request
engine: dfa engine: dfa
sensitive: false sensitive: false
- name: URL As A Value - name: URL As A Value
loaded: true loaded: true
f_regex: (=(https?)(://|%3a%2f%2f)) f_regex: (=(https?)(://|%3a%2f%2f))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: cyan color: cyan
scope: any scope: any
engine: nfa engine: nfa
sensitive: false sensitive: false
- name: Upload Form - name: Upload Form
loaded: true loaded: true
f_regex: (type\=\"file\") f_regex: (type\=\"file\")
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: yellow color: yellow
scope: response body scope: response body
engine: dfa engine: dfa
sensitive: false sensitive: false
- name: DoS Paramters - name: DoS Paramters
loaded: true loaded: true
f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=)) f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: cyan color: cyan
scope: request scope: request
engine: dfa engine: dfa
sensitive: false sensitive: false
- group: Basic Information - group: Basic Information
rule: rule:
- name: Email - name: Email
loaded: true loaded: true
f_regex: (([a-z0-9]+[_|\.])*[a-z0-9]+@([a-z0-9]+[-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,5})) f_regex: (([a-z0-9]+[_|\.])*[a-z0-9]+@([a-z0-9]+[-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,5}))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: yellow color: yellow
scope: response scope: response
engine: nfa engine: nfa
sensitive: false sensitive: false
- name: Chinese IDCard - name: Chinese IDCard
loaded: true loaded: true
f_regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]' f_regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]'
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: orange color: orange
scope: response body scope: response body
engine: nfa engine: nfa
sensitive: true sensitive: true
- name: Chinese Mobile Number - name: Chinese Mobile Number
loaded: true loaded: true
f_regex: '[^\w]((?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]' f_regex: '[^\w]((?:(?:\+|0{0,2})86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]'
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: orange color: orange
scope: response body scope: response body
engine: nfa engine: nfa
sensitive: false sensitive: false
- name: Internal IP Address - name: Internal IP Address
loaded: true loaded: true
f_regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))' f_regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))'
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: cyan color: cyan
scope: response scope: response
engine: nfa engine: nfa
sensitive: true sensitive: true
- name: MAC Address - name: MAC Address
loaded: true loaded: true
f_regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})) f_regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: green color: green
scope: response scope: response
engine: nfa engine: nfa
sensitive: true sensitive: true
- group: Sensitive Information - group: Sensitive Information
rule: rule:
- name: Cloud Key - name: Cloud Key
loaded: true loaded: true
f_regex: (((access)(|-|_)(key)(|-|_)(id|secret))|(LTAI[a-z0-9]{12,20})) f_regex: (((access)(|-|_)(key)(|-|_)(id|secret))|(LTAI[a-z0-9]{12,20}))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: yellow color: yellow
scope: any scope: any
engine: nfa engine: nfa
sensitive: false sensitive: false
- name: Windows File/Dir Path - name: Windows File/Dir Path
loaded: true loaded: true
f_regex: '[^\w]([a-zA-Z]:\\\\?(?:[^<>:/\\|?*]+\\\\?)*)([^<>:/\\|?*]+(?:\.[^<>:/\\|?*]+)?)' f_regex: '[^\w]([a-zA-Z]:\\\\?(?:[^<>:/\\|?*]+\\\\?)*)([^<>:/\\|?*]+(?:\.[^<>:/\\|?*]+)?)'
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: green color: green
scope: response scope: response
engine: nfa engine: nfa
sensitive: true sensitive: true
- name: Password Field - name: Password Field
loaded: true loaded: true
f_regex: ((|'|")(|[\w]{1,10})([p](ass|wd|asswd|assword))(|[\w]{1,10})(|'|")(:|=)( f_regex: ((|\\)(|'|")(|[\w]{1,10})([p](ass|wd|asswd|assword))(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()(
|)('|")(.*?)('|")(|,)) |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: yellow color: yellow
scope: response body scope: response body
engine: nfa engine: nfa
sensitive: false sensitive: false
- name: Username Field - name: Username Field
loaded: true loaded: true
f_regex: ((|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|'|")(:|=)( f_regex: ((|\\)(|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()(
|)('|")(.*?)('|")(|,)) |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: green color: green
scope: response body scope: response body
engine: nfa engine: nfa
sensitive: false sensitive: false
- name: WeCom Key - name: WeCom Key
loaded: true loaded: true
f_regex: ((corp)(id|secret)) f_regex: ((corp)(id|secret))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: green color: green
scope: response body scope: response body
engine: dfa engine: dfa
sensitive: false sensitive: false
- name: JDBC Connection - name: JDBC Connection
loaded: true loaded: true
f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+) f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+)
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: yellow color: yellow
scope: any scope: any
engine: nfa engine: nfa
sensitive: false sensitive: false
- name: Authorization Header - name: Authorization Header
loaded: true loaded: true
f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100})) f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100}))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: yellow color: yellow
scope: response body scope: response body
engine: nfa engine: nfa
sensitive: false sensitive: false
- name: Sensitive Field - name: Sensitive Field
loaded: true loaded: true
f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin)|(ticket))([\w]{0,10})('|")?(\])?( f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin)|(ticket))([\w]{0,10})('|")?(\])?(
|)(:|=)( |)('|")(.*?)('|")(|,)) |)(:|=|\)\.val\()( |)('|")([^'"]+?)('|")(|,|\)))
s_regex: '' s_regex: ''
format: '{0}' format: '{0}'
color: yellow color: yellow
scope: response scope: response
engine: nfa engine: nfa
sensitive: false sensitive: false
- group: Other - name: Mobile Number Field
rule: loaded: true
- name: Linkfinder f_regex: ((|\\)(|'|")(|[\w]{1,10})(mobile|phone|sjh|shoujihao|concat)(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()(
loaded: true |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))
f_regex: (?:"|')(((?:[a-zA-Z]{1,10}://|//)[^"'/]{1,}\.[a-zA-Z]{2,}[^"']{0,})|((?:/|\.\./|\./)[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,})|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{3,}(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-]{1,}\.(?:\w)(?:[\?|#][^"|']{0,}|)))(?:"|') s_regex: ''
s_regex: '' format: '{0}'
format: '{0}' color: green
color: gray scope: response body
scope: response body engine: nfa
engine: nfa sensitive: false
sensitive: true - group: Other
- name: Source Map rule:
loaded: true - name: Linkfinder
f_regex: (\.js\.map) loaded: true
s_regex: '' f_regex: (?:"|')(((?:[a-zA-Z]{1,10}://|//)[^"'/]{1,}\.[a-zA-Z]{2,}[^"']{0,})|((?:/|\.\./|\./)[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,})|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{3,}(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-]{1,}\.(?:\w)(?:[\?|#][^"|']{0,}|)))(?:"|')
format: '{0}' s_regex: ''
color: pink format: '{0}'
scope: response body color: gray
engine: dfa scope: response body
sensitive: false engine: nfa
- name: Create Script sensitive: true
loaded: true - name: Source Map
f_regex: (\{[^{}]*\}\s*\[[^\s]*\]\s*\+\s*"[^\s]*\.js") loaded: true
s_regex: '"?([\w].*?)"?:"(.*?)"' f_regex: (\.js\.map)
format: '{0}.{1}' s_regex: ''
color: green format: '{0}'
scope: response body color: pink
engine: nfa scope: response body
sensitive: false engine: dfa
- name: URL Schemes sensitive: false
loaded: true - name: Create Script
f_regex: (\b(?![\w]{0,10}?https?://)(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|])) loaded: true
s_regex: '' f_regex: (\{[^{}]*\}\s*\[[^\s]*\]\s*\+\s*"[^\s]*\.js")
format: '{0}' s_regex: '"?([\w].*?)"?:"(.*?)"'
color: yellow format: '{0}.{1}'
scope: response body color: green
engine: nfa scope: response body
sensitive: false engine: nfa
- name: Router Push sensitive: false
loaded: true - name: URL Schemes
f_regex: (\$router\.push) loaded: true
s_regex: '' f_regex: (\b(?![\w]{0,10}?https?://)(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]))
format: '{0}' s_regex: ''
color: magenta format: '{0}'
scope: response body color: yellow
engine: dfa scope: response body
sensitive: false engine: nfa
- name: All URL sensitive: false
loaded: true - name: Router Push
f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|]) loaded: true
s_regex: '' f_regex: (\$router\.push)
format: '{0}' s_regex: ''
color: gray format: '{0}'
scope: response body color: magenta
engine: nfa scope: response body
sensitive: true engine: dfa
- name: Request URI sensitive: false
loaded: true - name: All URL
f_regex: ' ((?!.*\.js(\?.*)?$)(.*?[^.js$])) ' loaded: true
s_regex: '' f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|])
format: '{0}' s_regex: ''
color: gray format: '{0}'
scope: request line color: gray
engine: nfa scope: response body
sensitive: false engine: nfa
- name: 302 Location sensitive: true
loaded: true - name: Request URI
f_regex: 'Location: (.*?)\n' loaded: true
s_regex: '' f_regex: ' ((?!.*\.js(\?.*)?$)(.*?[^.js$])) '
format: '{0}' s_regex: ''
color: gray format: '{0}'
scope: response header color: gray
engine: nfa scope: request line
sensitive: false engine: nfa
sensitive: false
- name: 302 Location
loaded: true
f_regex: 'Location: (.*?)\n'
s_regex: ''
format: '{0}'
color: gray
scope: response header
engine: nfa
sensitive: false