Version: 3.3 Update
This commit is contained in:
12
README.md
12
README.md
@@ -6,22 +6,20 @@
|
||||
|
||||
## 项目介绍
|
||||
|
||||
**HaE**是一款**网络安全(数据安全)领域**下的**结合人工智能(AI)大模型辅助**的框架式项目,旨在实现对HTTP消息(包含WebSocket)的高亮标记和信息提取。本项目通过自定义正则表达式匹配响应报文或请求报文,并对匹配成功的报文进行标记和提取。
|
||||
**HaE**是一款**网络安全(数据安全)领域**下的框架式项目,HaE采用了**乐高积木式**模块化设计理念,巧妙地融入了**人工智能大模型辅助技术**,实现对HTTP消息(包含WebSocket)精细化的标记和提取。
|
||||
|
||||
通过运用**多引擎**的自定义正则表达式,HaE能够准确匹配并处理HTTP请求与响应报文(包含WebSocket),对匹配成功的内容进行有效的标记和信息抽取,从而提升网络安全(数据安全)领域下的**漏洞和数据分析效率**。
|
||||
|
||||
> 随着现代化Web应用采用前后端分离的开发模式,日常漏洞挖掘的过程中,捕获的HTTP请求流量也相应增加。若想全面评估一个Web应用,会花费大量时间在无用的报文上。**HaE的出现旨在解决这类情况**,借助HaE,您能够**有效减少**测试时间,将更多精力集中在**有价值且有意义**的报文上,从而**提高漏洞挖掘效率**。
|
||||
|
||||
**注意事项**:
|
||||
|
||||
1. HaE 3.3版本开启了AI+新功能,该功能仅支持阿里的`Qwen-Long`模型和月之暗面的`moonshot-v1-128k`模型,请配置和使用时注意。
|
||||
1. HaE 3.3版本开启了AI+新功能,该功能目前仅支持阿里的`Qwen-Long`模型(支持超长文本)和月之暗面的`moonshot-v1-128k`模型(支持短文本),请配置和使用时注意。
|
||||
2. HaE 3.0版本开始采用`Montoya API`进行开发,使用新版HaE需要升级你的BurpSuite版本(>=2023.12.1)。
|
||||
3. HaE 2.6版本后对规则字段进行了更新,因此无法适配<=2.6版本的规则,请用户自行前往[规则转换页面](https://gh0st.cn/HaE/ConversionRule.html)进行转换。
|
||||
4. HaE官方规则库存放在[Github](https://raw.githubusercontent.com/gh0stkey/HaE/gh-pages/Rules.yml)上,因此点击`Update`升级HaE官方规则库时需使用代理(BApp审核考虑安全性,不允许使用CDN)。
|
||||
5. 自定义HaE规则必须用左右括号`()`将所需提取的表达式内容包含,例如你要匹配一个**Shiro应用**的响应报文,正常匹配规则为`rememberMe=delete`,在HaE的规则中就需要变成`(rememberMe=delete)`。
|
||||
|
||||
**温馨暖语**:
|
||||
|
||||
> HaE与你同行,点亮BurpSuite那最后一抹光芒,踏上漏洞挖掘的征途,在这场智慧与技巧的角逐中,共同发掘数字世界的隐秘角落。
|
||||
|
||||
## 使用方法
|
||||
|
||||
插件装载: `Extender - Extensions - Add - Select File - Next`
|
||||
@@ -57,7 +55,7 @@ HaE目前的规则一共有8个字段,详细的含义如下所示:
|
||||
4. **算法**:内置高亮颜色的升级算法,当出现相同颜色时**会自动向上升级一个颜色**进行标记,**避免`屠龙者终成恶龙`场景**。
|
||||
5. **管理**:支持对数据的一键导出、导入,以**自定义`.hae`文件的方式**进行项目数据存储,**便于存储和共享项目数据**。
|
||||
6. **实战**:官方规则库和规则字段作用功能,都是**基于实战化场景总结输出**的,**以此提高数据的有效性、精准性发现**。
|
||||
7. **智能**:集成阿里、月之暗面**人工智能(AI)大模型**API,对匹配的数据进行优化处理,**提高数据式漏洞挖掘效率**。
|
||||
7. **智能**:融入**人工智能(AI)大模型**API,对匹配的数据进行优化处理,**提高数据式漏洞挖掘效率**。
|
||||
|
||||
| 界面名称 | 界面展示 |
|
||||
| ------------------------ | ---------------------------------------------------- |
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 168 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 270 KiB After Width: | Height: | Size: 331 KiB |
BIN
images/rules.png
BIN
images/rules.png
Binary file not shown.
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 155 KiB |
@@ -24,6 +24,8 @@ public class Config {
|
||||
"request body"
|
||||
};
|
||||
|
||||
public static String scopeOptions = "Suite|Target|Proxy|Scanner|Intruder|Repeater|Logger|Sequencer|Decoder|Comparer|Extensions|Organizer|Recorded login replayer";
|
||||
|
||||
public static String[] ruleFields = {
|
||||
"Loaded", "Name", "F-Regex", "S-Regex", "Format", "Color", "Scope", "Engine", "Sensitive"
|
||||
};
|
||||
@@ -51,9 +53,12 @@ public class Config {
|
||||
"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 ConcurrentHashMap<String, Map<String, List<String>>> globalDataMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static ConcurrentHashMap<String, Map<String, Object>> globalHostHashMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public class HaE implements BurpExtension {
|
||||
@Override
|
||||
public void initialize(MontoyaApi api) {
|
||||
// 设置扩展名称
|
||||
String version = "3.2.2";
|
||||
String version = "3.3";
|
||||
api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version));
|
||||
|
||||
// 加载扩展后输出的项目信息
|
||||
@@ -44,7 +44,7 @@ public class HaE implements BurpExtension {
|
||||
// 注册消息编辑框(用于展示数据)
|
||||
api.userInterface().registerHttpRequestEditorProvider(new RequestEditor(api, configLoader));
|
||||
api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api, configLoader));
|
||||
api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api));
|
||||
api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api, configLoader));
|
||||
|
||||
api.extension().registerUnloadingHandler(new ExtensionUnloadingHandler() {
|
||||
@Override
|
||||
|
||||
@@ -37,26 +37,17 @@ public class Main extends JPanel {
|
||||
|
||||
// 新增Logo
|
||||
JTabbedPane HaETabbedPane = new JTabbedPane();
|
||||
HaETabbedPane.addTab("", getImageIcon(false), mainTabbedPane);
|
||||
boolean isDarkBg = isDarkBg(HaETabbedPane);
|
||||
HaETabbedPane.addTab("", getImageIcon(isDarkBg), mainTabbedPane);
|
||||
// 中文Slogan:赋能白帽,高效作战
|
||||
HaETabbedPane.addTab(" Highlighter and Extractor - Empower ethical hacker for efficient operations. ", null);
|
||||
HaETabbedPane.setEnabledAt(1, false);
|
||||
HaETabbedPane.addPropertyChangeListener("background", new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent e) {
|
||||
boolean isDarkBg = isDarkBg();
|
||||
boolean isDarkBg = isDarkBg(HaETabbedPane);
|
||||
HaETabbedPane.setIconAt(0, getImageIcon(isDarkBg));
|
||||
}
|
||||
|
||||
private boolean isDarkBg() {
|
||||
Color bg = HaETabbedPane.getBackground();
|
||||
int r = bg.getRed();
|
||||
int g = bg.getGreen();
|
||||
int b = bg.getBlue();
|
||||
int avg = (r + g + b) / 3;
|
||||
|
||||
return avg < 128;
|
||||
}
|
||||
});
|
||||
|
||||
add(HaETabbedPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
|
||||
@@ -70,6 +61,16 @@ public class Main extends JPanel {
|
||||
mainTabbedPane.addTab("Config", new Config(api, configLoader, rules));
|
||||
}
|
||||
|
||||
private boolean isDarkBg(JTabbedPane HaETabbedPane) {
|
||||
Color bg = HaETabbedPane.getBackground();
|
||||
int r = bg.getRed();
|
||||
int g = bg.getGreen();
|
||||
int b = bg.getBlue();
|
||||
int avg = (r + g + b) / 3;
|
||||
|
||||
return avg < 128;
|
||||
}
|
||||
|
||||
private ImageIcon getImageIcon(boolean isDark) {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
URL imageURL;
|
||||
|
||||
@@ -5,6 +5,7 @@ import hae.Config;
|
||||
import hae.component.board.message.MessageEntry;
|
||||
import hae.component.board.message.MessageTableModel;
|
||||
import hae.component.board.message.MessageTableModel.MessageTable;
|
||||
import hae.component.board.table.Datatable;
|
||||
import hae.instances.http.utils.RegularMatcher;
|
||||
import hae.utils.ConfigLoader;
|
||||
import hae.utils.project.ProjectProcessor;
|
||||
@@ -46,7 +47,7 @@ public class Databoard extends JPanel {
|
||||
private final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();
|
||||
private final JComboBox hostComboBox = new JComboBox(comboBoxModel);
|
||||
|
||||
private SwingWorker<Boolean, Void> handleComboBoxWorker;
|
||||
private SwingWorker<Map<String, List<String>>, Void> handleComboBoxWorker;
|
||||
private SwingWorker<Void, Void> applyHostFilterWorker;
|
||||
private SwingWorker<List<String>, Void> exportActionWorker;
|
||||
private SwingWorker<List<String>, Void> importActionWorker;
|
||||
@@ -63,7 +64,7 @@ public class Databoard extends JPanel {
|
||||
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, 25, 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, 0.0, 1.0E-4};
|
||||
|
||||
@@ -120,7 +121,7 @@ public class Databoard extends JPanel {
|
||||
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, 3, 3, 1, 1.0, 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);
|
||||
@@ -143,6 +144,11 @@ public class Databoard extends JPanel {
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -150,7 +156,7 @@ public class Databoard extends JPanel {
|
||||
progressBar.setStringPainted(true);
|
||||
progressBar.setValue(progressBar.getMaximum());
|
||||
} else {
|
||||
progressBar.setString("Loading...");
|
||||
progressBar.setString(showString);
|
||||
progressBar.setStringPainted(true);
|
||||
}
|
||||
}
|
||||
@@ -198,18 +204,27 @@ public class Databoard extends JPanel {
|
||||
handleComboBoxWorker.cancel(true);
|
||||
}
|
||||
|
||||
handleComboBoxWorker = new SwingWorker<Boolean, Void>() {
|
||||
handleComboBoxWorker = new SwingWorker<Map<String, List<String>>, Void>() {
|
||||
@Override
|
||||
protected Boolean doInBackground() {
|
||||
return populateTabbedPaneByHost(selectedHost);
|
||||
protected Map<String, List<String>> doInBackground() {
|
||||
return getSelectedMapByHost(selectedHost);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
if (!isCancelled()) {
|
||||
try {
|
||||
boolean status = get();
|
||||
if (status) {
|
||||
Map<String, List<String>> selectedDataMap = get();
|
||||
if (!selectedDataMap.isEmpty()) {
|
||||
dataTabbedPane.removeAll();
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
|
||||
String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size());
|
||||
Datatable datatablePanel = new Datatable(api, configLoader, entry.getKey(), entry.getValue());
|
||||
datatablePanel.setTableListener(messageTableModel);
|
||||
dataTabbedPane.addTab(tabTitle, datatablePanel);
|
||||
}
|
||||
|
||||
JSplitPane messageSplitPane = messageTableModel.getSplitPane();
|
||||
splitPane.setLeftComponent(dataTabbedPane);
|
||||
splitPane.setRightComponent(messageSplitPane);
|
||||
@@ -257,7 +272,7 @@ public class Databoard extends JPanel {
|
||||
isMatchHost = false;
|
||||
}
|
||||
|
||||
private boolean populateTabbedPaneByHost(String selectedHost) {
|
||||
private Map<String, List<String>> getSelectedMapByHost(String selectedHost) {
|
||||
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
|
||||
Map<String, List<String>> selectedDataMap;
|
||||
|
||||
@@ -283,20 +298,7 @@ public class Databoard extends JPanel {
|
||||
selectedDataMap = dataMap.get(selectedHost);
|
||||
}
|
||||
|
||||
if (!selectedDataMap.isEmpty()) {
|
||||
dataTabbedPane.removeAll();
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : selectedDataMap.entrySet()) {
|
||||
String tabTitle = String.format("%s (%s)", entry.getKey(), entry.getValue().size());
|
||||
Datatable datatablePanel = new Datatable(api, entry.getKey(), entry.getValue());
|
||||
datatablePanel.setTableListener(messageTableModel);
|
||||
dataTabbedPane.addTab(tabTitle, datatablePanel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return selectedDataMap;
|
||||
}
|
||||
|
||||
private void filterComboBoxList() {
|
||||
@@ -396,7 +398,8 @@ public class Databoard extends JPanel {
|
||||
List<String> taskStatusList = get();
|
||||
if (!taskStatusList.isEmpty()) {
|
||||
String exportStatusMessage = String.format("Exported File List Status:\n%s", String.join("\n", taskStatusList));
|
||||
JOptionPane.showConfirmDialog(Databoard.this, exportStatusMessage, "Info", JOptionPane.YES_OPTION);
|
||||
|
||||
JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(exportStatusMessage), "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
@@ -406,6 +409,16 @@ public class Databoard extends JPanel {
|
||||
exportActionWorker.execute();
|
||||
}
|
||||
|
||||
private JScrollPane generateTaskStatusPane(String message) {
|
||||
JTextArea textArea = new JTextArea(message);
|
||||
textArea.setEditable(false);
|
||||
textArea.setLineWrap(true);
|
||||
JScrollPane scrollPane = new JScrollPane(textArea);
|
||||
scrollPane.setPreferredSize(new Dimension(400, 200));
|
||||
|
||||
return scrollPane;
|
||||
}
|
||||
|
||||
private List<String> 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))
|
||||
@@ -509,7 +522,7 @@ public class Databoard extends JPanel {
|
||||
List<String> taskStatusList = get();
|
||||
if (!taskStatusList.isEmpty()) {
|
||||
String importStatusMessage = "Imported File List Status:\n" + String.join("\n", taskStatusList);
|
||||
JOptionPane.showConfirmDialog(Databoard.this, importStatusMessage, "Info", JOptionPane.YES_OPTION);
|
||||
JOptionPane.showMessageDialog(Databoard.this, generateTaskStatusPane(importStatusMessage), "Info", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
package hae.component.board;
|
||||
|
||||
import burp.api.montoya.MontoyaApi;
|
||||
import hae.component.board.message.MessageTableModel;
|
||||
import hae.utils.UIEnhancer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Datatable extends JPanel {
|
||||
private final MontoyaApi api;
|
||||
private final JTable dataTable;
|
||||
private final DefaultTableModel dataTableModel;
|
||||
private final JTextField searchField;
|
||||
private final TableRowSorter<DefaultTableModel> sorter;
|
||||
private final JCheckBox searchMode = new JCheckBox("Reverse search");
|
||||
private final String tabName;
|
||||
|
||||
public Datatable(MontoyaApi api, String tabName, List<String> dataList) {
|
||||
this.api = api;
|
||||
this.tabName = tabName;
|
||||
|
||||
String[] columnNames = {"#", "Information"};
|
||||
|
||||
dataTableModel = new DefaultTableModel(columnNames, 0);
|
||||
dataTable = new JTable(dataTableModel);
|
||||
sorter = new TableRowSorter<>(dataTableModel);
|
||||
|
||||
searchField = new JTextField();
|
||||
|
||||
initComponents(dataList);
|
||||
}
|
||||
|
||||
private void initComponents(List<String> dataList) {
|
||||
// 设置ID排序
|
||||
sorter.setComparator(0, new Comparator<Integer>() {
|
||||
@Override
|
||||
public int compare(Integer s1, Integer s2) {
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
});
|
||||
|
||||
dataTable.setRowSorter(sorter);
|
||||
TableColumn idColumn = dataTable.getColumnModel().getColumn(0);
|
||||
idColumn.setMaxWidth(50);
|
||||
|
||||
for (String item : dataList) {
|
||||
if (!item.isEmpty()) {
|
||||
addRowToTable(new Object[]{item});
|
||||
}
|
||||
}
|
||||
|
||||
// 设置灰色默认文本
|
||||
String searchText = "Search";
|
||||
UIEnhancer.setTextFieldPlaceholder(searchField, searchText);
|
||||
|
||||
// 监听输入框内容输入、更新、删除
|
||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
performSearch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
performSearch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
performSearch();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// 设置布局
|
||||
JScrollPane scrollPane = new JScrollPane(dataTable);
|
||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
|
||||
searchMode.addItemListener(e -> performSearch());
|
||||
|
||||
setLayout(new BorderLayout(0, 5));
|
||||
|
||||
JPanel optionsPanel = new JPanel();
|
||||
optionsPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 5));
|
||||
optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.X_AXIS));
|
||||
|
||||
// 新增复选框要在这修改rows
|
||||
JPanel menuPanel = new JPanel(new GridLayout(1, 1));
|
||||
menuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
menuPanel.add(searchMode);
|
||||
menu.add(menuPanel);
|
||||
|
||||
JButton settingsButton = new JButton("Settings");
|
||||
settingsButton.addActionListener(e -> {
|
||||
int x = settingsButton.getX();
|
||||
int y = settingsButton.getY() - menu.getPreferredSize().height;
|
||||
menu.show(settingsButton, x, y);
|
||||
});
|
||||
|
||||
optionsPanel.add(settingsButton);
|
||||
optionsPanel.add(Box.createHorizontalStrut(5));
|
||||
optionsPanel.add(searchField);
|
||||
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
add(optionsPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private void addRowToTable(Object[] data) {
|
||||
int rowCount = dataTableModel.getRowCount();
|
||||
int id = rowCount > 0 ? (Integer) dataTableModel.getValueAt(rowCount - 1, 0) + 1 : 1;
|
||||
Object[] rowData = new Object[data.length + 1];
|
||||
rowData[0] = id;
|
||||
System.arraycopy(data, 0, rowData, 1, data.length);
|
||||
dataTableModel.addRow(rowData);
|
||||
}
|
||||
|
||||
private void performSearch() {
|
||||
if (searchField.getForeground().equals(Color.BLACK)) {
|
||||
RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() {
|
||||
public boolean include(Entry<?, ?> entry) {
|
||||
String searchFieldTextText = searchField.getText();
|
||||
Pattern pattern = null;
|
||||
try {
|
||||
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
String entryValue = ((String) entry.getValue(1)).toLowerCase();
|
||||
searchFieldTextText = searchFieldTextText.toLowerCase();
|
||||
if (pattern != null) {
|
||||
return searchFieldTextText.isEmpty() || pattern.matcher(entryValue).find() != searchMode.isSelected();
|
||||
} else {
|
||||
return searchFieldTextText.isEmpty() || entryValue.contains(searchFieldTextText) != searchMode.isSelected();
|
||||
}
|
||||
}
|
||||
};
|
||||
sorter.setRowFilter(rowFilter);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTableListener(MessageTableModel messagePanel) {
|
||||
// 表格复制功能
|
||||
dataTable.setTransferHandler(new TransferHandler() {
|
||||
@Override
|
||||
public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException {
|
||||
if (comp instanceof JTable) {
|
||||
StringSelection stringSelection = new StringSelection(getSelectedDataAtTable((JTable) comp).replace("\0", "").replaceAll("[\\p{Cntrl}&&[^\r\n\t]]", ""));
|
||||
clip.setContents(stringSelection, null);
|
||||
} else {
|
||||
super.exportToClipboard(comp, clip, action);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dataTable.setDefaultEditor(Object.class, null);
|
||||
|
||||
// 表格内容双击事件
|
||||
dataTable.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 2) {
|
||||
int selectedRow = dataTable.getSelectedRow();
|
||||
if (selectedRow != -1) {
|
||||
String rowData = dataTable.getValueAt(selectedRow, 1).toString();
|
||||
messagePanel.applyMessageFilter(tabName, rowData);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String getSelectedDataAtTable(JTable table) {
|
||||
int[] selectRows = table.getSelectedRows();
|
||||
StringBuilder selectData = new StringBuilder();
|
||||
|
||||
for (int row : selectRows) {
|
||||
selectData.append(table.getValueAt(row, 1).toString()).append("\n");
|
||||
}
|
||||
|
||||
if (!selectData.isEmpty()) {
|
||||
selectData.deleteCharAt(selectData.length() - 1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
return selectData.toString();
|
||||
}
|
||||
|
||||
public JTable getDataTable() {
|
||||
return this.dataTable;
|
||||
}
|
||||
}
|
||||
|
||||
157
src/main/java/hae/component/board/table/AIPower.java
Normal file
157
src/main/java/hae/component/board/table/AIPower.java
Normal file
@@ -0,0 +1,157 @@
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
347
src/main/java/hae/component/board/table/Datatable.java
Normal file
347
src/main/java/hae/component/board/table/Datatable.java
Normal file
@@ -0,0 +1,347 @@
|
||||
package hae.component.board.table;
|
||||
|
||||
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.utils.ConfigLoader;
|
||||
import hae.utils.UIEnhancer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.PopupMenuEvent;
|
||||
import javax.swing.event.PopupMenuListener;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Datatable extends JPanel {
|
||||
private final MontoyaApi api;
|
||||
private final ConfigLoader configLoader;
|
||||
private final JTable dataTable;
|
||||
private final DefaultTableModel dataTableModel;
|
||||
private final JTextField searchField;
|
||||
private final TableRowSorter<DefaultTableModel> sorter;
|
||||
private final JCheckBox searchMode = new JCheckBox("Reverse search");
|
||||
private final String tabName;
|
||||
private final JProgressBar progressBar;
|
||||
private final JPopupMenu aiEmpoweredMenu;
|
||||
private final JPanel footerPanel;
|
||||
|
||||
public Datatable(MontoyaApi api, ConfigLoader configLoader, String tabName, List<String> dataList) {
|
||||
this.api = api;
|
||||
this.configLoader = configLoader;
|
||||
this.tabName = tabName;
|
||||
this.progressBar = new JProgressBar();
|
||||
|
||||
String[] columnNames = {"#", "Information"};
|
||||
this.dataTableModel = new DefaultTableModel(columnNames, 0);
|
||||
|
||||
this.dataTable = new JTable(dataTableModel);
|
||||
this.sorter = new TableRowSorter<>(dataTableModel);
|
||||
this.searchField = new JTextField();
|
||||
this.aiEmpoweredMenu = new JPopupMenu();
|
||||
this.footerPanel = new JPanel(new BorderLayout(0, 5));
|
||||
|
||||
initComponents(dataList);
|
||||
}
|
||||
|
||||
private void initComponents(List<String> dataList) {
|
||||
progressBar.setVisible(false);
|
||||
|
||||
// 设置ID排序
|
||||
sorter.setComparator(0, new Comparator<Integer>() {
|
||||
@Override
|
||||
public int compare(Integer s1, Integer s2) {
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
});
|
||||
|
||||
dataTable.setRowSorter(sorter);
|
||||
TableColumn idColumn = dataTable.getColumnModel().getColumn(0);
|
||||
idColumn.setMaxWidth(50);
|
||||
|
||||
for (String item : dataList) {
|
||||
if (!item.isEmpty()) {
|
||||
addRowToTable(new Object[]{item});
|
||||
}
|
||||
}
|
||||
|
||||
// 设置灰色默认文本
|
||||
String searchText = "Search";
|
||||
UIEnhancer.setTextFieldPlaceholder(searchField, searchText);
|
||||
|
||||
// 监听输入框内容输入、更新、删除
|
||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
performSearch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
performSearch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
performSearch();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// 设置布局
|
||||
JScrollPane scrollPane = new JScrollPane(dataTable);
|
||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
|
||||
setLayout(new BorderLayout(0, 5));
|
||||
|
||||
JPanel optionsPanel = new JPanel();
|
||||
optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.X_AXIS));
|
||||
|
||||
// Settings按钮
|
||||
JPanel settingMenuPanel = new JPanel(new GridLayout(1, 1));
|
||||
settingMenuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
|
||||
JPopupMenu settingMenu = new JPopupMenu();
|
||||
settingMenuPanel.add(searchMode);
|
||||
searchMode.addItemListener(e -> performSearch());
|
||||
settingMenu.add(settingMenuPanel);
|
||||
|
||||
JButton settingsButton = new JButton("Settings");
|
||||
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(Box.createHorizontalStrut(5));
|
||||
optionsPanel.add(searchField);
|
||||
optionsPanel.add(Box.createHorizontalStrut(5));
|
||||
optionsPanel.add(aiEmpoweredButton);
|
||||
|
||||
footerPanel.setBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3));
|
||||
footerPanel.add(optionsPanel, BorderLayout.CENTER);
|
||||
footerPanel.add(progressBar, BorderLayout.SOUTH);
|
||||
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
add(footerPanel, BorderLayout.SOUTH);
|
||||
|
||||
setProgressBar(false);
|
||||
}
|
||||
|
||||
private void setMenuShow(JPopupMenu menu, JButton button) {
|
||||
button.addActionListener(e -> {
|
||||
Point buttonLocation = button.getLocationOnScreen();
|
||||
Dimension menuSize = menu.getPreferredSize();
|
||||
int x = buttonLocation.x + (button.getWidth() - menuSize.width) / 2;
|
||||
int y = buttonLocation.y - menuSize.height;
|
||||
menu.show(button, x - buttonLocation.x, y - buttonLocation.y);
|
||||
});
|
||||
}
|
||||
|
||||
private void setProgressBar(boolean status) {
|
||||
Databoard.setProgressBar(status, progressBar, "AI+ ...");
|
||||
}
|
||||
|
||||
private void addRowToTable(Object[] data) {
|
||||
int rowCount = dataTableModel.getRowCount();
|
||||
int id = rowCount > 0 ? (Integer) dataTableModel.getValueAt(rowCount - 1, 0) + 1 : 1;
|
||||
Object[] rowData = new Object[data.length + 1];
|
||||
rowData[0] = id;
|
||||
System.arraycopy(data, 0, rowData, 1, data.length);
|
||||
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() {
|
||||
if (searchField.getForeground().equals(Color.BLACK)) {
|
||||
RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() {
|
||||
public boolean include(Entry<?, ?> entry) {
|
||||
String searchFieldTextText = searchField.getText();
|
||||
Pattern pattern = null;
|
||||
try {
|
||||
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
String entryValue = ((String) entry.getValue(1)).toLowerCase();
|
||||
searchFieldTextText = searchFieldTextText.toLowerCase();
|
||||
if (pattern != null) {
|
||||
return searchFieldTextText.isEmpty() || pattern.matcher(entryValue).find() != searchMode.isSelected();
|
||||
} else {
|
||||
return searchFieldTextText.isEmpty() || entryValue.contains(searchFieldTextText) != searchMode.isSelected();
|
||||
}
|
||||
}
|
||||
};
|
||||
sorter.setRowFilter(rowFilter);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTableListener(MessageTableModel messagePanel) {
|
||||
// 表格复制功能
|
||||
dataTable.setTransferHandler(new TransferHandler() {
|
||||
@Override
|
||||
public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException {
|
||||
if (comp instanceof JTable) {
|
||||
StringSelection stringSelection = new StringSelection(getSelectedDataAtTable((JTable) comp).replace("\0", "").replaceAll("[\\p{Cntrl}&&[^\r\n\t]]", ""));
|
||||
clip.setContents(stringSelection, null);
|
||||
} else {
|
||||
super.exportToClipboard(comp, clip, action);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dataTable.setDefaultEditor(Object.class, null);
|
||||
|
||||
// 表格内容双击事件
|
||||
dataTable.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 2) {
|
||||
int selectedRow = dataTable.getSelectedRow();
|
||||
if (selectedRow != -1) {
|
||||
String rowData = dataTable.getValueAt(selectedRow, 1).toString();
|
||||
messagePanel.applyMessageFilter(tabName, rowData);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getTableData(JTable table) {
|
||||
StringBuilder selectData = new StringBuilder();
|
||||
int rowCount = table.getRowCount();
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
selectData.append(table.getValueAt(i, 1).toString()).append("\r\n");
|
||||
}
|
||||
|
||||
if (!selectData.isEmpty()) {
|
||||
selectData.delete(selectData.length() - 2, selectData.length());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
return selectData.toString();
|
||||
}
|
||||
|
||||
public String getSelectedDataAtTable(JTable table) {
|
||||
int[] selectRows = table.getSelectedRows();
|
||||
StringBuilder selectData = new StringBuilder();
|
||||
|
||||
for (int row : selectRows) {
|
||||
selectData.append(table.getValueAt(row, 1).toString()).append("\r\n");
|
||||
}
|
||||
|
||||
if (!selectData.isEmpty()) {
|
||||
selectData.delete(selectData.length() - 2, selectData.length());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
return selectData.toString();
|
||||
}
|
||||
|
||||
|
||||
public JTable getDataTable() {
|
||||
return this.dataTable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import hae.utils.UIEnhancer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.event.TableModelListener;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
@@ -14,12 +17,11 @@ import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public class Config extends JPanel {
|
||||
private final MontoyaApi api;
|
||||
@@ -27,22 +29,22 @@ public class Config extends JPanel {
|
||||
private final Rules rules;
|
||||
private JTextField addTextField;
|
||||
private final String defaultText = "Enter a new item";
|
||||
private final GridBagConstraints constraints = new GridBagConstraints();
|
||||
|
||||
public Config(MontoyaApi api, ConfigLoader configLoader, Rules rules) {
|
||||
this.api = api;
|
||||
this.configLoader = configLoader;
|
||||
this.rules = rules;
|
||||
|
||||
constraints.weightx = 1.0;
|
||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
initComponents();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
constraints.weightx = 1.0;
|
||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JPanel ruleInfoPanel = new JPanel(new GridBagLayout());
|
||||
ruleInfoPanel.setBorder(new EmptyBorder(10, 15, 5, 15));
|
||||
|
||||
@@ -62,6 +64,153 @@ public class Config extends JPanel {
|
||||
reloadButton.addActionListener(this::reloadActionPerformed);
|
||||
updateButton.addActionListener(this::onlineUpdateActionPerformed);
|
||||
|
||||
constraints.gridx = 1;
|
||||
JTabbedPane configTabbedPanel = new JTabbedPane();
|
||||
|
||||
String[] settingMode = new String[]{"Exclude suffix", "Block host"};
|
||||
JPanel settingPanel = createConfigTablePanel(settingMode, "Setting");
|
||||
JPanel scopePanel = getScopePanel();
|
||||
JScrollPane scopeScrollPane = new JScrollPane(scopePanel);
|
||||
scopeScrollPane.setBorder(new TitledBorder("Scope"));
|
||||
settingPanel.add(scopeScrollPane, BorderLayout.NORTH);
|
||||
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(configTabbedPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private JPanel getScopePanel() {
|
||||
JPanel scopePanel = new JPanel();
|
||||
scopePanel.setLayout(new BoxLayout(scopePanel, BoxLayout.X_AXIS));
|
||||
|
||||
String[] scopeInit = hae.Config.scopeOptions.split("\\|");
|
||||
String[] scopeMode = configLoader.getScope().split("\\|");
|
||||
for (String scope : scopeInit) {
|
||||
JCheckBox checkBox = new JCheckBox(scope);
|
||||
scopePanel.add(checkBox);
|
||||
for (String mode : scopeMode) {
|
||||
if (scope.equals(mode)) {
|
||||
checkBox.setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
checkBox.addActionListener(e -> updateScope(checkBox));
|
||||
}
|
||||
return scopePanel;
|
||||
}
|
||||
|
||||
private TableModelListener craeteSettingTableModelListener(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("Exclude suffix")) {
|
||||
if (!values.equals(configLoader.getExcludeSuffix()) && !values.isEmpty()) {
|
||||
configLoader.setExcludeSuffix(values);
|
||||
}
|
||||
}
|
||||
|
||||
if (selected.equals("Block host")) {
|
||||
if (!values.equals(configLoader.getBlockHost()) && !values.isEmpty()) {
|
||||
configLoader.setBlockHost(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ActionListener createSettingActionListener(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("Exclude suffix")) {
|
||||
addDataToTable(configLoader.getExcludeSuffix().replaceAll("\\|", "\r\n"), model);
|
||||
}
|
||||
|
||||
if (selected.equals("Block host")) {
|
||||
addDataToTable(configLoader.getBlockHost().replaceAll("\\|", "\r\n"), model);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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")) {
|
||||
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) {
|
||||
JPanel settingPanel = new JPanel(new BorderLayout());
|
||||
DefaultTableModel model = new DefaultTableModel();
|
||||
|
||||
@@ -80,48 +229,19 @@ public class Config extends JPanel {
|
||||
JPanel inputPanelB = new JPanel(new BorderLayout());
|
||||
inputPanelB.setBorder(new EmptyBorder(0, 0, 3, 0));
|
||||
|
||||
constraints.gridx = 1;
|
||||
JButton addButton = new JButton("Add");
|
||||
JButton removeButton = new JButton("Remove");
|
||||
JButton pasteButton = new JButton("Paste");
|
||||
JButton clearButton = new JButton("Clear");
|
||||
|
||||
JComboBox<String> setTypeComboBox = new JComboBox<>();
|
||||
String[] mode = new String[]{"Exclude suffix", "Block host"};
|
||||
setTypeComboBox.setModel(new DefaultComboBoxModel<>(mode));
|
||||
setTypeComboBox.addActionListener(e -> {
|
||||
String selected = (String) setTypeComboBox.getSelectedItem();
|
||||
model.setRowCount(0);
|
||||
|
||||
if (selected.equals("Exclude suffix")) {
|
||||
addDataToTable(configLoader.getExcludeSuffix().replaceAll("\\|", "\r\n"), model);
|
||||
}
|
||||
setTypeComboBox.addActionListener(type.equals("AI+") ? createAIActionListener(setTypeComboBox, model) : createSettingActionListener(setTypeComboBox, model));
|
||||
|
||||
if (selected.equals("Block host")) {
|
||||
addDataToTable(configLoader.getBlockHost().replaceAll("\\|", "\r\n"), model);
|
||||
}
|
||||
});
|
||||
setTypeComboBox.setSelectedItem("Exclude suffix");
|
||||
setTypeComboBox.setSelectedItem(mode[0]);
|
||||
|
||||
model.addTableModelListener(new TableModelListener() {
|
||||
@Override
|
||||
public void tableChanged(TableModelEvent e) {
|
||||
String selected = (String) setTypeComboBox.getSelectedItem();
|
||||
String values = getFirstColumnDataAsString(model);
|
||||
|
||||
if (selected.equals("Exclude suffix")) {
|
||||
if (!values.equals(configLoader.getExcludeSuffix()) && !values.isEmpty()) {
|
||||
configLoader.setExcludeSuffix(values);
|
||||
}
|
||||
}
|
||||
|
||||
if (selected.equals("Block host")) {
|
||||
if (!values.equals(configLoader.getBlockHost()) && !values.isEmpty()) {
|
||||
configLoader.setBlockHost(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
model.addTableModelListener(type.equals("AI+") ? craeteAITableModelListener(setTypeComboBox, model) : craeteSettingTableModelListener(setTypeComboBox, model));
|
||||
|
||||
constraints.insets = new Insets(0, 0, 3, 0);
|
||||
constraints.gridy = 0;
|
||||
@@ -146,13 +266,13 @@ public class Config extends JPanel {
|
||||
settingPanel.add(inputPanel, BorderLayout.CENTER);
|
||||
|
||||
|
||||
addButton.addActionListener(e -> addActionPerformedAction(e, model));
|
||||
addButton.addActionListener(e -> addActionPerformed(e, model));
|
||||
|
||||
addTextField.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
addActionPerformedAction(null, model);
|
||||
addActionPerformed(null, model);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -178,16 +298,14 @@ public class Config extends JPanel {
|
||||
|
||||
clearButton.addActionListener(e -> model.setRowCount(0));
|
||||
|
||||
JPanel settingMainPanel = new JPanel(new BorderLayout());
|
||||
JLabel settingLabel = new JLabel("Setting:");
|
||||
JPanel settingLabelPanel = new JPanel(new BorderLayout());
|
||||
settingLabelPanel.add(settingLabel, BorderLayout.WEST);
|
||||
settingMainPanel.setBorder(new EmptyBorder(0, 15, 10, 15));
|
||||
settingMainPanel.add(settingLabelPanel, BorderLayout.NORTH);
|
||||
settingMainPanel.add(settingPanel, BorderLayout.CENTER);
|
||||
|
||||
add(ruleInfoPanel, BorderLayout.NORTH);
|
||||
add(settingMainPanel, BorderLayout.CENTER);
|
||||
JPanel settingMainPanel = new JPanel(new BorderLayout());
|
||||
settingMainPanel.setBorder(new EmptyBorder(5, 15, 10, 15));
|
||||
JScrollPane settingScroller = new JScrollPane(settingPanel);
|
||||
settingScroller.setBorder(new TitledBorder(type.equals("AI+") ? "API Key" : "Setting"));
|
||||
settingMainPanel.add(settingScroller, BorderLayout.CENTER);
|
||||
|
||||
return settingMainPanel;
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +357,22 @@ public class Config extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
private void addActionPerformedAction(ActionEvent e, DefaultTableModel model) {
|
||||
public void updateScope(JCheckBox checkBox) {
|
||||
String boxText = checkBox.getText();
|
||||
boolean selected = checkBox.isSelected();
|
||||
|
||||
Set<String> HaEScope = new HashSet<>(Arrays.asList(configLoader.getScope().split("\\|")));
|
||||
|
||||
if (selected) {
|
||||
HaEScope.add(boxText);
|
||||
} else {
|
||||
HaEScope.remove(boxText);
|
||||
}
|
||||
|
||||
configLoader.setScope(String.join("|", HaEScope));
|
||||
}
|
||||
|
||||
private void addActionPerformed(ActionEvent e, DefaultTableModel model) {
|
||||
String addTextFieldText = addTextField.getText();
|
||||
if (!addTextFieldText.equals(defaultText)) {
|
||||
addDataToTable(addTextFieldText, model);
|
||||
|
||||
@@ -9,7 +9,7 @@ import burp.api.montoya.ui.Selection;
|
||||
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
|
||||
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor;
|
||||
import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider;
|
||||
import hae.component.board.Datatable;
|
||||
import hae.component.board.table.Datatable;
|
||||
import hae.instances.http.utils.MessageProcessor;
|
||||
import hae.utils.ConfigLoader;
|
||||
import hae.utils.string.StringProcessor;
|
||||
@@ -59,7 +59,7 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
||||
@Override
|
||||
public void setRequestResponse(HttpRequestResponse requestResponse) {
|
||||
this.requestResponse = requestResponse;
|
||||
generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
|
||||
generateTabbedPaneFromResultMap(api, configLoader, jTabbedPane, this.dataList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,7 +73,10 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
||||
boolean isBlockHost = isBlockHost(hostList, host);
|
||||
|
||||
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
||||
boolean matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost;
|
||||
String toolType = creationContext.toolSource().toolType().toolName();
|
||||
boolean isToolScope = configLoader.getScope().contains(toolType);
|
||||
|
||||
boolean matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost || !isToolScope;
|
||||
|
||||
if (!matches && !request.bodyToString().equals("Loading...")) {
|
||||
this.dataList = messageProcessor.processRequest("", request, false);
|
||||
@@ -139,14 +142,14 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void generateTabbedPaneFromResultMap(MontoyaApi api, JTabbedPane tabbedPane, List<Map<String, String>> result) {
|
||||
public static void generateTabbedPaneFromResultMap(MontoyaApi api, ConfigLoader configLoader, JTabbedPane tabbedPane, List<Map<String, String>> result) {
|
||||
tabbedPane.removeAll();
|
||||
if (result != null && !result.isEmpty()) {
|
||||
Map<String, String> dataMap = result.get(0);
|
||||
if (dataMap != null && !dataMap.isEmpty()) {
|
||||
dataMap.keySet().forEach(i -> {
|
||||
String[] extractData = dataMap.get(i).split("\n");
|
||||
Datatable dataPanel = new Datatable(api, i, Arrays.asList(extractData));
|
||||
Datatable dataPanel = new Datatable(api, configLoader, i, Arrays.asList(extractData));
|
||||
tabbedPane.addTab(i, dataPanel);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import burp.api.montoya.ui.Selection;
|
||||
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
|
||||
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpResponseEditor;
|
||||
import burp.api.montoya.ui.editor.extension.HttpResponseEditorProvider;
|
||||
import hae.component.board.Datatable;
|
||||
import hae.component.board.table.Datatable;
|
||||
import hae.instances.http.utils.MessageProcessor;
|
||||
import hae.utils.ConfigLoader;
|
||||
import hae.utils.string.StringProcessor;
|
||||
@@ -60,7 +60,7 @@ public class ResponseEditor implements HttpResponseEditorProvider {
|
||||
@Override
|
||||
public void setRequestResponse(HttpRequestResponse requestResponse) {
|
||||
this.requestResponse = requestResponse;
|
||||
RequestEditor.generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
|
||||
RequestEditor.generateTabbedPaneFromResultMap(api, configLoader, jTabbedPane, this.dataList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +79,10 @@ public class ResponseEditor implements HttpResponseEditorProvider {
|
||||
boolean isBlockHost = RequestEditor.isBlockHost(hostList, host);
|
||||
|
||||
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
||||
matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost;
|
||||
String toolType = creationContext.toolSource().toolType().toolName();
|
||||
boolean isToolScope = configLoader.getScope().contains(toolType);
|
||||
|
||||
matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost || !isToolScope;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
@@ -8,8 +8,9 @@ import burp.api.montoya.ui.contextmenu.WebSocketMessage;
|
||||
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
|
||||
import burp.api.montoya.ui.editor.extension.ExtensionProvidedWebSocketMessageEditor;
|
||||
import burp.api.montoya.ui.editor.extension.WebSocketMessageEditorProvider;
|
||||
import hae.component.board.Datatable;
|
||||
import hae.component.board.table.Datatable;
|
||||
import hae.instances.http.utils.MessageProcessor;
|
||||
import hae.utils.ConfigLoader;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -18,18 +19,21 @@ import java.util.Map;
|
||||
|
||||
public class WebSocketEditor implements WebSocketMessageEditorProvider {
|
||||
private final MontoyaApi api;
|
||||
private final ConfigLoader configLoader;
|
||||
|
||||
public WebSocketEditor(MontoyaApi api) {
|
||||
public WebSocketEditor(MontoyaApi api, ConfigLoader configLoader) {
|
||||
this.api = api;
|
||||
this.configLoader = configLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionProvidedWebSocketMessageEditor provideMessageEditor(EditorCreationContext editorCreationContext) {
|
||||
return new Editor(api, editorCreationContext);
|
||||
return new Editor(api, configLoader, editorCreationContext);
|
||||
}
|
||||
|
||||
private static class Editor implements ExtensionProvidedWebSocketMessageEditor {
|
||||
private final MontoyaApi api;
|
||||
private final ConfigLoader configLoader;
|
||||
private final EditorCreationContext creationContext;
|
||||
private final MessageProcessor messageProcessor;
|
||||
private ByteArray message;
|
||||
@@ -37,8 +41,9 @@ public class WebSocketEditor implements WebSocketMessageEditorProvider {
|
||||
|
||||
private final JTabbedPane jTabbedPane = new JTabbedPane();
|
||||
|
||||
public Editor(MontoyaApi api, EditorCreationContext creationContext) {
|
||||
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
|
||||
this.api = api;
|
||||
this.configLoader = configLoader;
|
||||
this.creationContext = creationContext;
|
||||
this.messageProcessor = new MessageProcessor(api);
|
||||
}
|
||||
@@ -51,7 +56,7 @@ public class WebSocketEditor implements WebSocketMessageEditorProvider {
|
||||
@Override
|
||||
public void setMessage(WebSocketMessage webSocketMessage) {
|
||||
this.message = webSocketMessage.payload();
|
||||
RequestEditor.generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
|
||||
RequestEditor.generateTabbedPaneFromResultMap(api, configLoader, jTabbedPane, this.dataList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -48,14 +48,16 @@ public class HttpMessageHandler implements HttpHandler {
|
||||
|
||||
try {
|
||||
httpRequest.set(httpRequestToBeSent);
|
||||
|
||||
host.set(StringProcessor.getHostByUrl(httpRequestToBeSent.url()));
|
||||
|
||||
String[] hostList = configLoader.getBlockHost().split("\\|");
|
||||
boolean isBlockHost = RequestEditor.isBlockHost(hostList, host.get());
|
||||
|
||||
String toolType = httpRequestToBeSent.toolSource().toolType().toolName();
|
||||
boolean isToolScope = configLoader.getScope().contains(toolType);
|
||||
|
||||
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
||||
matches.set(suffixList.contains(httpRequestToBeSent.fileExtension().toLowerCase()) || isBlockHost);
|
||||
matches.set(suffixList.contains(httpRequestToBeSent.fileExtension().toLowerCase()) || isBlockHost || !isToolScope);
|
||||
|
||||
if (!matches.get()) {
|
||||
List<Map<String, String>> result = messageProcessor.processRequest(host.get(), httpRequestToBeSent, true);
|
||||
|
||||
@@ -134,40 +134,80 @@ public class ConfigLoader {
|
||||
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() {
|
||||
File yamlSetting = new File(configFilePath);
|
||||
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
|
||||
return Config.host;
|
||||
}
|
||||
|
||||
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
|
||||
Map<String, Object> r = new Yaml().load(inorder);
|
||||
|
||||
if (r.containsKey("blockHost")) {
|
||||
return r.get("blockHost").toString();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return Config.host;
|
||||
return getValueFromConfig("blockHost", Config.host);
|
||||
}
|
||||
|
||||
public String getExcludeSuffix() {
|
||||
return getValueFromConfig("excludeSuffix", Config.suffix);
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return getValueFromConfig("HaEScope", Config.scopeOptions);
|
||||
}
|
||||
|
||||
private String getValueFromConfig(String name, String value) {
|
||||
File yamlSetting = new File(configFilePath);
|
||||
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
|
||||
return Config.suffix;
|
||||
return value;
|
||||
}
|
||||
|
||||
try (InputStream inorder = Files.newInputStream(Paths.get(configFilePath))) {
|
||||
Map<String, Object> r = new Yaml().load(inorder);
|
||||
|
||||
if (r.containsKey("excludeSuffix")) {
|
||||
return r.get("excludeSuffix").toString();
|
||||
if (r.containsKey(name)) {
|
||||
return r.get(name).toString();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return Config.suffix;
|
||||
return value;
|
||||
}
|
||||
|
||||
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) {
|
||||
setValueToConfig("excludeSuffix", excludeSuffix);
|
||||
}
|
||||
|
||||
public void setBlockHost(String blockHost) {
|
||||
setValueToConfig("blockHost", blockHost);
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
setValueToConfig("HaEScope", scope);
|
||||
}
|
||||
|
||||
private void setValueToConfig(String name, String value) {
|
||||
Map<String, Object> currentConfig = loadCurrentConfig();
|
||||
currentConfig.put(name, value);
|
||||
|
||||
try (Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8)) {
|
||||
yaml.dump(currentConfig, ws);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> loadCurrentConfig() {
|
||||
@@ -183,26 +223,6 @@ public class ConfigLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public void setExcludeSuffix(String excludeSuffix) {
|
||||
Map<String, Object> currentConfig = loadCurrentConfig();
|
||||
currentConfig.put("excludeSuffix", excludeSuffix); // 更新配置
|
||||
|
||||
try (Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8)) {
|
||||
yaml.dump(currentConfig, ws);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlockHost(String blockHost) {
|
||||
Map<String, Object> currentConfig = loadCurrentConfig();
|
||||
currentConfig.put("blockHost", blockHost); // 更新配置
|
||||
|
||||
try (Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8)) {
|
||||
yaml.dump(currentConfig, ws);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public void initRulesByRes() {
|
||||
boolean isCopySuccess = copyRulesToFile(this.rulesFilePath);
|
||||
if (!isCopySuccess) {
|
||||
|
||||
42
src/main/java/hae/utils/http/HttpUtils.java
Normal file
42
src/main/java/hae/utils/http/HttpUtils.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package hae.utils.http;
|
||||
|
||||
import burp.api.montoya.MontoyaApi;
|
||||
import burp.api.montoya.http.HttpService;
|
||||
import burp.api.montoya.http.message.requests.HttpRequest;
|
||||
import burp.api.montoya.http.message.requests.HttpTransformation;
|
||||
import burp.api.montoya.utilities.RandomUtils;
|
||||
|
||||
public class HttpUtils {
|
||||
private final MontoyaApi api;
|
||||
|
||||
public HttpUtils(MontoyaApi api) {
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
StringBuilder newBody = new StringBuilder();
|
||||
newBody.append(String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n\r\n%s\r\n", boundary, name, filename, content));
|
||||
newBody.append(String.format("--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", boundary, "purpose", "file-extract"));
|
||||
newBody.append("--").append(boundary).append("--\r\n");
|
||||
|
||||
baseRequest = baseRequest.withUpdatedHeader("Content-Type", "multipart/form-data; boundary=" + boundary).withBody(newBody.toString());
|
||||
|
||||
return baseRequest;
|
||||
}
|
||||
|
||||
public HttpRequest generateRequestByJsonMethod(String url, String data) {
|
||||
HttpRequest baseRequest = HttpRequest.httpRequestFromUrl(url).withTransformationApplied(HttpTransformation.TOGGLE_METHOD);
|
||||
HttpService baseService = baseRequest.httpService();
|
||||
String requestString = baseRequest.toString().replace("application/x-www-form-urlencoded", "application/json");
|
||||
baseRequest = HttpRequest.httpRequest(baseService, requestString).withBody(data);
|
||||
return baseRequest;
|
||||
}
|
||||
|
||||
public HttpRequest generateRequestByDeleteMethod(String url) {
|
||||
return HttpRequest.httpRequestFromUrl(url).withMethod("DELETE");
|
||||
}
|
||||
}
|
||||
@@ -1,284 +1,284 @@
|
||||
rules:
|
||||
- group: Fingerprint
|
||||
rule:
|
||||
- name: Shiro
|
||||
loaded: true
|
||||
f_regex: (=deleteMe|rememberMe=)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: any header
|
||||
engine: dfa
|
||||
sensitive: true
|
||||
- name: JSON Web Token
|
||||
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,})
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: any
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: Swagger UI
|
||||
loaded: true
|
||||
f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: red
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: Ueditor
|
||||
loaded: true
|
||||
f_regex: (ueditor\.(config|all)\.js)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: Druid
|
||||
loaded: true
|
||||
f_regex: (Druid Stat Index)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: orange
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- group: Maybe Vulnerability
|
||||
rule:
|
||||
- name: Java Deserialization
|
||||
loaded: true
|
||||
f_regex: (javax\.faces\.ViewState)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: Debug Logic Parameters
|
||||
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=))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: cyan
|
||||
scope: request
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: URL As A Value
|
||||
loaded: true
|
||||
f_regex: (=(https?)(://|%3a%2f%2f))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: cyan
|
||||
scope: any
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Upload Form
|
||||
loaded: true
|
||||
f_regex: (type\=\"file\")
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: DoS Paramters
|
||||
loaded: true
|
||||
f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: cyan
|
||||
scope: request
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- group: Basic Information
|
||||
rule:
|
||||
- name: Email
|
||||
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}))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Chinese IDCard
|
||||
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]'
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: orange
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: Chinese Mobile Number
|
||||
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]'
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: orange
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Internal IP Address
|
||||
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}))'
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: cyan
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: MAC Address
|
||||
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}))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- group: Sensitive Information
|
||||
rule:
|
||||
- name: Cloud Key
|
||||
loaded: true
|
||||
f_regex: (((access)(|-|_)(key)(|-|_)(id|secret))|(LTAI[a-z0-9]{12,20}))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: any
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Windows File/Dir Path
|
||||
loaded: true
|
||||
f_regex: '[^\w](([a-zA-Z]:\\(?:\w+\\?)*)|([a-zA-Z]:\\(?:\w+\\)*\w+\.\w+))'
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: Password Field
|
||||
loaded: true
|
||||
f_regex: ((|'|")(|[\w]{1,10})([p](ass|wd|asswd|assword))(|[\w]{1,10})(|'|")(:|=)(
|
||||
|)('|")(.*?)('|")(|,))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Username Field
|
||||
loaded: true
|
||||
f_regex: ((|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|'|")(:|=)(
|
||||
|)('|")(.*?)('|")(|,))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: WeCom Key
|
||||
loaded: true
|
||||
f_regex: ((corp)(id|secret))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: JDBC Connection
|
||||
loaded: true
|
||||
f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: any
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Authorization Header
|
||||
loaded: true
|
||||
f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100}))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Sensitive Field
|
||||
loaded: true
|
||||
f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin))([\w]{0,10})('|")?(\])?(
|
||||
|)(:|=)( |)('|")(.*?)('|")(|,))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- group: Other
|
||||
rule:
|
||||
- name: Linkfinder
|
||||
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: ''
|
||||
format: '{0}'
|
||||
color: gray
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: Source Map
|
||||
loaded: true
|
||||
f_regex: (\.js\.map)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: pink
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: HTML Notes
|
||||
loaded: true
|
||||
f_regex: (<!--.*?-->)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: magenta
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Create Script
|
||||
loaded: true
|
||||
f_regex: (\+\{.*?\}\[[a-zA-Z]\]\+".*?\.js")
|
||||
s_regex: '"?([\w].*?)"?:"(.*?)"'
|
||||
format: '{0}.{1}'
|
||||
color: green
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: URL Schemes
|
||||
loaded: true
|
||||
f_regex: ((?![http]|[https])(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Router Push
|
||||
loaded: true
|
||||
f_regex: (\$router\.push)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: magenta
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: All URL
|
||||
loaded: true
|
||||
f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|])
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: gray
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- group: Fingerprint
|
||||
rule:
|
||||
- name: Shiro
|
||||
loaded: true
|
||||
f_regex: (=deleteMe|rememberMe=)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: any header
|
||||
engine: dfa
|
||||
sensitive: true
|
||||
- name: JSON Web Token
|
||||
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,})
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: any
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: Swagger UI
|
||||
loaded: true
|
||||
f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: red
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: Ueditor
|
||||
loaded: true
|
||||
f_regex: (ueditor\.(config|all)\.js)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: Druid
|
||||
loaded: true
|
||||
f_regex: (Druid Stat Index)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: orange
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- group: Maybe Vulnerability
|
||||
rule:
|
||||
- name: Java Deserialization
|
||||
loaded: true
|
||||
f_regex: (javax\.faces\.ViewState)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: Debug Logic Parameters
|
||||
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=))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: cyan
|
||||
scope: request
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: URL As A Value
|
||||
loaded: true
|
||||
f_regex: (=(https?)(://|%3a%2f%2f))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: cyan
|
||||
scope: any
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Upload Form
|
||||
loaded: true
|
||||
f_regex: (type\=\"file\")
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: DoS Paramters
|
||||
loaded: true
|
||||
f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: cyan
|
||||
scope: request
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- group: Basic Information
|
||||
rule:
|
||||
- name: Email
|
||||
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}))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Chinese IDCard
|
||||
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]'
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: orange
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: Chinese Mobile Number
|
||||
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]'
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: orange
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Internal IP Address
|
||||
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}))'
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: cyan
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: MAC Address
|
||||
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}))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- group: Sensitive Information
|
||||
rule:
|
||||
- name: Cloud Key
|
||||
loaded: true
|
||||
f_regex: (((access)(|-|_)(key)(|-|_)(id|secret))|(LTAI[a-z0-9]{12,20}))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: any
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Windows File/Dir Path
|
||||
loaded: true
|
||||
f_regex: '[^\w](([a-zA-Z]:\\(?:\w+\\?)*)|([a-zA-Z]:\\(?:\w+\\)*\w+\.\w+))'
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: Password Field
|
||||
loaded: true
|
||||
f_regex: ((|'|")(|[\w]{1,10})([p](ass|wd|asswd|assword))(|[\w]{1,10})(|'|")(:|=)(
|
||||
|)('|")(.*?)('|")(|,))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Username Field
|
||||
loaded: true
|
||||
f_regex: ((|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|'|")(:|=)(
|
||||
|)('|")(.*?)('|")(|,))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: WeCom Key
|
||||
loaded: true
|
||||
f_regex: ((corp)(id|secret))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: green
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: JDBC Connection
|
||||
loaded: true
|
||||
f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: any
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Authorization Header
|
||||
loaded: true
|
||||
f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100}))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Sensitive Field
|
||||
loaded: true
|
||||
f_regex: ((\[)?('|")?([\w]{0,10})((key)|(secret)|(token)|(config)|(auth)|(access)|(admin))([\w]{0,10})('|")?(\])?(
|
||||
|)(:|=)( |)('|")(.*?)('|")(|,))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- group: Other
|
||||
rule:
|
||||
- name: Linkfinder
|
||||
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: ''
|
||||
format: '{0}'
|
||||
color: gray
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
- name: Source Map
|
||||
loaded: true
|
||||
f_regex: (\.js\.map)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: pink
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: HTML Notes
|
||||
loaded: true
|
||||
f_regex: (<!--.*?-->)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: magenta
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Create Script
|
||||
loaded: true
|
||||
f_regex: (\+\{.*?\}\[[a-zA-Z]\]\+".*?\.js")
|
||||
s_regex: '"?([\w].*?)"?:"(.*?)"'
|
||||
format: '{0}.{1}'
|
||||
color: green
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: URL Schemes
|
||||
loaded: true
|
||||
f_regex: ((?![http]|[https])(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]))
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: yellow
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: false
|
||||
- name: Router Push
|
||||
loaded: true
|
||||
f_regex: (\$router\.push)
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: magenta
|
||||
scope: response body
|
||||
engine: dfa
|
||||
sensitive: false
|
||||
- name: All URL
|
||||
loaded: true
|
||||
f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|])
|
||||
s_regex: ''
|
||||
format: '{0}'
|
||||
color: gray
|
||||
scope: response body
|
||||
engine: nfa
|
||||
sensitive: true
|
||||
|
||||
Reference in New Issue
Block a user