Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
332b119064 | ||
|
|
ead03d42b9 |
@@ -30,9 +30,7 @@
|
|||||||
|
|
||||||
### 规则释义
|
### 规则释义
|
||||||
|
|
||||||
HaE目前的规则一共有8个字段,分别是规则名称、规则正则、规则作用域、正则引擎、规则匹配颜色、规则敏感性。
|
HaE目前的规则一共有8个字段,详细的含义如下所示:
|
||||||
|
|
||||||
详细的含义如下所示:
|
|
||||||
|
|
||||||
| 字段 | 含义 |
|
| 字段 | 含义 |
|
||||||
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
public class Config {
|
public class Config {
|
||||||
public static String suffix = "3g2|3gp|7z|aac|abw|aif|aifc|aiff|apk|arc|au|avi|azw|bat|bin|bmp|bz|bz2|cmd|cmx|cod|com|csh|css|csv|dll|doc|docx|ear|eot|epub|exe|flac|flv|gif|gz|ico|ics|ief|jar|jfif|jpe|jpeg|jpg|less|m3u|mid|midi|mjs|mkv|mov|mp2|mp3|mp4|mpa|mpe|mpeg|mpg|mpkg|mpp|mpv2|odp|ods|odt|oga|ogg|ogv|ogx|otf|pbm|pdf|pgm|png|pnm|ppm|ppt|pptx|ra|ram|rar|ras|rgb|rmi|rtf|scss|sh|snd|svg|swf|tar|tif|tiff|ttf|vsd|war|wav|weba|webm|webp|wmv|woff|woff2|xbm|xls|xlsx|xpm|xul|xwd|zip";
|
public static String suffix = "3g2|3gp|7z|aac|abw|aif|aifc|aiff|apk|arc|au|avi|azw|bat|bin|bmp|bz|bz2|cmd|cmx|cod|com|csh|css|csv|dll|doc|docx|ear|eot|epub|exe|flac|flv|gif|gz|ico|ics|ief|jar|jfif|jpe|jpeg|jpg|less|m3u|mid|midi|mjs|mkv|mov|mp2|mp3|mp4|mpa|mpe|mpeg|mpg|mpkg|mpp|mpv2|odp|ods|odt|oga|ogg|ogv|ogx|otf|pbm|pdf|pgm|png|pnm|ppm|ppt|pptx|ra|ram|rar|ras|rgb|rmi|rtf|scss|sh|snd|svg|swf|tar|tif|tiff|ttf|vsd|war|wav|weba|webm|webp|wmv|woff|woff2|xbm|xls|xlsx|xpm|xul|xwd|zip";
|
||||||
|
|
||||||
|
public static String host = "gh0st.cn";
|
||||||
|
|
||||||
public static String[] scope = new String[]{
|
public static String[] scope = new String[]{
|
||||||
"any",
|
"any",
|
||||||
"any header",
|
"any header",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class HaE implements BurpExtension {
|
|||||||
@Override
|
@Override
|
||||||
public void initialize(MontoyaApi api) {
|
public void initialize(MontoyaApi api) {
|
||||||
// 设置扩展名称
|
// 设置扩展名称
|
||||||
String version = "3.0.2";
|
String version = "3.1";
|
||||||
api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version));
|
api.extension().setName(String.format("HaE (%s) - Highlighter and Extractor", version));
|
||||||
|
|
||||||
// 加载扩展后输出的项目信息
|
// 加载扩展后输出的项目信息
|
||||||
@@ -34,14 +34,14 @@ public class HaE implements BurpExtension {
|
|||||||
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
|
api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel));
|
||||||
|
|
||||||
// 注册HTTP处理器
|
// 注册HTTP处理器
|
||||||
api.http().registerHttpHandler(new HttpMessageHandler(api, messageTableModel));
|
api.http().registerHttpHandler(new HttpMessageHandler(api, configLoader, messageTableModel));
|
||||||
|
|
||||||
// 注册WebSocket处理器
|
// 注册WebSocket处理器
|
||||||
api.proxy().registerWebSocketCreationHandler(proxyWebSocketCreation -> proxyWebSocketCreation.proxyWebSocket().registerProxyMessageHandler(new WebSocketMessageHandler(api)));
|
api.proxy().registerWebSocketCreationHandler(proxyWebSocketCreation -> proxyWebSocketCreation.proxyWebSocket().registerProxyMessageHandler(new WebSocketMessageHandler(api)));
|
||||||
|
|
||||||
// 注册消息编辑框(用于展示数据)
|
// 注册消息编辑框(用于展示数据)
|
||||||
api.userInterface().registerHttpRequestEditorProvider(new RequestEditor(api));
|
api.userInterface().registerHttpRequestEditorProvider(new RequestEditor(api, configLoader));
|
||||||
api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api));
|
api.userInterface().registerHttpResponseEditorProvider(new ResponseEditor(api, configLoader));
|
||||||
api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api));
|
api.userInterface().registerWebSocketMessageEditorProvider(new WebSocketEditor(api));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/main/java/hae/cache/CachePool.java
vendored
28
src/main/java/hae/cache/CachePool.java
vendored
@@ -1,20 +1,34 @@
|
|||||||
package hae.cache;
|
package hae.cache;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class CachePool {
|
public class CachePool {
|
||||||
private static final Map<String, Map<String, Map<String, Object>>> cache = new HashMap<>();
|
private static final int MAX_SIZE = 100000;
|
||||||
|
private static final int EXPIRE_DURATION = 5;
|
||||||
|
|
||||||
public static void addToCache(String key, Map<String, Map<String, Object>> value) {
|
private static final Cache<String, Map<String, Map<String, Object>>> cache =
|
||||||
|
Caffeine.newBuilder()
|
||||||
|
.maximumSize(MAX_SIZE)
|
||||||
|
.expireAfterWrite(EXPIRE_DURATION, TimeUnit.HOURS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public static void put(String key, Map<String, Map<String, Object>> value) {
|
||||||
cache.put(key, value);
|
cache.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, Map<String, Object>> getFromCache(String key) {
|
public static Map<String, Map<String, Object>> get(String key) {
|
||||||
return cache.get(key);
|
return cache.getIfPresent(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeFromCache(String key) {
|
public static void remove(String key) {
|
||||||
cache.remove(key);
|
cache.invalidate(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
cache.invalidateAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,17 +66,17 @@ public class Main extends JPanel {
|
|||||||
// 依次添加Rules、Config、Databoard
|
// 依次添加Rules、Config、Databoard
|
||||||
Rules rules = new Rules(api, configLoader);
|
Rules rules = new Rules(api, configLoader);
|
||||||
mainTabbedPane.addTab("Rules", rules);
|
mainTabbedPane.addTab("Rules", rules);
|
||||||
mainTabbedPane.addTab("Config", new Config(api, configLoader, rules));
|
|
||||||
mainTabbedPane.addTab("Databoard", new Databoard(api, configLoader, messageTableModel));
|
mainTabbedPane.addTab("Databoard", new Databoard(api, configLoader, messageTableModel));
|
||||||
|
mainTabbedPane.addTab("Config", new Config(api, configLoader, rules));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImageIcon getImageIcon(boolean isDark) {
|
private ImageIcon getImageIcon(boolean isDark) {
|
||||||
ClassLoader classLoader = getClass().getClassLoader();
|
ClassLoader classLoader = getClass().getClassLoader();
|
||||||
URL imageURL;
|
URL imageURL;
|
||||||
if (isDark) {
|
if (isDark) {
|
||||||
imageURL = classLoader.getResource("logo.png");
|
imageURL = classLoader.getResource("logo/logo.png");
|
||||||
} else {
|
} else {
|
||||||
imageURL = classLoader.getResource("logo_black.png");
|
imageURL = classLoader.getResource("logo/logo_black.png");
|
||||||
}
|
}
|
||||||
ImageIcon originalIcon = new ImageIcon(imageURL);
|
ImageIcon originalIcon = new ImageIcon(imageURL);
|
||||||
Image originalImage = originalIcon.getImage();
|
Image originalImage = originalIcon.getImage();
|
||||||
|
|||||||
@@ -250,12 +250,18 @@ public class Databoard extends JPanel {
|
|||||||
|
|
||||||
String cleanedText = StringProcessor.replaceFirstOccurrence(filterText, "*.", "");
|
String cleanedText = StringProcessor.replaceFirstOccurrence(filterText, "*.", "");
|
||||||
|
|
||||||
if (cleanedText.contains("*")) {
|
RowFilter<Object, Object> rowFilter = new RowFilter<Object, Object>() {
|
||||||
cleanedText = "";
|
public boolean include(Entry<?, ?> entry) {
|
||||||
|
if (cleanedText.equals("*")) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
String host = StringProcessor.getHostByUrl((String) entry.getValue(1));
|
||||||
|
return StringProcessor.matchFromEnd(host, cleanedText);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(cleanedText, 1);
|
sorter.setRowFilter(rowFilter);
|
||||||
sorter.setRowFilter(filter);
|
|
||||||
|
|
||||||
messageTableModel.applyHostFilter(filterText);
|
messageTableModel.applyHostFilter(filterText);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ package hae.component.board;
|
|||||||
|
|
||||||
import burp.api.montoya.MontoyaApi;
|
import burp.api.montoya.MontoyaApi;
|
||||||
import hae.component.board.message.MessageTableModel;
|
import hae.component.board.message.MessageTableModel;
|
||||||
import jregex.Pattern;
|
import hae.utils.ui.UIEnhancer;
|
||||||
import jregex.REFlags;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
@@ -14,12 +13,11 @@ import javax.swing.table.TableRowSorter;
|
|||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.datatransfer.Clipboard;
|
import java.awt.datatransfer.Clipboard;
|
||||||
import java.awt.datatransfer.StringSelection;
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.awt.event.FocusEvent;
|
|
||||||
import java.awt.event.FocusListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class Datatable extends JPanel {
|
public class Datatable extends JPanel {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
@@ -66,7 +64,7 @@ public class Datatable extends JPanel {
|
|||||||
|
|
||||||
// 设置灰色默认文本
|
// 设置灰色默认文本
|
||||||
String searchText = "Search";
|
String searchText = "Search";
|
||||||
addPlaceholder(searchField, searchText);
|
UIEnhancer.setTextFieldPlaceholder(searchField, searchText);
|
||||||
|
|
||||||
// 监听输入框内容输入、更新、删除
|
// 监听输入框内容输入、更新、删除
|
||||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
@@ -121,28 +119,6 @@ public class Datatable extends JPanel {
|
|||||||
add(optionsPanel, BorderLayout.SOUTH);
|
add(optionsPanel, BorderLayout.SOUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addPlaceholder(JTextField textField, String placeholderText) {
|
|
||||||
textField.setForeground(Color.GRAY);
|
|
||||||
textField.setText(placeholderText);
|
|
||||||
textField.addFocusListener(new FocusListener() {
|
|
||||||
@Override
|
|
||||||
public void focusGained(FocusEvent e) {
|
|
||||||
if (textField.getText().equals(placeholderText)) {
|
|
||||||
textField.setText("");
|
|
||||||
textField.setForeground(Color.BLACK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void focusLost(FocusEvent e) {
|
|
||||||
if (textField.getText().isEmpty()) {
|
|
||||||
textField.setForeground(Color.GRAY);
|
|
||||||
textField.setText(placeholderText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addRowToTable(Object[] data) {
|
private void addRowToTable(Object[] data) {
|
||||||
int rowCount = dataTableModel.getRowCount();
|
int rowCount = dataTableModel.getRowCount();
|
||||||
int id = rowCount > 0 ? (Integer) dataTableModel.getValueAt(rowCount - 1, 0) + 1 : 1;
|
int id = rowCount > 0 ? (Integer) dataTableModel.getValueAt(rowCount - 1, 0) + 1 : 1;
|
||||||
@@ -159,7 +135,7 @@ public class Datatable extends JPanel {
|
|||||||
String searchFieldTextText = searchField.getText();
|
String searchFieldTextText = searchField.getText();
|
||||||
Pattern pattern = null;
|
Pattern pattern = null;
|
||||||
try {
|
try {
|
||||||
pattern = new Pattern(searchFieldTextText, REFlags.IGNORE_CASE);
|
pattern = Pattern.compile(searchFieldTextText, Pattern.CASE_INSENSITIVE);
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
|
|
||||||
private Map<String, Map<String, Object>> getCacheData(byte[] content) {
|
private Map<String, Map<String, Object>> getCacheData(byte[] content) {
|
||||||
String hashIndex = HashCalculator.calculateHash(content);
|
String hashIndex = HashCalculator.calculateHash(content);
|
||||||
return CachePool.getFromCache(hashIndex);
|
return CachePool.get(hashIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean areMapsEqual(Map<String, Map<String, Object>> map1, Map<String, Map<String, Object>> map2) {
|
private boolean areMapsEqual(Map<String, Map<String, Object>> map1, Map<String, Map<String, Object>> map2) {
|
||||||
@@ -396,8 +396,8 @@ public class MessageTableModel extends AbstractTableModel {
|
|||||||
public class MessageTable extends JTable {
|
public class MessageTable extends JTable {
|
||||||
private MessageEntry MessageEntry;
|
private MessageEntry MessageEntry;
|
||||||
private SwingWorker<Object, Void> currentWorker;
|
private SwingWorker<Object, Void> currentWorker;
|
||||||
// 设置响应报文返回的最大长度为3MB
|
// 设置响应报文返回的最大长度
|
||||||
private final int MAX_LENGTH = 3145728;
|
private final int MAX_LENGTH = 5242880;
|
||||||
private int lastSelectedIndex = -1;
|
private int lastSelectedIndex = -1;
|
||||||
private final HttpRequestEditor requestEditor;
|
private final HttpRequestEditor requestEditor;
|
||||||
private final HttpResponseEditor responseEditor;
|
private final HttpResponseEditor responseEditor;
|
||||||
|
|||||||
@@ -3,10 +3,21 @@ package hae.component.config;
|
|||||||
import burp.api.montoya.MontoyaApi;
|
import burp.api.montoya.MontoyaApi;
|
||||||
import hae.component.rule.Rules;
|
import hae.component.rule.Rules;
|
||||||
import hae.utils.config.ConfigLoader;
|
import hae.utils.config.ConfigLoader;
|
||||||
|
import hae.utils.ui.UIEnhancer;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.event.TableModelEvent;
|
||||||
|
import javax.swing.event.TableModelListener;
|
||||||
|
import javax.swing.table.DefaultTableModel;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.datatransfer.Clipboard;
|
||||||
|
import java.awt.datatransfer.DataFlavor;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class Config extends JPanel {
|
public class Config extends JPanel {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
@@ -22,67 +33,214 @@ public class Config extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
setLayout(new GridBagLayout());
|
setLayout(new BorderLayout());
|
||||||
((GridBagLayout) getLayout()).columnWidths = new int[]{0, 0, 0, 0, 0};
|
|
||||||
((GridBagLayout) getLayout()).rowHeights = new int[]{0, 0, 0};
|
|
||||||
((GridBagLayout) getLayout()).columnWeights = new double[]{0.0, 1.0, 0.0, 0.0, 1.0E-4};
|
|
||||||
((GridBagLayout) getLayout()).rowWeights = new double[]{0.0, 0.0, 1.0E-4};
|
|
||||||
|
|
||||||
JLabel rulesFilePathLabel = new JLabel("Rules Path:");
|
GridBagConstraints constraints = new GridBagConstraints();
|
||||||
JTextField rulesFilePathTextField = new JTextField();
|
constraints.weightx = 1.0;
|
||||||
JButton onlineUpdateButton = new JButton("Update");
|
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||||
JLabel excludeSuffixLabel = new JLabel("Exclude Suffix:");
|
|
||||||
JTextField excludeSuffixTextField = new JTextField();
|
JPanel ruleInfoPanel = new JPanel(new GridBagLayout());
|
||||||
JButton excludeSuffixSaveButton = new JButton("Save");
|
ruleInfoPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||||
|
|
||||||
|
JLabel ruleLabel = new JLabel("Path:");
|
||||||
|
JTextField pathTextField = new JTextField();
|
||||||
|
pathTextField.setEditable(false);
|
||||||
|
pathTextField.setText(configLoader.getRulesFilePath());
|
||||||
JButton reloadButton = new JButton("Reload");
|
JButton reloadButton = new JButton("Reload");
|
||||||
|
JButton updateButton = new JButton("Update");
|
||||||
|
ruleInfoPanel.add(ruleLabel);
|
||||||
|
ruleInfoPanel.add(pathTextField, constraints);
|
||||||
|
ruleInfoPanel.add(Box.createHorizontalStrut(5));
|
||||||
|
ruleInfoPanel.add(reloadButton);
|
||||||
|
ruleInfoPanel.add(Box.createHorizontalStrut(5));
|
||||||
|
ruleInfoPanel.add(updateButton);
|
||||||
|
|
||||||
rulesFilePathTextField.setEditable(false);
|
|
||||||
|
|
||||||
onlineUpdateButton.addActionListener(this::onlineUpdateActionPerformed);
|
|
||||||
excludeSuffixSaveButton.addActionListener(e -> excludeSuffixSaveActionPerformed(e, excludeSuffixTextField.getText()));
|
|
||||||
reloadButton.addActionListener(this::reloadActionPerformed);
|
reloadButton.addActionListener(this::reloadActionPerformed);
|
||||||
|
updateButton.addActionListener(this::onlineUpdateActionPerformed);
|
||||||
|
|
||||||
rulesFilePathTextField.setText(configLoader.getRulesFilePath());
|
JPanel settingPanel = new JPanel(new BorderLayout());
|
||||||
excludeSuffixTextField.setText(configLoader.getExcludeSuffix());
|
DefaultTableModel model = new DefaultTableModel();
|
||||||
|
|
||||||
add(rulesFilePathTextField, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
|
JTable table = new JTable(model);
|
||||||
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
model.addColumn("Value");
|
||||||
new Insets(5, 0, 5, 5), 0, 0));
|
JScrollPane scrollPane = new JScrollPane(table);
|
||||||
add(rulesFilePathLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
|
|
||||||
GridBagConstraints.WEST, GridBagConstraints.VERTICAL,
|
JPanel buttonPanel = new JPanel();
|
||||||
new Insets(5, 5, 5, 5), 0, 0));
|
buttonPanel.setBorder(new EmptyBorder(0, 3, 0, 0));
|
||||||
add(onlineUpdateButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
|
GridBagLayout layout = new GridBagLayout();
|
||||||
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
layout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0};
|
||||||
new Insets(5, 0, 5, 5), 0, 0));
|
layout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
|
||||||
add(reloadButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0,
|
buttonPanel.setLayout(layout);
|
||||||
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
|
|
||||||
new Insets(5, 0, 5, 5), 0, 0));
|
JPanel inputPanel = new JPanel(new BorderLayout());
|
||||||
add(excludeSuffixLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
|
JPanel inputPanelB = new JPanel(new BorderLayout());
|
||||||
GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE,
|
inputPanelB.setBorder(new EmptyBorder(0, 0, 3, 0));
|
||||||
new Insets(0, 5, 5, 5), 0, 0));
|
|
||||||
add(excludeSuffixTextField, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0,
|
constraints.gridx = 1;
|
||||||
GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL,
|
JButton addButton = new JButton("Add");
|
||||||
new Insets(0, 0, 0, 5), 0, 0));
|
JButton removeButton = new JButton("Remove");
|
||||||
add(excludeSuffixSaveButton, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0,
|
JButton pasteButton = new JButton("Paste");
|
||||||
GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL,
|
JButton clearButton = new JButton("Clear");
|
||||||
new Insets(0, 0, 0, 5), 0, 0));
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected.equals("Block host")) {
|
||||||
|
addDataToTable(configLoader.getBlockHost().replaceAll("\\|", "\r\n"), model);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTypeComboBox.setSelectedItem("Exclude suffix");
|
||||||
|
|
||||||
|
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.getExcludeSuffix()) && !values.isEmpty()) {
|
||||||
|
configLoader.setBlockHost(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
constraints.insets = new Insets(0, 0, 3, 0);
|
||||||
|
constraints.gridy = 0;
|
||||||
|
buttonPanel.add(setTypeComboBox, constraints);
|
||||||
|
constraints.gridy = 1;
|
||||||
|
buttonPanel.add(addButton, constraints);
|
||||||
|
constraints.gridy = 2;
|
||||||
|
buttonPanel.add(removeButton, constraints);
|
||||||
|
constraints.gridy = 3;
|
||||||
|
buttonPanel.add(pasteButton, constraints);
|
||||||
|
constraints.gridy = 4;
|
||||||
|
buttonPanel.add(clearButton, constraints);
|
||||||
|
|
||||||
|
JTextField addTextField = new JTextField();
|
||||||
|
String defaultText = "Enter a new item";
|
||||||
|
UIEnhancer.setTextFieldPlaceholder(addTextField, defaultText);
|
||||||
|
|
||||||
|
inputPanelB.add(addTextField, BorderLayout.CENTER);
|
||||||
|
inputPanel.add(scrollPane, BorderLayout.CENTER);
|
||||||
|
inputPanel.add(inputPanelB, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
settingPanel.add(buttonPanel, BorderLayout.EAST);
|
||||||
|
settingPanel.add(inputPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
addButton.addActionListener(e -> {
|
||||||
|
String addTextFieldText = addTextField.getText();
|
||||||
|
if (!addTextFieldText.equals(defaultText)) {
|
||||||
|
addDataToTable(addTextFieldText, model);
|
||||||
|
}
|
||||||
|
addTextField.setText("");
|
||||||
|
addTextField.requestFocusInWindow();
|
||||||
|
});
|
||||||
|
|
||||||
|
pasteButton.addActionListener(e -> {
|
||||||
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||||
|
try {
|
||||||
|
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
|
||||||
|
|
||||||
|
if (data != null && !data.isEmpty()) {
|
||||||
|
addDataToTable(data, model);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
removeButton.addActionListener(e -> {
|
||||||
|
int selectedRow = table.getSelectedRow();
|
||||||
|
if (selectedRow != -1) {
|
||||||
|
model.removeRow(selectedRow);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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, 5, 10, 5));
|
||||||
|
settingMainPanel.add(settingLabelPanel, BorderLayout.NORTH);
|
||||||
|
settingMainPanel.add(settingPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
add(ruleInfoPanel, BorderLayout.NORTH);
|
||||||
|
add(settingMainPanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFirstColumnDataAsString(DefaultTableModel model) {
|
||||||
|
StringBuilder firstColumnData = new StringBuilder();
|
||||||
|
int numRows = model.getRowCount();
|
||||||
|
|
||||||
|
for (int row = 0; row < numRows; row++) {
|
||||||
|
firstColumnData.append(model.getValueAt(row, 0));
|
||||||
|
if (row < numRows - 1) {
|
||||||
|
firstColumnData.append("|");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstColumnData.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDataToTable(String data, DefaultTableModel model) {
|
||||||
|
if (!data.isBlank()) {
|
||||||
|
String[] rows = data.split("\\r?\\n");
|
||||||
|
for (String row : rows) {
|
||||||
|
model.addRow(new String[]{row});
|
||||||
|
}
|
||||||
|
deduplicateTableData(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deduplicateTableData(DefaultTableModel model) {
|
||||||
|
// 使用 Map 存储每一行的数据,用于去重
|
||||||
|
Set<List<Object>> rowData = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
int columnCount = model.getColumnCount();
|
||||||
|
|
||||||
|
// 将每一行数据作为一个列表,添加到 Set 中
|
||||||
|
for (int i = 0; i < model.getRowCount(); i++) {
|
||||||
|
List<Object> row = new ArrayList<>();
|
||||||
|
for (int j = 0; j < columnCount; j++) {
|
||||||
|
row.add(model.getValueAt(i, j));
|
||||||
|
}
|
||||||
|
rowData.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除原始数据
|
||||||
|
model.setRowCount(0);
|
||||||
|
|
||||||
|
// 将去重后的数据添加回去
|
||||||
|
for (List<Object> uniqueRow : rowData) {
|
||||||
|
model.addRow(uniqueRow.toArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onlineUpdateActionPerformed(ActionEvent e) {
|
private void onlineUpdateActionPerformed(ActionEvent e) {
|
||||||
// 添加提示框防止用户误触导致配置更新
|
// 添加提示框防止用户误触导致配置更新
|
||||||
int retCode = JOptionPane.showConfirmDialog(null, "Do you want to update rules?", "Info", JOptionPane.YES_NO_OPTION);
|
int retCode = JOptionPane.showConfirmDialog(null, "Do you want to update rules?", "Info", JOptionPane.YES_NO_OPTION);
|
||||||
if (retCode == JOptionPane.YES_OPTION) {
|
if (retCode == JOptionPane.YES_OPTION) {
|
||||||
configLoader.initRules();
|
configLoader.initRulesByNet();
|
||||||
reloadActionPerformed(null);
|
reloadActionPerformed(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void excludeSuffixSaveActionPerformed(ActionEvent e, String suffix) {
|
|
||||||
if (!suffix.equals(configLoader.getExcludeSuffix()) && !suffix.isEmpty()) {
|
|
||||||
configLoader.setExcludeSuffix(suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reloadActionPerformed(ActionEvent e) {
|
private void reloadActionPerformed(ActionEvent e) {
|
||||||
rules.reloadRuleGroup();
|
rules.reloadRuleGroup();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor;
|
|||||||
import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider;
|
import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider;
|
||||||
import hae.component.board.Datatable;
|
import hae.component.board.Datatable;
|
||||||
import hae.instances.http.utils.MessageProcessor;
|
import hae.instances.http.utils.MessageProcessor;
|
||||||
|
import hae.utils.config.ConfigLoader;
|
||||||
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -20,26 +22,31 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class RequestEditor implements HttpRequestEditorProvider {
|
public class RequestEditor implements HttpRequestEditorProvider {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
|
|
||||||
public RequestEditor(MontoyaApi api) {
|
public RequestEditor(MontoyaApi api, ConfigLoader configLoader) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExtensionProvidedHttpRequestEditor provideHttpRequestEditor(EditorCreationContext editorCreationContext) {
|
public ExtensionProvidedHttpRequestEditor provideHttpRequestEditor(EditorCreationContext editorCreationContext) {
|
||||||
return new Editor(api, editorCreationContext);
|
return new Editor(api, configLoader, editorCreationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Editor implements ExtensionProvidedHttpRequestEditor {
|
private static class Editor implements ExtensionProvidedHttpRequestEditor {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
private final EditorCreationContext creationContext;
|
private final EditorCreationContext creationContext;
|
||||||
private final MessageProcessor messageProcessor;
|
private final MessageProcessor messageProcessor;
|
||||||
private HttpRequestResponse requestResponse;
|
private HttpRequestResponse requestResponse;
|
||||||
|
private List<Map<String, String>> dataList;
|
||||||
|
|
||||||
private final JTabbedPane jTabbedPane = new JTabbedPane();
|
private final JTabbedPane jTabbedPane = new JTabbedPane();
|
||||||
|
|
||||||
public Editor(MontoyaApi api, EditorCreationContext creationContext) {
|
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
this.creationContext = creationContext;
|
this.creationContext = creationContext;
|
||||||
this.messageProcessor = new MessageProcessor(api);
|
this.messageProcessor = new MessageProcessor(api);
|
||||||
}
|
}
|
||||||
@@ -52,15 +59,29 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void setRequestResponse(HttpRequestResponse requestResponse) {
|
public void setRequestResponse(HttpRequestResponse requestResponse) {
|
||||||
this.requestResponse = requestResponse;
|
this.requestResponse = requestResponse;
|
||||||
|
generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean isEnabledFor(HttpRequestResponse requestResponse) {
|
public synchronized boolean isEnabledFor(HttpRequestResponse requestResponse) {
|
||||||
HttpRequest request = requestResponse.request();
|
HttpRequest request = requestResponse.request();
|
||||||
if (request != null && !request.bodyToString().equals("Loading...")) {
|
if (request != null) {
|
||||||
List<Map<String, String>> result = messageProcessor.processRequest("", request, false);
|
try {
|
||||||
generateTabbedPaneFromResultMap(api, jTabbedPane, result);
|
String host = StringProcessor.getHostByUrl(request.url());
|
||||||
return jTabbedPane.getTabCount() > 0;
|
if (!host.isEmpty()) {
|
||||||
|
String[] hostList = configLoader.getBlockHost().split("\\|");
|
||||||
|
boolean isBlockHost = isBlockHost(hostList, host);
|
||||||
|
|
||||||
|
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
||||||
|
boolean matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost;
|
||||||
|
|
||||||
|
if (!matches && !request.bodyToString().equals("Loading...")) {
|
||||||
|
this.dataList = messageProcessor.processRequest("", request, false);
|
||||||
|
return isListHasData(this.dataList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -97,11 +118,30 @@ public class RequestEditor implements HttpRequestEditorProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isBlockHost(String[] hostList, String host) {
|
||||||
|
boolean isBlockHost = false;
|
||||||
|
for (String hostName : hostList) {
|
||||||
|
String cleanedHost = StringProcessor.replaceFirstOccurrence(hostName, "*.", "");
|
||||||
|
if (StringProcessor.matchFromEnd(host, cleanedHost)) {
|
||||||
|
isBlockHost = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isBlockHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isListHasData(List<Map<String, String>> dataList) {
|
||||||
|
if (dataList != null && !dataList.isEmpty()) {
|
||||||
|
Map<String, String> dataMap = dataList.get(0);
|
||||||
|
return dataMap != null && !dataMap.isEmpty();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static void generateTabbedPaneFromResultMap(MontoyaApi api, JTabbedPane tabbedPane, List<Map<String, String>> result) {
|
public static void generateTabbedPaneFromResultMap(MontoyaApi api, JTabbedPane tabbedPane, List<Map<String, String>> result) {
|
||||||
tabbedPane.removeAll();
|
tabbedPane.removeAll();
|
||||||
if (result != null && !result.isEmpty() && result.size() > 0) {
|
if (result != null && !result.isEmpty()) {
|
||||||
Map<String, String> dataMap = result.get(0);
|
Map<String, String> dataMap = result.get(0);
|
||||||
if (dataMap != null && !dataMap.isEmpty() && dataMap.size() > 0) {
|
if (dataMap != null && !dataMap.isEmpty()) {
|
||||||
dataMap.keySet().forEach(i -> {
|
dataMap.keySet().forEach(i -> {
|
||||||
String[] extractData = dataMap.get(i).split("\n");
|
String[] extractData = dataMap.get(i).split("\n");
|
||||||
Datatable dataPanel = new Datatable(api, i, Arrays.asList(extractData));
|
Datatable dataPanel = new Datatable(api, i, Arrays.asList(extractData));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import burp.api.montoya.MontoyaApi;
|
|||||||
import burp.api.montoya.core.ByteArray;
|
import burp.api.montoya.core.ByteArray;
|
||||||
import burp.api.montoya.core.Range;
|
import burp.api.montoya.core.Range;
|
||||||
import burp.api.montoya.http.message.HttpRequestResponse;
|
import burp.api.montoya.http.message.HttpRequestResponse;
|
||||||
|
import burp.api.montoya.http.message.requests.HttpRequest;
|
||||||
import burp.api.montoya.http.message.responses.HttpResponse;
|
import burp.api.montoya.http.message.responses.HttpResponse;
|
||||||
import burp.api.montoya.ui.Selection;
|
import burp.api.montoya.ui.Selection;
|
||||||
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
|
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
|
||||||
@@ -11,34 +12,42 @@ import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpResponseEditor;
|
|||||||
import burp.api.montoya.ui.editor.extension.HttpResponseEditorProvider;
|
import burp.api.montoya.ui.editor.extension.HttpResponseEditorProvider;
|
||||||
import hae.component.board.Datatable;
|
import hae.component.board.Datatable;
|
||||||
import hae.instances.http.utils.MessageProcessor;
|
import hae.instances.http.utils.MessageProcessor;
|
||||||
|
import hae.utils.config.ConfigLoader;
|
||||||
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ResponseEditor implements HttpResponseEditorProvider {
|
public class ResponseEditor implements HttpResponseEditorProvider {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
|
|
||||||
public ResponseEditor(MontoyaApi api) {
|
public ResponseEditor(MontoyaApi api, ConfigLoader configLoader) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExtensionProvidedHttpResponseEditor provideHttpResponseEditor(EditorCreationContext editorCreationContext) {
|
public ExtensionProvidedHttpResponseEditor provideHttpResponseEditor(EditorCreationContext editorCreationContext) {
|
||||||
return new Editor(api, editorCreationContext);
|
return new Editor(api, configLoader, editorCreationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Editor implements ExtensionProvidedHttpResponseEditor {
|
private static class Editor implements ExtensionProvidedHttpResponseEditor {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
private final EditorCreationContext creationContext;
|
private final EditorCreationContext creationContext;
|
||||||
private final MessageProcessor messageProcessor;
|
private final MessageProcessor messageProcessor;
|
||||||
private HttpRequestResponse requestResponse;
|
private HttpRequestResponse requestResponse;
|
||||||
|
private List<Map<String, String>> dataList;
|
||||||
|
|
||||||
private final JTabbedPane jTabbedPane = new JTabbedPane();
|
private final JTabbedPane jTabbedPane = new JTabbedPane();
|
||||||
|
|
||||||
public Editor(MontoyaApi api, EditorCreationContext creationContext) {
|
public Editor(MontoyaApi api, ConfigLoader configLoader, EditorCreationContext creationContext) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
this.creationContext = creationContext;
|
this.creationContext = creationContext;
|
||||||
this.messageProcessor = new MessageProcessor(api);
|
this.messageProcessor = new MessageProcessor(api);
|
||||||
}
|
}
|
||||||
@@ -51,16 +60,37 @@ public class ResponseEditor implements HttpResponseEditorProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void setRequestResponse(HttpRequestResponse requestResponse) {
|
public void setRequestResponse(HttpRequestResponse requestResponse) {
|
||||||
this.requestResponse = requestResponse;
|
this.requestResponse = requestResponse;
|
||||||
|
RequestEditor.generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean isEnabledFor(HttpRequestResponse requestResponse) {
|
public synchronized boolean isEnabledFor(HttpRequestResponse requestResponse) {
|
||||||
HttpResponse request = requestResponse.response();
|
HttpResponse response = requestResponse.response();
|
||||||
if (request != null && !request.bodyToString().equals("Loading...")) {
|
|
||||||
List<Map<String, String>> result = messageProcessor.processResponse("", request, false);
|
if (response != null) {
|
||||||
RequestEditor.generateTabbedPaneFromResultMap(api, jTabbedPane, result);
|
HttpRequest request = requestResponse.request();
|
||||||
return jTabbedPane.getTabCount() > 0;
|
boolean matches = false;
|
||||||
|
|
||||||
|
if (request != null) {
|
||||||
|
try {
|
||||||
|
String host = StringProcessor.getHostByUrl(request.url());
|
||||||
|
if (!host.isEmpty()) {
|
||||||
|
String[] hostList = configLoader.getBlockHost().split("\\|");
|
||||||
|
boolean isBlockHost = RequestEditor.isBlockHost(hostList, host);
|
||||||
|
|
||||||
|
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
||||||
|
matches = suffixList.contains(request.fileExtension().toLowerCase()) || isBlockHost;
|
||||||
}
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matches && !response.bodyToString().equals("Loading...")) {
|
||||||
|
this.dataList = messageProcessor.processResponse("", response, false);
|
||||||
|
return RequestEditor.isListHasData(this.dataList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public class WebSocketEditor implements WebSocketMessageEditorProvider {
|
|||||||
private final EditorCreationContext creationContext;
|
private final EditorCreationContext creationContext;
|
||||||
private final MessageProcessor messageProcessor;
|
private final MessageProcessor messageProcessor;
|
||||||
private ByteArray message;
|
private ByteArray message;
|
||||||
|
private List<Map<String, String>> dataList;
|
||||||
|
|
||||||
private final JTabbedPane jTabbedPane = new JTabbedPane();
|
private final JTabbedPane jTabbedPane = new JTabbedPane();
|
||||||
|
|
||||||
@@ -50,15 +51,15 @@ public class WebSocketEditor implements WebSocketMessageEditorProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void setMessage(WebSocketMessage webSocketMessage) {
|
public void setMessage(WebSocketMessage webSocketMessage) {
|
||||||
this.message = webSocketMessage.payload();
|
this.message = webSocketMessage.payload();
|
||||||
|
RequestEditor.generateTabbedPaneFromResultMap(api, jTabbedPane, this.dataList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledFor(WebSocketMessage webSocketMessage) {
|
public boolean isEnabledFor(WebSocketMessage webSocketMessage) {
|
||||||
String websocketMessage = webSocketMessage.payload().toString();
|
String websocketMessage = webSocketMessage.payload().toString();
|
||||||
if (!websocketMessage.isEmpty()) {
|
if (!websocketMessage.isEmpty()) {
|
||||||
List<Map<String, String>> result = messageProcessor.processMessage("", websocketMessage, false);
|
this.dataList = messageProcessor.processMessage("", websocketMessage, false);
|
||||||
RequestEditor.generateTabbedPaneFromResultMap(api, jTabbedPane, result);
|
return RequestEditor.isListHasData(this.dataList);
|
||||||
return jTabbedPane.getTabCount() > 0;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import burp.api.montoya.core.HighlightColor;
|
|||||||
import burp.api.montoya.http.handler.*;
|
import burp.api.montoya.http.handler.*;
|
||||||
import burp.api.montoya.http.message.HttpRequestResponse;
|
import burp.api.montoya.http.message.HttpRequestResponse;
|
||||||
import burp.api.montoya.http.message.requests.HttpRequest;
|
import burp.api.montoya.http.message.requests.HttpRequest;
|
||||||
import hae.Config;
|
|
||||||
import hae.component.board.message.MessageTableModel;
|
import hae.component.board.message.MessageTableModel;
|
||||||
|
import hae.instances.editor.RequestEditor;
|
||||||
import hae.instances.http.utils.MessageProcessor;
|
import hae.instances.http.utils.MessageProcessor;
|
||||||
|
import hae.utils.config.ConfigLoader;
|
||||||
import hae.utils.string.StringProcessor;
|
import hae.utils.string.StringProcessor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -18,6 +19,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class HttpMessageHandler implements HttpHandler {
|
public class HttpMessageHandler implements HttpHandler {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
|
private final ConfigLoader configLoader;
|
||||||
private final MessageTableModel messageTableModel;
|
private final MessageTableModel messageTableModel;
|
||||||
private final MessageProcessor messageProcessor;
|
private final MessageProcessor messageProcessor;
|
||||||
|
|
||||||
@@ -29,8 +31,9 @@ public class HttpMessageHandler implements HttpHandler {
|
|||||||
private final ThreadLocal<Boolean> matches = ThreadLocal.withInitial(() -> false);
|
private final ThreadLocal<Boolean> matches = ThreadLocal.withInitial(() -> false);
|
||||||
private final ThreadLocal<HttpRequest> httpRequest = new ThreadLocal<>();
|
private final ThreadLocal<HttpRequest> httpRequest = new ThreadLocal<>();
|
||||||
|
|
||||||
public HttpMessageHandler(MontoyaApi api, MessageTableModel messageTableModel) {
|
public HttpMessageHandler(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.configLoader = configLoader;
|
||||||
this.messageTableModel = messageTableModel;
|
this.messageTableModel = messageTableModel;
|
||||||
this.messageProcessor = new MessageProcessor(api);
|
this.messageProcessor = new MessageProcessor(api);
|
||||||
}
|
}
|
||||||
@@ -46,8 +49,11 @@ public class HttpMessageHandler implements HttpHandler {
|
|||||||
|
|
||||||
host.set(StringProcessor.getHostByUrl(httpRequestToBeSent.url()));
|
host.set(StringProcessor.getHostByUrl(httpRequestToBeSent.url()));
|
||||||
|
|
||||||
List<String> suffixList = Arrays.asList(Config.suffix.split("\\|"));
|
String[] hostList = configLoader.getBlockHost().split("\\|");
|
||||||
matches.set(suffixList.contains(httpRequestToBeSent.fileExtension()));
|
boolean isBlockHost = RequestEditor.isBlockHost(hostList, host.get());
|
||||||
|
|
||||||
|
List<String> suffixList = Arrays.asList(configLoader.getExcludeSuffix().split("\\|"));
|
||||||
|
matches.set(suffixList.contains(httpRequestToBeSent.fileExtension().toLowerCase()) || isBlockHost);
|
||||||
|
|
||||||
if (!matches.get()) {
|
if (!matches.get()) {
|
||||||
List<Map<String, String>> result = messageProcessor.processRequest(host.get(), httpRequestToBeSent, true);
|
List<Map<String, String>> result = messageProcessor.processRequest(host.get(), httpRequestToBeSent, true);
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import hae.Config;
|
|||||||
import hae.cache.CachePool;
|
import hae.cache.CachePool;
|
||||||
import hae.utils.string.HashCalculator;
|
import hae.utils.string.HashCalculator;
|
||||||
import hae.utils.string.StringProcessor;
|
import hae.utils.string.StringProcessor;
|
||||||
import jregex.Matcher;
|
|
||||||
import jregex.Pattern;
|
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class RegularMatcher {
|
public class RegularMatcher {
|
||||||
private final MontoyaApi api;
|
private final MontoyaApi api;
|
||||||
@@ -27,7 +27,7 @@ public class RegularMatcher {
|
|||||||
public Map<String, Map<String, Object>> match(String host, String type, String message, String header, String body) {
|
public Map<String, Map<String, Object>> match(String host, String type, String message, String header, String body) {
|
||||||
// 先从缓存池里判断是否有已经匹配好的结果
|
// 先从缓存池里判断是否有已经匹配好的结果
|
||||||
String messageIndex = HashCalculator.calculateHash(message.getBytes());
|
String messageIndex = HashCalculator.calculateHash(message.getBytes());
|
||||||
Map<String, Map<String, Object>> map = CachePool.getFromCache(messageIndex);
|
Map<String, Map<String, Object>> map = CachePool.get(messageIndex);
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
return map;
|
return map;
|
||||||
} else {
|
} else {
|
||||||
@@ -81,6 +81,7 @@ public class RegularMatcher {
|
|||||||
result.addAll(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive));
|
result.addAll(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
api.logging().logToError(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex));
|
api.logging().logToError(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex));
|
||||||
|
api.logging().logToError(e.getMessage());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,27 +99,18 @@ public class RegularMatcher {
|
|||||||
// 添加到全局变量中,便于Databoard检索
|
// 添加到全局变量中,便于Databoard检索
|
||||||
if (!Objects.equals(host, "") && host != null) {
|
if (!Objects.equals(host, "") && host != null) {
|
||||||
List<String> dataList = Arrays.asList(dataStr.split("\n"));
|
List<String> dataList = Arrays.asList(dataStr.split("\n"));
|
||||||
if (Config.globalDataMap.containsKey(host)) {
|
|
||||||
ConcurrentHashMap<String, List<String>> gRuleMap = new ConcurrentHashMap<>(Config.globalDataMap.get(host));
|
Config.globalDataMap.compute(host, (existingHost, existingMap) -> {
|
||||||
if (gRuleMap.containsKey(name)) {
|
Map<String, List<String>> gRuleMap = Optional.ofNullable(existingMap).orElse(new ConcurrentHashMap<>());
|
||||||
// gDataList为不可变列表,因此需要重新创建一个列表以便于使用addAll方法
|
|
||||||
List<String> gDataList = gRuleMap.get(name);
|
gRuleMap.merge(name, new ArrayList<>(dataList), (existingList, newList) -> {
|
||||||
List<String> newDataList = new ArrayList<>(gDataList);
|
Set<String> combinedSet = new LinkedHashSet<>(existingList);
|
||||||
newDataList.addAll(dataList);
|
combinedSet.addAll(newList);
|
||||||
newDataList = new ArrayList<>(new HashSet<>(newDataList));
|
return new ArrayList<>(combinedSet);
|
||||||
gRuleMap.remove(name);
|
});
|
||||||
gRuleMap.put(name, newDataList);
|
|
||||||
} else {
|
return gRuleMap;
|
||||||
gRuleMap.put(name, dataList);
|
});
|
||||||
}
|
|
||||||
Config.globalDataMap.remove(host);
|
|
||||||
Config.globalDataMap.put(host, gRuleMap);
|
|
||||||
} else {
|
|
||||||
Map<String, List<String>> ruleMap = new HashMap<>();
|
|
||||||
ruleMap.put(name, dataList);
|
|
||||||
// 添加单一Host
|
|
||||||
Config.globalDataMap.put(host, ruleMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] splitHost = host.split("\\.");
|
String[] splitHost = host.split("\\.");
|
||||||
String onlyHost = host.split(":")[0];
|
String onlyHost = host.split(":")[0];
|
||||||
@@ -139,7 +131,7 @@ public class RegularMatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
CachePool.addToCache(messageIndex, finalMap);
|
CachePool.put(messageIndex, finalMap);
|
||||||
return finalMap;
|
return finalMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,7 +221,7 @@ public class RegularMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Matcher createPatternMatcher(String regex, String content, boolean sensitive) {
|
private Matcher createPatternMatcher(String regex, String content, boolean sensitive) {
|
||||||
Pattern pattern = (sensitive) ? new Pattern(regex) : new Pattern(regex, Pattern.IGNORE_CASE);
|
Pattern pattern = sensitive ? Pattern.compile(regex) : Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
|
||||||
return pattern.matcher(content);
|
return pattern.matcher(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +234,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 = new Pattern("\\{(\\d+)}");
|
Pattern pattern = Pattern.compile("\\{(\\d+)}");
|
||||||
Matcher matcher = pattern.matcher(input);
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
@@ -264,7 +256,7 @@ public class RegularMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String reorderIndex(String format) {
|
private String reorderIndex(String format) {
|
||||||
Pattern pattern = new Pattern("\\{(\\d+)}");
|
Pattern pattern = Pattern.compile("\\{(\\d+)}");
|
||||||
Matcher matcher = pattern.matcher(format);
|
Matcher matcher = pattern.matcher(format);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package hae.utils.config;
|
package hae.utils.config;
|
||||||
|
|
||||||
import burp.api.montoya.MontoyaApi;
|
import burp.api.montoya.MontoyaApi;
|
||||||
|
import burp.api.montoya.http.RequestOptions;
|
||||||
import burp.api.montoya.http.message.HttpRequestResponse;
|
import burp.api.montoya.http.message.HttpRequestResponse;
|
||||||
import burp.api.montoya.http.message.requests.HttpRequest;
|
import burp.api.montoya.http.message.requests.HttpRequest;
|
||||||
import hae.Config;
|
import hae.Config;
|
||||||
@@ -11,6 +12,7 @@ import org.yaml.snakeyaml.representer.Representer;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ public class ConfigLoader {
|
|||||||
|
|
||||||
File rulesFilePath = new File(this.rulesFilePath);
|
File rulesFilePath = new File(this.rulesFilePath);
|
||||||
if (!(rulesFilePath.exists() && rulesFilePath.isFile())) {
|
if (!(rulesFilePath.exists() && rulesFilePath.isFile())) {
|
||||||
initRules();
|
initRulesByRes();
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.globalRules = getRules();
|
Config.globalRules = getRules();
|
||||||
@@ -76,6 +78,7 @@ public class ConfigLoader {
|
|||||||
public void initConfig() {
|
public void initConfig() {
|
||||||
Map<String, Object> r = new LinkedHashMap<>();
|
Map<String, Object> r = new LinkedHashMap<>();
|
||||||
r.put("excludeSuffix", getExcludeSuffix());
|
r.put("excludeSuffix", getExcludeSuffix());
|
||||||
|
r.put("blockHost", getBlockHost());
|
||||||
try {
|
try {
|
||||||
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
|
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
|
||||||
yaml.dump(r, ws);
|
yaml.dump(r, ws);
|
||||||
@@ -88,24 +91,6 @@ public class ConfigLoader {
|
|||||||
return rulesFilePath;
|
return rulesFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getExcludeSuffix() {
|
|
||||||
File yamlSetting = new File(configFilePath);
|
|
||||||
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
|
|
||||||
return Config.suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return Config.suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取规则配置
|
// 获取规则配置
|
||||||
public Map<String, Object[][]> getRules() {
|
public Map<String, Object[][]> getRules() {
|
||||||
Map<String, Object[][]> rules = new HashMap<>();
|
Map<String, Object[][]> rules = new HashMap<>();
|
||||||
@@ -149,18 +134,104 @@ public class ConfigLoader {
|
|||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExcludeSuffix(String excludeSuffix) {
|
public String getBlockHost() {
|
||||||
Map<String, Object> r = new LinkedHashMap<>();
|
File yamlSetting = new File(configFilePath);
|
||||||
r.put("excludeSuffix", excludeSuffix);
|
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
|
||||||
try {
|
return Config.host;
|
||||||
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(configFilePath)), StandardCharsets.UTF_8);
|
}
|
||||||
yaml.dump(r, ws);
|
|
||||||
ws.close();
|
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) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Config.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExcludeSuffix() {
|
||||||
|
File yamlSetting = new File(configFilePath);
|
||||||
|
if (!yamlSetting.exists() || !yamlSetting.isFile()) {
|
||||||
|
return Config.suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return Config.suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> loadCurrentConfig() {
|
||||||
|
Path path = Paths.get(configFilePath);
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
return new LinkedHashMap<>(); // 返回空的Map,表示没有当前配置
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream in = Files.newInputStream(path)) {
|
||||||
|
return yaml.load(in);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return new LinkedHashMap<>(); // 读取失败时也返回空的Map
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initRules() {
|
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 (IOException 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 (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initRulesByRes() {
|
||||||
|
boolean isCopySuccess = copyRulesToFile(this.rulesFilePath);
|
||||||
|
if (!isCopySuccess) {
|
||||||
|
api.extension().unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean copyRulesToFile(String targetFilePath) {
|
||||||
|
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("rules/Rules.yml");
|
||||||
|
File targetFile = new File(targetFilePath);
|
||||||
|
|
||||||
|
try (inputStream; OutputStream outputStream = new FileOutputStream(targetFile)) {
|
||||||
|
if (inputStream != null) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int length;
|
||||||
|
|
||||||
|
while ((length = inputStream.read(buffer)) > 0) {
|
||||||
|
outputStream.write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initRulesByNet() {
|
||||||
Thread t = new Thread() {
|
Thread t = new Thread() {
|
||||||
public void run() {
|
public void run() {
|
||||||
pullRules();
|
pullRules();
|
||||||
@@ -177,7 +248,7 @@ public class ConfigLoader {
|
|||||||
try {
|
try {
|
||||||
String url = "https://raw.githubusercontent.com/gh0stkey/HaE/gh-pages/Rules.yml";
|
String url = "https://raw.githubusercontent.com/gh0stkey/HaE/gh-pages/Rules.yml";
|
||||||
HttpRequest httpRequest = HttpRequest.httpRequestFromUrl(url);
|
HttpRequest httpRequest = HttpRequest.httpRequestFromUrl(url);
|
||||||
HttpRequestResponse requestResponse = api.http().sendRequest(httpRequest);
|
HttpRequestResponse requestResponse = api.http().sendRequest(httpRequest, RequestOptions.requestOptions().withUpstreamTLSVerification());
|
||||||
String responseBody = requestResponse.response().bodyToString();
|
String responseBody = requestResponse.response().bodyToString();
|
||||||
if (responseBody.contains("rules")) {
|
if (responseBody.contains("rules")) {
|
||||||
FileOutputStream fileOutputStream = new FileOutputStream(rulesFilePath);
|
FileOutputStream fileOutputStream = new FileOutputStream(rulesFilePath);
|
||||||
|
|||||||
30
src/main/java/hae/utils/ui/UIEnhancer.java
Normal file
30
src/main/java/hae/utils/ui/UIEnhancer.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package hae.utils.ui;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
|
|
||||||
|
public class UIEnhancer {
|
||||||
|
public static void setTextFieldPlaceholder(JTextField textField, String placeholderText) {
|
||||||
|
textField.setForeground(Color.GRAY);
|
||||||
|
textField.setText(placeholderText);
|
||||||
|
textField.addFocusListener(new FocusListener() {
|
||||||
|
@Override
|
||||||
|
public void focusGained(FocusEvent e) {
|
||||||
|
if (textField.getText().equals(placeholderText)) {
|
||||||
|
textField.setText("");
|
||||||
|
textField.setForeground(Color.BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost(FocusEvent e) {
|
||||||
|
if (textField.getText().isEmpty()) {
|
||||||
|
textField.setForeground(Color.GRAY);
|
||||||
|
textField.setText(placeholderText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
284
src/main/resources/rules/Rules.yml
Normal file
284
src/main/resources/rules/Rules.yml
Normal file
@@ -0,0 +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
|
||||||
Reference in New Issue
Block a user