Version: 4.3 Update
Signed-off-by: gh0stkey <24655118+gh0stkey@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package hae;
|
|||||||
|
|
||||||
import burp.api.montoya.BurpExtension;
|
import burp.api.montoya.BurpExtension;
|
||||||
import burp.api.montoya.MontoyaApi;
|
import burp.api.montoya.MontoyaApi;
|
||||||
|
import burp.api.montoya.core.BurpSuiteEdition;
|
||||||
import burp.api.montoya.logging.Logging;
|
import burp.api.montoya.logging.Logging;
|
||||||
import hae.cache.DataCache;
|
import hae.cache.DataCache;
|
||||||
import hae.component.Main;
|
import hae.component.Main;
|
||||||
@@ -9,7 +10,6 @@ import hae.component.board.message.MessageTableModel;
|
|||||||
import hae.instances.editor.RequestEditor;
|
import hae.instances.editor.RequestEditor;
|
||||||
import hae.instances.editor.ResponseEditor;
|
import hae.instances.editor.ResponseEditor;
|
||||||
import hae.instances.editor.WebSocketEditor;
|
import hae.instances.editor.WebSocketEditor;
|
||||||
import hae.instances.http.HttpMessagePassiveHandler;
|
|
||||||
import hae.instances.websocket.WebSocketMessageHandler;
|
import hae.instances.websocket.WebSocketMessageHandler;
|
||||||
import hae.utils.ConfigLoader;
|
import hae.utils.ConfigLoader;
|
||||||
import hae.utils.DataManager;
|
import hae.utils.DataManager;
|
||||||
@@ -19,7 +19,7 @@ public class HaE implements BurpExtension {
|
|||||||
public void initialize(MontoyaApi api) {
|
public void initialize(MontoyaApi api) {
|
||||||
// 设置扩展名称
|
// 设置扩展名称
|
||||||
api.extension().setName("HaE - Highlighter and Extractor");
|
api.extension().setName("HaE - Highlighter and Extractor");
|
||||||
String version = "4.2.1";
|
String version = "4.3";
|
||||||
|
|
||||||
// 加载扩展后输出的项目信息
|
// 加载扩展后输出的项目信息
|
||||||
Logging logging = api.logging();
|
Logging logging = api.logging();
|
||||||
@@ -34,7 +34,7 @@ public class HaE implements BurpExtension {
|
|||||||
MessageTableModel messageTableModel = new MessageTableModel(api, configLoader);
|
MessageTableModel messageTableModel = new MessageTableModel(api, configLoader);
|
||||||
|
|
||||||
// 设置BurpSuite专业版状态
|
// 设置BurpSuite专业版状态
|
||||||
Config.proVersionStatus = getBurpSuiteProStatus(api, configLoader, messageTableModel);
|
Config.proVersionStatus = getBurpSuiteProStatus(api);
|
||||||
|
|
||||||
// 注册Tab页(用于查询数据)
|
// 注册Tab页(用于查询数据)
|
||||||
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
|
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
|
||||||
@@ -58,17 +58,13 @@ public class HaE implements BurpExtension {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean getBurpSuiteProStatus(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
private Boolean getBurpSuiteProStatus(MontoyaApi api) {
|
||||||
boolean burpSuiteProStatus = false;
|
boolean burpSuiteProStatus = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
burpSuiteProStatus = api.burpSuite().version().edition().displayName().equals("Professional");
|
burpSuiteProStatus = api.burpSuite().version().edition() == BurpSuiteEdition.PROFESSIONAL;
|
||||||
} catch (Exception e) {
|
|
||||||
try {
|
|
||||||
api.scanner().registerScanCheck(new HttpMessagePassiveHandler(api, configLoader, messageTableModel)).deregister();
|
|
||||||
burpSuiteProStatus = true;
|
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return burpSuiteProStatus;
|
return burpSuiteProStatus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class Databoard extends JPanel {
|
|||||||
private JSplitPane splitPane;
|
private JSplitPane splitPane;
|
||||||
private MessageTable messageTable;
|
private MessageTable messageTable;
|
||||||
private JProgressBar progressBar;
|
private JProgressBar progressBar;
|
||||||
private SwingWorker<Map<String, List<String>>, Void> handleComboBoxWorker;
|
private SwingWorker<Map<String, List<String>>, Integer> handleComboBoxWorker;
|
||||||
private SwingWorker<Void, Void> applyHostFilterWorker;
|
private SwingWorker<Void, Void> applyHostFilterWorker;
|
||||||
|
|
||||||
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
public Databoard(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
||||||
@@ -125,16 +125,16 @@ 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) {
|
private void setProgressBar(boolean status, String message, int progress) {
|
||||||
progressBar.setIndeterminate(status);
|
progressBar.setIndeterminate(status && progress <= 0);
|
||||||
if (!status) {
|
progressBar.setString(message);
|
||||||
|
progressBar.setStringPainted(true);
|
||||||
progressBar.setMaximum(100);
|
progressBar.setMaximum(100);
|
||||||
progressBar.setString("OK");
|
|
||||||
progressBar.setStringPainted(true);
|
if (progress > 0) {
|
||||||
|
progressBar.setValue(progress);
|
||||||
|
} else if (!status) {
|
||||||
progressBar.setValue(progressBar.getMaximum());
|
progressBar.setValue(progressBar.getMaximum());
|
||||||
} else {
|
|
||||||
progressBar.setString("Loading...");
|
|
||||||
progressBar.setStringPainted(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,54 +173,15 @@ 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);
|
||||||
|
hostComboBox.setPopupVisible(false);
|
||||||
|
|
||||||
if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) {
|
if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) {
|
||||||
|
progressBar.setVisible(false);
|
||||||
handleComboBoxWorker.cancel(true);
|
handleComboBoxWorker.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleComboBoxWorker = new SwingWorker<>() {
|
handleComboBoxWorker = new DataLoadingWorker(selectedHost);
|
||||||
@Override
|
|
||||||
protected Map<String, List<String>> doInBackground() {
|
|
||||||
return getSelectedMapByHost(selectedHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
if (!isCancelled()) {
|
|
||||||
try {
|
|
||||||
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);
|
|
||||||
messageTable = messageTableModel.getMessageTable();
|
|
||||||
resizePanel();
|
|
||||||
|
|
||||||
splitPane.setVisible(true);
|
|
||||||
hostTextField.setText(selectedHost);
|
|
||||||
|
|
||||||
hostComboBox.setPopupVisible(false);
|
|
||||||
applyHostFilter(selectedHost);
|
|
||||||
|
|
||||||
setProgressBar(false);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleComboBoxWorker.execute();
|
handleComboBoxWorker.execute();
|
||||||
}
|
}
|
||||||
@@ -251,33 +212,57 @@ public class Databoard extends JPanel {
|
|||||||
isMatchHost = false;
|
isMatchHost = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, List<String>> getSelectedMapByHost(String selectedHost) {
|
private Map<String, List<String>> getSelectedMapByHost(String selectedHost, DataLoadingWorker worker) {
|
||||||
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
|
ConcurrentHashMap<String, Map<String, List<String>>> dataMap = Config.globalDataMap;
|
||||||
Map<String, List<String>> selectedDataMap;
|
Map<String, List<String>> selectedDataMap;
|
||||||
|
|
||||||
if (selectedHost.contains("*")) {
|
if (selectedHost.contains("*")) {
|
||||||
selectedDataMap = new HashMap<>();
|
selectedDataMap = new HashMap<>();
|
||||||
dataMap.keySet().forEach(key -> {
|
List<String> matchingKeys = new ArrayList<>();
|
||||||
|
|
||||||
|
// 第一步:找出所有匹配的键(预处理)
|
||||||
|
for (String key : dataMap.keySet()) {
|
||||||
if ((StringProcessor.matchesHostPattern(key, selectedHost) || selectedHost.equals("*")) && !key.contains("*")) {
|
if ((StringProcessor.matchesHostPattern(key, selectedHost) || selectedHost.equals("*")) && !key.contains("*")) {
|
||||||
|
matchingKeys.add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二步:分批处理数据
|
||||||
|
int totalKeys = matchingKeys.size();
|
||||||
|
for (int i = 0; i < totalKeys; i++) {
|
||||||
|
String key = matchingKeys.get(i);
|
||||||
Map<String, List<String>> ruleMap = dataMap.get(key);
|
Map<String, List<String>> ruleMap = dataMap.get(key);
|
||||||
|
|
||||||
|
if (ruleMap != null) {
|
||||||
for (String ruleKey : ruleMap.keySet()) {
|
for (String ruleKey : ruleMap.keySet()) {
|
||||||
List<String> dataList = ruleMap.get(ruleKey);
|
List<String> dataList = ruleMap.get(ruleKey);
|
||||||
if (selectedDataMap.containsKey(ruleKey)) {
|
if (selectedDataMap.containsKey(ruleKey)) {
|
||||||
List<String> mergedList = new ArrayList<>(selectedDataMap.get(ruleKey));
|
List<String> mergedList = new ArrayList<>(selectedDataMap.get(ruleKey));
|
||||||
mergedList.addAll(dataList);
|
mergedList.addAll(dataList);
|
||||||
|
// 使用HashSet去重
|
||||||
HashSet<String> uniqueSet = new HashSet<>(mergedList);
|
HashSet<String> uniqueSet = new HashSet<>(mergedList);
|
||||||
selectedDataMap.put(ruleKey, new ArrayList<>(uniqueSet));
|
selectedDataMap.put(ruleKey, new ArrayList<>(uniqueSet));
|
||||||
} else {
|
} else {
|
||||||
selectedDataMap.put(ruleKey, dataList);
|
selectedDataMap.put(ruleKey, new ArrayList<>(dataList));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} else {
|
|
||||||
selectedDataMap = dataMap.get(selectedHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
return selectedDataMap;
|
// 报告进度
|
||||||
|
if (worker != null && i % 5 == 0) {
|
||||||
|
int progress = (int) ((i + 1) * 90.0 / totalKeys);
|
||||||
|
worker.publishProgress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedDataMap = dataMap.get(selectedHost);
|
||||||
|
// 对于非通配符匹配,直接返回结果
|
||||||
|
if (worker != null) {
|
||||||
|
worker.publishProgress(90);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedDataMap != null ? selectedDataMap : new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void filterComboBoxList() {
|
private void filterComboBoxList() {
|
||||||
@@ -395,4 +380,67 @@ public class Databoard extends JPanel {
|
|||||||
hostTextField.setText("");
|
hostTextField.setText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 定义为内部类
|
||||||
|
private class DataLoadingWorker extends SwingWorker<Map<String, List<String>>, Integer> {
|
||||||
|
private final String selectedHost;
|
||||||
|
|
||||||
|
public DataLoadingWorker(String selectedHost) {
|
||||||
|
this.selectedHost = selectedHost;
|
||||||
|
progressBar.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, List<String>> doInBackground() throws Exception {
|
||||||
|
return getSelectedMapByHost(selectedHost, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void process(List<Integer> chunks) {
|
||||||
|
if (!chunks.isEmpty()) {
|
||||||
|
int progress = chunks.get(chunks.size() - 1);
|
||||||
|
setProgressBar(true, "Loading... " + progress + "%", progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
if (!isCancelled()) {
|
||||||
|
try {
|
||||||
|
Map<String, List<String>> selectedDataMap = get();
|
||||||
|
if (selectedDataMap != null && !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);
|
||||||
|
messageTable = messageTableModel.getMessageTable();
|
||||||
|
resizePanel();
|
||||||
|
|
||||||
|
splitPane.setVisible(true);
|
||||||
|
|
||||||
|
applyHostFilter(selectedHost);
|
||||||
|
setProgressBar(false, "OK", 100);
|
||||||
|
} else {
|
||||||
|
setProgressBar(false, "Error", 0);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
api.logging().logToOutput("DataLoadingWorker: " + e.getMessage());
|
||||||
|
setProgressBar(false, "Error", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提供一个公共方法来发布进度
|
||||||
|
public void publishProgress(int progress) {
|
||||||
|
publish(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,15 +33,32 @@ public class MessageRenderer extends DefaultTableCellRenderer {
|
|||||||
boolean hasFocus, int row, int column) {
|
boolean hasFocus, int row, int column) {
|
||||||
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
|
||||||
MessageEntry messageEntry = log.get(table.convertRowIndexToModel(row)); // 使用convertRowIndexToModel方法转换行索引
|
// 添加边界检查以防止IndexOutOfBoundsException
|
||||||
|
int modelRow = table.convertRowIndexToModel(row);
|
||||||
|
if (modelRow < 0 || modelRow >= log.size()) {
|
||||||
|
// 如果索引无效,返回默认渲染组件(使用默认背景色)
|
||||||
|
component.setBackground(Color.WHITE);
|
||||||
|
component.setForeground(Color.BLACK);
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageEntry messageEntry = log.get(modelRow);
|
||||||
|
|
||||||
// 设置颜色
|
// 设置颜色
|
||||||
String colorByLog = messageEntry.getColor();
|
String colorByLog = messageEntry.getColor();
|
||||||
Color color = colorMap.get(colorByLog);
|
Color color = colorMap.get(colorByLog);
|
||||||
|
|
||||||
|
// 如果颜色映射中没有找到对应颜色,使用默认白色
|
||||||
|
if (color == null) {
|
||||||
|
color = Color.WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
// 通过更改RGB颜色来达成阴影效果
|
// 通过更改RGB颜色来达成阴影效果
|
||||||
component.setBackground(new Color(color.getRed() - 0x20, color.getGreen() - 0x20, color.getBlue() - 0x20));
|
int red = Math.max(0, color.getRed() - 0x20);
|
||||||
|
int green = Math.max(0, color.getGreen() - 0x20);
|
||||||
|
int blue = Math.max(0, color.getBlue() - 0x20);
|
||||||
|
component.setBackground(new Color(red, green, blue));
|
||||||
} else {
|
} else {
|
||||||
// 否则使用原始颜色
|
// 否则使用原始颜色
|
||||||
component.setBackground(color);
|
component.setBackground(color);
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
messageTable.setDefaultRenderer(Object.class, new MessageRenderer(filteredLog, messageTable));
|
messageTable.setDefaultRenderer(Object.class, new MessageRenderer(filteredLog, messageTable));
|
||||||
messageTable.setAutoCreateRowSorter(true);
|
messageTable.setAutoCreateRowSorter(true);
|
||||||
|
|
||||||
// Length字段根据大小进行排序
|
|
||||||
TableRowSorter<DefaultTableModel> sorter = getDefaultTableModelTableRowSorter();
|
TableRowSorter<DefaultTableModel> sorter = getDefaultTableModelTableRowSorter();
|
||||||
messageTable.setRowSorter(sorter);
|
messageTable.setRowSorter(sorter);
|
||||||
messageTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
messageTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||||
@@ -71,6 +70,8 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
|
|
||||||
private TableRowSorter<DefaultTableModel> getDefaultTableModelTableRowSorter() {
|
private TableRowSorter<DefaultTableModel> getDefaultTableModelTableRowSorter() {
|
||||||
TableRowSorter<DefaultTableModel> sorter = (TableRowSorter<DefaultTableModel>) messageTable.getRowSorter();
|
TableRowSorter<DefaultTableModel> sorter = (TableRowSorter<DefaultTableModel>) messageTable.getRowSorter();
|
||||||
|
|
||||||
|
// Length字段根据大小进行排序
|
||||||
sorter.setComparator(4, (Comparator<String>) (s1, s2) -> {
|
sorter.setComparator(4, (Comparator<String>) (s1, s2) -> {
|
||||||
Integer age1 = Integer.parseInt(s1);
|
Integer age1 = Integer.parseInt(s1);
|
||||||
Integer age2 = Integer.parseInt(s2);
|
Integer age2 = Integer.parseInt(s2);
|
||||||
@@ -104,6 +105,14 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (comment == null || comment.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color == null || color.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean isDuplicate = false;
|
boolean isDuplicate = false;
|
||||||
try {
|
try {
|
||||||
if (!log.isEmpty() && flag) {
|
if (!log.isEmpty() && flag) {
|
||||||
@@ -241,49 +250,66 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void applyHostFilter(String filterText) {
|
public void applyHostFilter(String filterText) {
|
||||||
filteredLog.clear();
|
// 预分配合适的容量,避免频繁扩容
|
||||||
fireTableDataChanged();
|
final List<MessageEntry> newFilteredLog = new ArrayList<>(log.size() / 2);
|
||||||
|
|
||||||
int batchSize = 500;
|
// 预处理过滤条件,优化性能
|
||||||
|
final boolean isWildcardFilter = "*".equals(filterText) || filterText.contains("*");
|
||||||
|
final String normalizedFilter = filterText.toLowerCase().trim();
|
||||||
|
|
||||||
// 分批处理数据
|
// 创建log的安全副本
|
||||||
List<MessageEntry> batch = new ArrayList<>(batchSize);
|
final List<MessageEntry> logSnapshot;
|
||||||
int count = 0;
|
synchronized (log) {
|
||||||
|
logSnapshot = new ArrayList<>(log);
|
||||||
|
}
|
||||||
|
|
||||||
for (MessageEntry entry : log) {
|
// 使用并行流高效过滤,但保持有序
|
||||||
|
logSnapshot.parallelStream()
|
||||||
|
.filter(entry -> {
|
||||||
|
// 快速通配符检查
|
||||||
|
if (isWildcardFilter && "*".equals(filterText)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
String host = StringProcessor.getHostByUrl(entry.getUrl());
|
String host = StringProcessor.getHostByUrl(entry.getUrl());
|
||||||
if (!host.isEmpty() && (StringProcessor.matchesHostPattern(host, filterText) || filterText.contains("*"))) {
|
if (host.isEmpty()) {
|
||||||
batch.add(entry);
|
return false;
|
||||||
count++;
|
|
||||||
|
|
||||||
// 当批次达到指定大小时,更新UI
|
|
||||||
if (count % batchSize == 0) {
|
|
||||||
final List<MessageEntry> currentBatch = new ArrayList<>(batch);
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
filteredLog.addAll(currentBatch);
|
|
||||||
fireTableDataChanged();
|
|
||||||
});
|
|
||||||
batch.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理最后一批
|
// 优化后的匹配逻辑
|
||||||
if (!batch.isEmpty()) {
|
return StringProcessor.matchesHostPattern(host, filterText) ||
|
||||||
final List<MessageEntry> finalBatch = new ArrayList<>(batch);
|
(isWildcardFilter && host.toLowerCase().contains(normalizedFilter.replace("*", "")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.forEachOrdered(newFilteredLog::add);
|
||||||
|
|
||||||
|
// 一次性更新UI,避免频繁刷新
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
filteredLog.addAll(finalBatch);
|
synchronized (filteredLog) {
|
||||||
|
filteredLog.clear();
|
||||||
|
filteredLog.addAll(newFilteredLog);
|
||||||
|
}
|
||||||
fireTableDataChanged();
|
fireTableDataChanged();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void applyMessageFilter(String tableName, String filterText) {
|
public void applyMessageFilter(String tableName, String filterText) {
|
||||||
filteredLog.clear();
|
List<MessageEntry> newFilteredLog = new ArrayList<>();
|
||||||
for (MessageEntry entry : log) {
|
|
||||||
|
// 创建log的安全副本以避免ConcurrentModificationException
|
||||||
|
List<MessageEntry> logSnapshot;
|
||||||
|
synchronized (log) {
|
||||||
|
logSnapshot = new ArrayList<>(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MessageEntry entry : logSnapshot) {
|
||||||
// 标志变量,表示是否满足过滤条件
|
// 标志变量,表示是否满足过滤条件
|
||||||
AtomicBoolean isMatched = new AtomicBoolean(false);
|
AtomicBoolean isMatched = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
try {
|
||||||
HttpRequestResponse requestResponse = entry.getRequestResponse();
|
HttpRequestResponse requestResponse = entry.getRequestResponse();
|
||||||
HttpRequest httpRequest = requestResponse.request();
|
HttpRequest httpRequest = requestResponse.request();
|
||||||
HttpResponse httpResponse = requestResponse.response();
|
HttpResponse httpResponse = requestResponse.response();
|
||||||
@@ -359,13 +385,28 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 由于每个用户规则不同,如果进行项目文件共享则需要考虑全部匹配一下
|
||||||
|
if (!isMatched.get()) {
|
||||||
|
isMatched.set(matchingString("{0}", filterText, requestString) || matchingString("{0}", filterText, responseString));
|
||||||
|
}
|
||||||
|
|
||||||
if (isMatched.get()) {
|
if (isMatched.get()) {
|
||||||
filteredLog.add(entry);
|
newFilteredLog.add(entry);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 在EDT线程中更新UI
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
synchronized (filteredLog) {
|
||||||
|
filteredLog.clear();
|
||||||
|
filteredLog.addAll(newFilteredLog);
|
||||||
|
}
|
||||||
fireTableDataChanged();
|
fireTableDataChanged();
|
||||||
messageTable.lastSelectedIndex = -1;
|
messageTable.lastSelectedIndex = -1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean matchingString(String format, String filterText, String target) {
|
private boolean matchingString(String format, String filterText, String target) {
|
||||||
@@ -398,8 +439,10 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRowCount() {
|
public int getRowCount() {
|
||||||
|
synchronized (filteredLog) {
|
||||||
return filteredLog.size();
|
return filteredLog.size();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getColumnCount() {
|
public int getColumnCount() {
|
||||||
@@ -408,11 +451,17 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
if (!filteredLog.isEmpty()) {
|
synchronized (filteredLog) {
|
||||||
|
if (rowIndex < 0 || rowIndex >= filteredLog.size()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MessageEntry messageEntry = filteredLog.get(rowIndex);
|
MessageEntry messageEntry = filteredLog.get(rowIndex);
|
||||||
|
if (messageEntry == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
if (messageEntry != null) {
|
|
||||||
return switch (columnIndex) {
|
return switch (columnIndex) {
|
||||||
case 0 -> messageEntry.getMethod();
|
case 0 -> messageEntry.getMethod();
|
||||||
case 1 -> messageEntry.getUrl();
|
case 1 -> messageEntry.getUrl();
|
||||||
@@ -422,14 +471,12 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
case 5 -> messageEntry.getColor();
|
case 5 -> messageEntry.getColor();
|
||||||
default -> "";
|
default -> "";
|
||||||
};
|
};
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
api.logging().logToError("getValueAt: " + e.getMessage());
|
api.logging().logToError("getValueAt: " + e.getMessage());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnName(int columnIndex) {
|
public String getColumnName(int columnIndex) {
|
||||||
|
|||||||
@@ -7,9 +7,13 @@ import hae.utils.rule.RuleProcessor;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.DefaultTableModel;
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
import javax.swing.table.JTableHeader;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
import javax.swing.table.TableRowSorter;
|
import javax.swing.table.TableRowSorter;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import static javax.swing.JOptionPane.YES_OPTION;
|
import static javax.swing.JOptionPane.YES_OPTION;
|
||||||
@@ -19,6 +23,7 @@ public class Rule extends JPanel {
|
|||||||
private final ConfigLoader configLoader;
|
private final ConfigLoader configLoader;
|
||||||
private final RuleProcessor ruleProcessor;
|
private final RuleProcessor ruleProcessor;
|
||||||
private final JTabbedPane tabbedPane;
|
private final JTabbedPane tabbedPane;
|
||||||
|
private JCheckBox headerCheckBox;
|
||||||
|
|
||||||
public Rule(MontoyaApi api, ConfigLoader configLoader, Object[][] data, JTabbedPane tabbedPane) {
|
public Rule(MontoyaApi api, ConfigLoader configLoader, Object[][] data, JTabbedPane tabbedPane) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
@@ -36,6 +41,7 @@ public class Rule extends JPanel {
|
|||||||
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 1.0, 1.0E-4};
|
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 1.0, 1.0E-4};
|
||||||
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 0.0, 0.0, 1.0, 1.0E-4};
|
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 0.0, 0.0, 1.0, 1.0E-4};
|
||||||
|
|
||||||
|
JButton copyButton = new JButton("Copy");
|
||||||
JButton addButton = new JButton("Add");
|
JButton addButton = new JButton("Add");
|
||||||
JButton editButton = new JButton("Edit");
|
JButton editButton = new JButton("Edit");
|
||||||
JButton removeButton = new JButton("Remove");
|
JButton removeButton = new JButton("Remove");
|
||||||
@@ -43,14 +49,13 @@ public class Rule extends JPanel {
|
|||||||
JTable ruleTable = new JTable();
|
JTable ruleTable = new JTable();
|
||||||
JScrollPane scrollPane = new JScrollPane();
|
JScrollPane scrollPane = new JScrollPane();
|
||||||
|
|
||||||
ruleTable.setShowVerticalLines(false);
|
|
||||||
ruleTable.setShowHorizontalLines(false);
|
|
||||||
ruleTable.setVerifyInputWhenFocusTarget(false);
|
ruleTable.setVerifyInputWhenFocusTarget(false);
|
||||||
ruleTable.setUpdateSelectionOnSort(false);
|
ruleTable.setUpdateSelectionOnSort(false);
|
||||||
ruleTable.setSurrendersFocusOnKeystroke(true);
|
ruleTable.setSurrendersFocusOnKeystroke(true);
|
||||||
scrollPane.setViewportView(ruleTable);
|
scrollPane.setViewportView(ruleTable);
|
||||||
|
|
||||||
// 按钮监听事件
|
// 按钮监听事件
|
||||||
|
copyButton.addActionListener(e -> ruleCopyActionPerformed(e, ruleTable, tabbedPane));
|
||||||
addButton.addActionListener(e -> ruleAddActionPerformed(e, ruleTable, tabbedPane));
|
addButton.addActionListener(e -> ruleAddActionPerformed(e, ruleTable, tabbedPane));
|
||||||
editButton.addActionListener(e -> ruleEditActionPerformed(e, ruleTable, tabbedPane));
|
editButton.addActionListener(e -> ruleEditActionPerformed(e, ruleTable, tabbedPane));
|
||||||
removeButton.addActionListener(e -> ruleRemoveActionPerformed(e, ruleTable, tabbedPane));
|
removeButton.addActionListener(e -> ruleRemoveActionPerformed(e, ruleTable, tabbedPane));
|
||||||
@@ -76,29 +81,216 @@ public class Rule extends JPanel {
|
|||||||
if (e.getColumn() == 0 && ruleTable.getSelectedRow() != -1) {
|
if (e.getColumn() == 0 && ruleTable.getSelectedRow() != -1) {
|
||||||
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
|
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
|
||||||
ruleProcessor.changeRule(model.getDataVector().get(select), select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
|
ruleProcessor.changeRule(model.getDataVector().get(select), select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
|
||||||
|
|
||||||
|
// 更新表头复选框状态并强制重新渲染
|
||||||
|
updateHeaderCheckBoxState(model);
|
||||||
|
ruleTable.getTableHeader().repaint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add(addButton, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
|
// 设置表头复选框
|
||||||
|
setupHeaderCheckBox(ruleTable);
|
||||||
|
|
||||||
|
// 设置Loaded列的宽度(第一列)
|
||||||
|
setupColumnWidths(ruleTable);
|
||||||
|
|
||||||
|
GridBagConstraints constraints = new GridBagConstraints();
|
||||||
|
constraints.weightx = 1.0;
|
||||||
|
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
|
JPanel buttonPanel = new JPanel();
|
||||||
|
GridBagLayout layout = new GridBagLayout();
|
||||||
|
layout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0};
|
||||||
|
layout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
|
||||||
|
buttonPanel.setLayout(layout);
|
||||||
|
|
||||||
|
constraints.insets = new Insets(0, 0, 3, 0);
|
||||||
|
constraints.gridy = 0;
|
||||||
|
buttonPanel.add(copyButton, constraints);
|
||||||
|
constraints.gridy = 1;
|
||||||
|
buttonPanel.add(addButton, constraints);
|
||||||
|
constraints.gridy = 2;
|
||||||
|
buttonPanel.add(editButton, constraints);
|
||||||
|
constraints.gridy = 3;
|
||||||
|
buttonPanel.add(removeButton, constraints);
|
||||||
|
|
||||||
|
add(buttonPanel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
|
||||||
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
||||||
new Insets(15, 5, 3, 2), 0, 0));
|
new Insets(15, 5, 3, 2), 0, 0));
|
||||||
add(editButton, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
|
|
||||||
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
|
||||||
new Insets(0, 5, 3, 2), 0, 0));
|
|
||||||
add(removeButton, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0,
|
|
||||||
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
|
||||||
new Insets(0, 5, 3, 2), 0, 0));
|
|
||||||
add(scrollPane, new GridBagConstraints(1, 0, 1, 4, 0.0, 0.0,
|
add(scrollPane, new GridBagConstraints(1, 0, 1, 4, 0.0, 0.0,
|
||||||
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
||||||
new Insets(15, 5, 5, 5), 0, 0));
|
new Insets(15, 5, 5, 5), 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ruleAddActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
|
/**
|
||||||
Display ruleDisplay = new Display();
|
* 设置列宽度
|
||||||
ruleDisplay.formatTextField.setText("{0}");
|
*/
|
||||||
|
private void setupColumnWidths(JTable ruleTable) {
|
||||||
|
// 设置Loaded列(第一列)的宽度
|
||||||
|
ruleTable.getColumnModel().getColumn(0).setPreferredWidth(50);
|
||||||
|
ruleTable.getColumnModel().getColumn(0).setMaxWidth(50);
|
||||||
|
ruleTable.getColumnModel().getColumn(0).setMinWidth(50);
|
||||||
|
}
|
||||||
|
|
||||||
int showState = JOptionPane.showConfirmDialog(this, ruleDisplay, "Add Rule", JOptionPane.YES_NO_OPTION);
|
/**
|
||||||
if (showState == YES_OPTION) {
|
* 设置表头复选框
|
||||||
|
*/
|
||||||
|
private void setupHeaderCheckBox(JTable ruleTable) {
|
||||||
|
// 创建表头复选框
|
||||||
|
headerCheckBox = new JCheckBox();
|
||||||
|
headerCheckBox.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
|
|
||||||
|
// 设置表头渲染器
|
||||||
|
ruleTable.getTableHeader().setDefaultRenderer(new HeaderCheckBoxRenderer(ruleTable.getTableHeader().getDefaultRenderer()));
|
||||||
|
|
||||||
|
// 添加表头鼠标点击事件
|
||||||
|
ruleTable.getTableHeader().addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
if (e.getClickCount() == 1) {
|
||||||
|
JTableHeader header = (JTableHeader) e.getSource();
|
||||||
|
JTable table = header.getTable();
|
||||||
|
int columnIndex = header.columnAtPoint(e.getPoint());
|
||||||
|
|
||||||
|
if (columnIndex == 0) { // 点击的是Loaded列表头
|
||||||
|
toggleAllRules(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义表头渲染器,在Loaded列显示复选框
|
||||||
|
*/
|
||||||
|
private class HeaderCheckBoxRenderer implements TableCellRenderer {
|
||||||
|
private final TableCellRenderer originalRenderer;
|
||||||
|
|
||||||
|
public HeaderCheckBoxRenderer(TableCellRenderer originalRenderer) {
|
||||||
|
this.originalRenderer = originalRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
if (column == 0) { // Loaded列
|
||||||
|
// 获取原始表头组件作为背景
|
||||||
|
Component originalComponent = originalRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
|
||||||
|
// 创建一个面板来包含复选框,保持原始样式
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
panel.setOpaque(true);
|
||||||
|
|
||||||
|
// 复制原始组件的样式
|
||||||
|
if (originalComponent instanceof JComponent) {
|
||||||
|
JComponent origComp = (JComponent) originalComponent;
|
||||||
|
panel.setBackground(origComp.getBackground());
|
||||||
|
panel.setBorder(origComp.getBorder());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新复选框状态并添加到面板中心
|
||||||
|
updateHeaderCheckBoxState((DefaultTableModel) table.getModel());
|
||||||
|
headerCheckBox.setOpaque(false); // 让复选框透明,显示背景
|
||||||
|
panel.add(headerCheckBox, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
} else {
|
||||||
|
return originalRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换所有规则的开启/关闭状态
|
||||||
|
*/
|
||||||
|
private void toggleAllRules(JTable ruleTable) {
|
||||||
|
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
|
||||||
|
int rowCount = model.getRowCount();
|
||||||
|
|
||||||
|
if (rowCount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断当前状态:如果所有规则都开启,则关闭所有;否则开启所有
|
||||||
|
boolean allEnabled = true;
|
||||||
|
for (int i = 0; i < rowCount; i++) {
|
||||||
|
if (!(Boolean) model.getValueAt(i, 0)) {
|
||||||
|
allEnabled = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean newState = !allEnabled;
|
||||||
|
|
||||||
|
// 更新所有行的状态
|
||||||
|
for (int i = 0; i < rowCount; i++) {
|
||||||
|
model.setValueAt(newState, i, 0);
|
||||||
|
// 通知规则处理器更新规则状态
|
||||||
|
ruleProcessor.changeRule(model.getDataVector().get(i), i, getCurrentTabTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新表头复选框状态
|
||||||
|
updateHeaderCheckBoxState(model);
|
||||||
|
|
||||||
|
// 刷新表格和表头
|
||||||
|
ruleTable.repaint();
|
||||||
|
ruleTable.getTableHeader().repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新表头复选框的状态
|
||||||
|
*/
|
||||||
|
private void updateHeaderCheckBoxState(DefaultTableModel model) {
|
||||||
|
int rowCount = model.getRowCount();
|
||||||
|
if (rowCount == 0) {
|
||||||
|
headerCheckBox.setSelected(false);
|
||||||
|
headerCheckBox.getModel().setArmed(false);
|
||||||
|
headerCheckBox.getModel().setPressed(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int enabledCount = 0;
|
||||||
|
for (int i = 0; i < rowCount; i++) {
|
||||||
|
if ((Boolean) model.getValueAt(i, 0)) {
|
||||||
|
enabledCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabledCount == 0) {
|
||||||
|
// 全部未选中
|
||||||
|
headerCheckBox.setSelected(false);
|
||||||
|
headerCheckBox.getModel().setArmed(false);
|
||||||
|
headerCheckBox.getModel().setPressed(false);
|
||||||
|
} else if (enabledCount == rowCount) {
|
||||||
|
// 全部选中
|
||||||
|
headerCheckBox.setSelected(true);
|
||||||
|
headerCheckBox.getModel().setArmed(false);
|
||||||
|
headerCheckBox.getModel().setPressed(false);
|
||||||
|
} else {
|
||||||
|
// 部分选中 - 显示为按下但未选中的状态
|
||||||
|
headerCheckBox.setSelected(false);
|
||||||
|
headerCheckBox.getModel().setArmed(true);
|
||||||
|
headerCheckBox.getModel().setPressed(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充Display对象的字段值
|
||||||
|
*/
|
||||||
|
private void populateDisplayFromTable(Display ruleDisplay, JTable ruleTable, int selectedRow) {
|
||||||
|
ruleDisplay.ruleNameTextField.setText(ruleTable.getValueAt(selectedRow, 1).toString());
|
||||||
|
ruleDisplay.firstRegexTextField.setText(ruleTable.getValueAt(selectedRow, 2).toString());
|
||||||
|
ruleDisplay.secondRegexTextField.setText(ruleTable.getValueAt(selectedRow, 3).toString());
|
||||||
|
ruleDisplay.formatTextField.setText(ruleTable.getValueAt(selectedRow, 4).toString());
|
||||||
|
ruleDisplay.colorComboBox.setSelectedItem(ruleTable.getValueAt(selectedRow, 5).toString());
|
||||||
|
ruleDisplay.scopeComboBox.setSelectedItem(ruleTable.getValueAt(selectedRow, 6).toString());
|
||||||
|
ruleDisplay.engineComboBox.setSelectedItem(ruleTable.getValueAt(selectedRow, 7).toString());
|
||||||
|
ruleDisplay.sensitiveComboBox.setSelectedItem(ruleTable.getValueAt(selectedRow, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Display对象创建规则数据Vector
|
||||||
|
*/
|
||||||
|
private Vector<Object> createRuleDataFromDisplay(Display ruleDisplay) {
|
||||||
Vector<Object> ruleData = new Vector<>();
|
Vector<Object> ruleData = new Vector<>();
|
||||||
ruleData.add(false);
|
ruleData.add(false);
|
||||||
ruleData.add(ruleDisplay.ruleNameTextField.getText());
|
ruleData.add(ruleDisplay.ruleNameTextField.getText());
|
||||||
@@ -109,55 +301,115 @@ public class Rule extends JPanel {
|
|||||||
ruleData.add(ruleDisplay.scopeComboBox.getSelectedItem().toString());
|
ruleData.add(ruleDisplay.scopeComboBox.getSelectedItem().toString());
|
||||||
ruleData.add(ruleDisplay.engineComboBox.getSelectedItem().toString());
|
ruleData.add(ruleDisplay.engineComboBox.getSelectedItem().toString());
|
||||||
ruleData.add(ruleDisplay.sensitiveComboBox.getSelectedItem());
|
ruleData.add(ruleDisplay.sensitiveComboBox.getSelectedItem());
|
||||||
|
return ruleData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示规则编辑对话框
|
||||||
|
*/
|
||||||
|
private boolean showRuleDialog(Display ruleDisplay, String title) {
|
||||||
|
ruleDisplay.formatTextField.setEnabled(ruleDisplay.engineComboBox.getSelectedItem().toString().equals("nfa"));
|
||||||
|
int showState = JOptionPane.showConfirmDialog(this, ruleDisplay, title, JOptionPane.YES_NO_OPTION);
|
||||||
|
return showState == YES_OPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有选中的行
|
||||||
|
*/
|
||||||
|
private boolean hasSelectedRow(JTable ruleTable) {
|
||||||
|
return ruleTable.getSelectedRowCount() >= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前选中的Tab标题
|
||||||
|
*/
|
||||||
|
private String getCurrentTabTitle() {
|
||||||
|
return tabbedPane.getTitleAt(tabbedPane.getSelectedIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ruleCopyActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
|
||||||
|
if (!hasSelectedRow(ruleTable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Display ruleDisplay = new Display();
|
||||||
|
int selectedRow = ruleTable.getSelectedRow();
|
||||||
|
|
||||||
|
populateDisplayFromTable(ruleDisplay, ruleTable, selectedRow);
|
||||||
|
// 为复制的规则名称添加前缀
|
||||||
|
ruleDisplay.ruleNameTextField.setText(String.format("Copy of %s", ruleDisplay.ruleNameTextField.getText()));
|
||||||
|
|
||||||
|
if (showRuleDialog(ruleDisplay, "Copy Rule")) {
|
||||||
|
Vector<Object> ruleData = createRuleDataFromDisplay(ruleDisplay);
|
||||||
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
|
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
|
||||||
model.insertRow(model.getRowCount(), ruleData);
|
model.insertRow(model.getRowCount(), ruleData);
|
||||||
ruleProcessor.addRule(ruleData, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
|
ruleProcessor.addRule(ruleData, getCurrentTabTitle());
|
||||||
|
|
||||||
|
// 复制规则后更新表头复选框状态
|
||||||
|
updateHeaderCheckBoxState(model);
|
||||||
|
ruleTable.getTableHeader().repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ruleAddActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
|
||||||
|
Display ruleDisplay = new Display();
|
||||||
|
ruleDisplay.formatTextField.setText("{0}");
|
||||||
|
|
||||||
|
if (showRuleDialog(ruleDisplay, "Add Rule")) {
|
||||||
|
Vector<Object> ruleData = createRuleDataFromDisplay(ruleDisplay);
|
||||||
|
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
|
||||||
|
model.insertRow(model.getRowCount(), ruleData);
|
||||||
|
ruleProcessor.addRule(ruleData, getCurrentTabTitle());
|
||||||
|
|
||||||
|
// 添加规则后更新表头复选框状态
|
||||||
|
updateHeaderCheckBoxState(model);
|
||||||
|
ruleTable.getTableHeader().repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ruleEditActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
|
private void ruleEditActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
|
||||||
if (ruleTable.getSelectedRowCount() >= 1) {
|
if (!hasSelectedRow(ruleTable)) {
|
||||||
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
|
return;
|
||||||
Display ruleDisplay = new Display();
|
|
||||||
|
|
||||||
ruleDisplay.ruleNameTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 1).toString());
|
|
||||||
ruleDisplay.firstRegexTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 2).toString());
|
|
||||||
ruleDisplay.secondRegexTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 3).toString());
|
|
||||||
ruleDisplay.formatTextField.setText(ruleTable.getValueAt(ruleTable.getSelectedRow(), 4).toString());
|
|
||||||
ruleDisplay.colorComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 5).toString());
|
|
||||||
ruleDisplay.scopeComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 6).toString());
|
|
||||||
ruleDisplay.engineComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 7).toString());
|
|
||||||
ruleDisplay.sensitiveComboBox.setSelectedItem(ruleTable.getValueAt(ruleTable.getSelectedRow(), 8));
|
|
||||||
|
|
||||||
ruleDisplay.formatTextField.setEnabled(ruleDisplay.engineComboBox.getSelectedItem().toString().equals("nfa"));
|
|
||||||
|
|
||||||
int showState = JOptionPane.showConfirmDialog(this, ruleDisplay, "Edit Rule", JOptionPane.YES_NO_OPTION);
|
|
||||||
if (showState == 0) {
|
|
||||||
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
|
|
||||||
model.setValueAt(ruleDisplay.ruleNameTextField.getText(), select, 1);
|
|
||||||
model.setValueAt(ruleDisplay.firstRegexTextField.getText(), select, 2);
|
|
||||||
model.setValueAt(ruleDisplay.secondRegexTextField.getText(), select, 3);
|
|
||||||
model.setValueAt(ruleDisplay.formatTextField.getText(), select, 4);
|
|
||||||
model.setValueAt(ruleDisplay.colorComboBox.getSelectedItem().toString(), select, 5);
|
|
||||||
model.setValueAt(ruleDisplay.scopeComboBox.getSelectedItem().toString(), select, 6);
|
|
||||||
model.setValueAt(ruleDisplay.engineComboBox.getSelectedItem().toString(), select, 7);
|
|
||||||
model.setValueAt(ruleDisplay.sensitiveComboBox.getSelectedItem(), select, 8);
|
|
||||||
model = (DefaultTableModel) ruleTable.getModel();
|
|
||||||
ruleProcessor.changeRule(model.getDataVector().get(select), select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Display ruleDisplay = new Display();
|
||||||
|
int selectedRow = ruleTable.getSelectedRow();
|
||||||
|
|
||||||
|
populateDisplayFromTable(ruleDisplay, ruleTable, selectedRow);
|
||||||
|
|
||||||
|
if (showRuleDialog(ruleDisplay, "Edit Rule")) {
|
||||||
|
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
|
||||||
|
int modelIndex = ruleTable.convertRowIndexToModel(selectedRow);
|
||||||
|
|
||||||
|
// 更新表格数据
|
||||||
|
Vector<Object> ruleData = createRuleDataFromDisplay(ruleDisplay);
|
||||||
|
for (int i = 1; i < ruleData.size(); i++) {
|
||||||
|
model.setValueAt(ruleData.get(i), modelIndex, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleProcessor.changeRule(model.getDataVector().get(modelIndex), modelIndex, getCurrentTabTitle());
|
||||||
|
|
||||||
|
// 编辑规则后更新表头复选框状态(如果编辑影响了启用状态)
|
||||||
|
updateHeaderCheckBoxState(model);
|
||||||
|
ruleTable.getTableHeader().repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ruleRemoveActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
|
private void ruleRemoveActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) {
|
||||||
if (ruleTable.getSelectedRowCount() >= 1) {
|
if (!hasSelectedRow(ruleTable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (JOptionPane.showConfirmDialog(this, "Are you sure you want to remove this rule?", "Info", JOptionPane.YES_NO_OPTION) == 0) {
|
if (JOptionPane.showConfirmDialog(this, "Are you sure you want to remove this rule?", "Info", JOptionPane.YES_NO_OPTION) == 0) {
|
||||||
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
|
DefaultTableModel model = (DefaultTableModel) ruleTable.getModel();
|
||||||
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
|
int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow());
|
||||||
|
|
||||||
model.removeRow(select);
|
model.removeRow(select);
|
||||||
ruleProcessor.removeRule(select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()));
|
ruleProcessor.removeRule(select, getCurrentTabTitle());
|
||||||
}
|
|
||||||
|
// 删除规则后更新表头复选框状态
|
||||||
|
updateHeaderCheckBoxState(model);
|
||||||
|
ruleTable.getTableHeader().repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ import java.util.regex.Pattern;
|
|||||||
public class RegularMatcher {
|
public class RegularMatcher {
|
||||||
private static final Map<String, Pattern> nfaPatternCache = new ConcurrentHashMap<>();
|
private static final Map<String, Pattern> nfaPatternCache = new ConcurrentHashMap<>();
|
||||||
private static final Map<String, RunAutomaton> dfaAutomatonCache = new ConcurrentHashMap<>();
|
private static final Map<String, RunAutomaton> dfaAutomatonCache = new ConcurrentHashMap<>();
|
||||||
|
private static final Pattern formatIndexPattern = Pattern.compile("\\{(\\d+)}");
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
private final ConfigLoader configLoader;
|
private final ConfigLoader configLoader;
|
||||||
|
|
||||||
@@ -217,8 +218,8 @@ public class RegularMatcher {
|
|||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
String matchContent = matcher.group(1);
|
String matchContent = matcher.group(1);
|
||||||
if (!matchContent.isEmpty()) {
|
if (!matchContent.isEmpty()) {
|
||||||
matcher = createPatternMatcher(s_regex, matchContent, sensitive);
|
Matcher secondMatcher = createPatternMatcher(s_regex, matchContent, sensitive);
|
||||||
matches.addAll(formatMatchResults(matcher, format));
|
matches.addAll(formatMatchResults(secondMatcher, format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,9 +243,20 @@ public class RegularMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<String> formatMatchResults(Matcher matcher, String format) {
|
private List<String> formatMatchResults(Matcher matcher, String format) {
|
||||||
List<Integer> indexList = parseIndexesFromString(format);
|
|
||||||
List<String> stringList = new ArrayList<>();
|
List<String> stringList = new ArrayList<>();
|
||||||
|
|
||||||
|
// 当format为{0}时,直接返回第一个捕获组,避免格式化开销
|
||||||
|
if ("{0}".equals(format)) {
|
||||||
|
while (matcher.find()) {
|
||||||
|
if (matcher.groupCount() > 0 && !matcher.group(1).isEmpty()) {
|
||||||
|
stringList.add(matcher.group(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 需要复杂格式化的情况
|
||||||
|
List<Integer> indexList = parseIndexesFromString(format);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
if (!matcher.group(1).isEmpty()) {
|
if (!matcher.group(1).isEmpty()) {
|
||||||
Object[] params = indexList.stream().map(i -> {
|
Object[] params = indexList.stream().map(i -> {
|
||||||
@@ -295,8 +307,7 @@ public class RegularMatcher {
|
|||||||
|
|
||||||
private LinkedList<Integer> parseIndexesFromString(String input) {
|
private LinkedList<Integer> parseIndexesFromString(String input) {
|
||||||
LinkedList<Integer> indexes = new LinkedList<>();
|
LinkedList<Integer> indexes = new LinkedList<>();
|
||||||
Pattern pattern = Pattern.compile("\\{(\\d+)}");
|
Matcher matcher = formatIndexPattern.matcher(input);
|
||||||
Matcher matcher = pattern.matcher(input);
|
|
||||||
|
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
String index = matcher.group(1);
|
String index = matcher.group(1);
|
||||||
@@ -318,8 +329,7 @@ public class RegularMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String normalizeFormatIndexes(String format) {
|
private String normalizeFormatIndexes(String format) {
|
||||||
Pattern pattern = Pattern.compile("\\{(\\d+)}");
|
Matcher matcher = formatIndexPattern.matcher(format);
|
||||||
Matcher matcher = pattern.matcher(format);
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
String newStr = String.format("{%s}", count);
|
String newStr = String.format("{%s}", count);
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
package hae.utils.string;
|
package hae.utils.string;
|
||||||
|
|
||||||
import burp.api.montoya.core.ByteArray;
|
|
||||||
import burp.api.montoya.http.HttpService;
|
|
||||||
import burp.api.montoya.http.message.HttpRequestResponse;
|
|
||||||
import burp.api.montoya.http.message.requests.HttpRequest;
|
|
||||||
import burp.api.montoya.http.message.responses.HttpResponse;
|
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -64,19 +56,6 @@ public class StringProcessor {
|
|||||||
return matchesDirectly || matchesPattern;
|
return matchesDirectly || matchesPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpRequestResponse createHttpRequestResponse(String url, byte[] request, byte[] response) {
|
|
||||||
HttpService httpService = HttpService.httpService(url);
|
|
||||||
HttpRequest httpRequest = HttpRequest.httpRequest(httpService, ByteArray.byteArray(request));
|
|
||||||
HttpResponse httpResponse = HttpResponse.httpResponse(ByteArray.byteArray(response));
|
|
||||||
return HttpRequestResponse.httpRequestResponse(httpRequest, httpResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCurrentTime() {
|
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
|
|
||||||
LocalDateTime now = LocalDateTime.now();
|
|
||||||
return now.format(formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getRandomUUID() {
|
public static String getRandomUUID() {
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
return uuid.toString();
|
return uuid.toString();
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ rules:
|
|||||||
sensitive: false
|
sensitive: false
|
||||||
- name: Vite DevMode
|
- name: Vite DevMode
|
||||||
loaded: true
|
loaded: true
|
||||||
f_regex: (/@vite/client)
|
f_regex: (/\@vite/client)
|
||||||
s_regex: ''
|
s_regex: ''
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
color: red
|
color: red
|
||||||
@@ -133,8 +133,8 @@ rules:
|
|||||||
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: (\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,5}\b)
|
||||||
s_regex: ''
|
s_regex: ^((?!.*\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico?)$).*@.*\..*)$
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
color: yellow
|
color: yellow
|
||||||
scope: response
|
scope: response
|
||||||
@@ -198,9 +198,9 @@ rules:
|
|||||||
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,32})([p](ass|wd|asswd|assword))(|[\.\w]{1,32})(|\\)(|'|")(
|
||||||
|)(:|[=]{1,3}|![=]{1,2}|[\)]{0,1}\.val\()( |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))|((|\\)('|")([^'"]+?)(|\\)('|")(|\\)(|'|")(
|
|)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(|'|")([^'"]+?)(|\\)(|'|")(|,|\)))|((|\\)(|'|")([^'"]+?)(|\\)(|'|")(|\\)(|'|")(
|
||||||
|)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,10})([p](ass|wd|asswd|assword))(|[\.\w]{1,10})(|\\)(|'|")))
|
|)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,32})([p](ass|wd|asswd|assword))(|[\.\w]{1,32})(|\\)(|'|")))
|
||||||
s_regex: ''
|
s_regex: ''
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -209,9 +209,9 @@ rules:
|
|||||||
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,32})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\.\w]{1,32})(|\\)(|'|")(
|
||||||
|)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))|((|\\)('|")([^'"]+?)(|\\)('|")(|\\)(|'|")(
|
|)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(|'|")([^'"]+?)(|\\)(|'|")(|,|\)))|((|\\)(|'|")([^'"]+?)(|\\)(|'|")(|\\)(|'|")(
|
||||||
|)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\.\w]{1,10})(|\\)(|'|")))
|
|)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,32})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\.\w]{1,32})(|\\)(|'|")))
|
||||||
s_regex: ''
|
s_regex: ''
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
color: green
|
color: green
|
||||||
@@ -247,9 +247,9 @@ rules:
|
|||||||
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]{1,32})(key|secret|token|config|auth|access|admin|ticket)(|[\.\w]{1,32})(|\\)(|'|")(
|
||||||
|)(:|=|!=|[\)]{0,1}\.val\()( |)('|")([^'"]+?)('|")(|,|\)))|((|\\)('|")([^'"]+?)(|\\)('|")(|\\)(|'|")(
|
|)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(|'|")([^'"]+?)(|\\)(|'|")(|,|\)))|((|\\)(|'|")([^'"]+?)(|\\)(|'|")(|\\)(|'|")(
|
||||||
|)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,10})(key|secret|token|config|auth|access|admin|ticket)(|[\.\w]{1,10})(|\\)(|'|")))
|
|)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,32})(key|secret|token|config|auth|access|admin|ticket)(|[\.\w]{1,32})(|\\)(|'|")))
|
||||||
s_regex: ''
|
s_regex: ''
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
color: yellow
|
color: yellow
|
||||||
@@ -258,20 +258,29 @@ rules:
|
|||||||
sensitive: false
|
sensitive: false
|
||||||
- name: Mobile Number Field
|
- name: Mobile Number Field
|
||||||
loaded: true
|
loaded: true
|
||||||
f_regex: '(((|\\)(|''|")(|[\w]{1,10})(mobile|phone|sjh|shoujihao|concat)(|[\.\w]{1,10})(|\\)(|''|")(
|
f_regex: (((|\\)(|'|")(|[\.\w]{1,32})(mobile|phone|sjh|shoujihao|concat)(|[\.\w]{1,32})(|\\)(|'|")(
|
||||||
|)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(''|")([^''"]+?)(|\\)(''|")(|,|\)))|((|\\)(''|")([^''"]+?)(|\\)(''|")(|\\)(|''|")(
|
|)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(|'|")([^'"]+?)(|\\)(|'|")(|,|\)))|((|\\)(|'|")([^'"]+?)(|\\)(|'|")(|\\)(|'|")(
|
||||||
|)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,10})(mobile|phone|sjh|shoujihao|concat)(|[\.\w]{1,10})(|\\)(|''|"))) '
|
|)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,32})(mobile|phone|sjh|shoujihao|concat)(|[\.\w]{1,32})(|\\)(|'|")))
|
||||||
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: Userinfo In Link
|
||||||
|
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: ((([p](ass|wd|asswd|assword))|(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator)))))=[\.\w]{1,32})
|
||||||
|
format: '{0}'
|
||||||
|
color: green
|
||||||
|
scope: response body
|
||||||
|
engine: nfa
|
||||||
|
sensitive: false
|
||||||
- group: Other
|
- group: Other
|
||||||
rule:
|
rule:
|
||||||
- name: Linkfinder
|
- name: Linkfinder
|
||||||
loaded: true
|
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,}|)))(?:"|')
|
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: gray
|
color: gray
|
||||||
@@ -334,10 +343,19 @@ rules:
|
|||||||
sensitive: false
|
sensitive: false
|
||||||
- name: 302 Location
|
- name: 302 Location
|
||||||
loaded: true
|
loaded: true
|
||||||
f_regex: 'Location: (.*?)\n'
|
f_regex: 'Location: (.*?)\r\n'
|
||||||
s_regex: ''
|
s_regex: ''
|
||||||
format: '{0}'
|
format: '{0}'
|
||||||
color: gray
|
color: gray
|
||||||
scope: response header
|
scope: response header
|
||||||
engine: nfa
|
engine: nfa
|
||||||
sensitive: false
|
sensitive: false
|
||||||
|
- name: OSKeys
|
||||||
|
loaded: false
|
||||||
|
f_regex: <Key>(.*?)</Key>
|
||||||
|
s_regex: ''
|
||||||
|
format: '{0}'
|
||||||
|
color: gray
|
||||||
|
scope: response body
|
||||||
|
engine: nfa
|
||||||
|
sensitive: true
|
||||||
|
|||||||
Reference in New Issue
Block a user