diff --git a/src/main/java/hae/HaE.java b/src/main/java/hae/HaE.java index 70efdd4..dbdd608 100644 --- a/src/main/java/hae/HaE.java +++ b/src/main/java/hae/HaE.java @@ -2,6 +2,7 @@ package hae; import burp.api.montoya.BurpExtension; import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.BurpSuiteEdition; import burp.api.montoya.logging.Logging; import hae.cache.DataCache; import hae.component.Main; @@ -9,7 +10,6 @@ import hae.component.board.message.MessageTableModel; import hae.instances.editor.RequestEditor; import hae.instances.editor.ResponseEditor; import hae.instances.editor.WebSocketEditor; -import hae.instances.http.HttpMessagePassiveHandler; import hae.instances.websocket.WebSocketMessageHandler; import hae.utils.ConfigLoader; import hae.utils.DataManager; @@ -19,7 +19,7 @@ public class HaE implements BurpExtension { public void initialize(MontoyaApi api) { // 设置扩展名称 api.extension().setName("HaE - Highlighter and Extractor"); - String version = "4.2.1"; + String version = "4.3"; // 加载扩展后输出的项目信息 Logging logging = api.logging(); @@ -34,7 +34,7 @@ public class HaE implements BurpExtension { MessageTableModel messageTableModel = new MessageTableModel(api, configLoader); // 设置BurpSuite专业版状态 - Config.proVersionStatus = getBurpSuiteProStatus(api, configLoader, messageTableModel); + Config.proVersionStatus = getBurpSuiteProStatus(api); // 注册Tab页(用于查询数据) api.userInterface().registerSuiteTab("HaE", new Main(api, configLoader, messageTableModel)); @@ -58,16 +58,12 @@ public class HaE implements BurpExtension { }); } - private Boolean getBurpSuiteProStatus(MontoyaApi api, ConfigLoader configLoader, MessageTableModel messageTableModel) { + private Boolean getBurpSuiteProStatus(MontoyaApi api) { boolean burpSuiteProStatus = false; + try { - burpSuiteProStatus = api.burpSuite().version().edition().displayName().equals("Professional"); - } catch (Exception e) { - try { - api.scanner().registerScanCheck(new HttpMessagePassiveHandler(api, configLoader, messageTableModel)).deregister(); - burpSuiteProStatus = true; - } catch (Exception ignored) { - } + burpSuiteProStatus = api.burpSuite().version().edition() == BurpSuiteEdition.PROFESSIONAL; + } catch (Exception ignored) { } return burpSuiteProStatus; diff --git a/src/main/java/hae/component/board/Databoard.java b/src/main/java/hae/component/board/Databoard.java index b5ae191..f3f3ef1 100644 --- a/src/main/java/hae/component/board/Databoard.java +++ b/src/main/java/hae/component/board/Databoard.java @@ -35,7 +35,7 @@ public class Databoard extends JPanel { private JSplitPane splitPane; private MessageTable messageTable; private JProgressBar progressBar; - private SwingWorker>, Void> handleComboBoxWorker; + private SwingWorker>, Integer> handleComboBoxWorker; private SwingWorker applyHostFilterWorker; 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)); } - private void setProgressBar(boolean status) { - progressBar.setIndeterminate(status); - if (!status) { - progressBar.setMaximum(100); - progressBar.setString("OK"); - progressBar.setStringPainted(true); + private void setProgressBar(boolean status, String message, int progress) { + progressBar.setIndeterminate(status && progress <= 0); + progressBar.setString(message); + progressBar.setStringPainted(true); + progressBar.setMaximum(100); + + if (progress > 0) { + progressBar.setValue(progress); + } else if (!status) { 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(); if (getHostByList().contains(selectedHost)) { - progressBar.setVisible(true); - setProgressBar(true); hostTextField.setText(selectedHost); + hostComboBox.setPopupVisible(false); if (handleComboBoxWorker != null && !handleComboBoxWorker.isDone()) { + progressBar.setVisible(false); handleComboBoxWorker.cancel(true); } - handleComboBoxWorker = new SwingWorker<>() { - @Override - protected Map> doInBackground() { - return getSelectedMapByHost(selectedHost); - } - - @Override - protected void done() { - if (!isCancelled()) { - try { - Map> selectedDataMap = get(); - if (!selectedDataMap.isEmpty()) { - dataTabbedPane.removeAll(); - - for (Map.Entry> 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 = new DataLoadingWorker(selectedHost); handleComboBoxWorker.execute(); } @@ -251,33 +212,57 @@ public class Databoard extends JPanel { isMatchHost = false; } - private Map> getSelectedMapByHost(String selectedHost) { + private Map> getSelectedMapByHost(String selectedHost, DataLoadingWorker worker) { ConcurrentHashMap>> dataMap = Config.globalDataMap; Map> selectedDataMap; if (selectedHost.contains("*")) { selectedDataMap = new HashMap<>(); - dataMap.keySet().forEach(key -> { + List matchingKeys = new ArrayList<>(); + + // 第一步:找出所有匹配的键(预处理) + for (String key : dataMap.keySet()) { if ((StringProcessor.matchesHostPattern(key, selectedHost) || selectedHost.equals("*")) && !key.contains("*")) { - Map> ruleMap = dataMap.get(key); + matchingKeys.add(key); + } + } + + // 第二步:分批处理数据 + int totalKeys = matchingKeys.size(); + for (int i = 0; i < totalKeys; i++) { + String key = matchingKeys.get(i); + Map> ruleMap = dataMap.get(key); + + if (ruleMap != null) { for (String ruleKey : ruleMap.keySet()) { List dataList = ruleMap.get(ruleKey); if (selectedDataMap.containsKey(ruleKey)) { List mergedList = new ArrayList<>(selectedDataMap.get(ruleKey)); mergedList.addAll(dataList); + // 使用HashSet去重 HashSet uniqueSet = new HashSet<>(mergedList); selectedDataMap.put(ruleKey, new ArrayList<>(uniqueSet)); } else { - selectedDataMap.put(ruleKey, dataList); + selectedDataMap.put(ruleKey, new ArrayList<>(dataList)); } } } - }); + + // 报告进度 + 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; + return selectedDataMap != null ? selectedDataMap : new HashMap<>(); } private void filterComboBoxList() { @@ -395,4 +380,67 @@ public class Databoard extends JPanel { hostTextField.setText(""); } } + + // 定义为内部类 + private class DataLoadingWorker extends SwingWorker>, Integer> { + private final String selectedHost; + + public DataLoadingWorker(String selectedHost) { + this.selectedHost = selectedHost; + progressBar.setVisible(true); + } + + @Override + protected Map> doInBackground() throws Exception { + return getSelectedMapByHost(selectedHost, this); + } + + @Override + protected void process(List chunks) { + if (!chunks.isEmpty()) { + int progress = chunks.get(chunks.size() - 1); + setProgressBar(true, "Loading... " + progress + "%", progress); + } + } + + @Override + protected void done() { + if (!isCancelled()) { + try { + Map> selectedDataMap = get(); + if (selectedDataMap != null && !selectedDataMap.isEmpty()) { + dataTabbedPane.removeAll(); + + for (Map.Entry> 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); + } + } } diff --git a/src/main/java/hae/component/board/message/MessageRenderer.java b/src/main/java/hae/component/board/message/MessageRenderer.java index 186efe2..23fd239 100644 --- a/src/main/java/hae/component/board/message/MessageRenderer.java +++ b/src/main/java/hae/component/board/message/MessageRenderer.java @@ -33,15 +33,32 @@ public class MessageRenderer extends DefaultTableCellRenderer { boolean hasFocus, int row, int 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(); Color color = colorMap.get(colorByLog); + // 如果颜色映射中没有找到对应颜色,使用默认白色 + if (color == null) { + color = Color.WHITE; + } + if (isSelected) { // 通过更改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 { // 否则使用原始颜色 component.setBackground(color); diff --git a/src/main/java/hae/component/board/message/MessageTableModel.java b/src/main/java/hae/component/board/message/MessageTableModel.java index ae01c61..81fa7e3 100644 --- a/src/main/java/hae/component/board/message/MessageTableModel.java +++ b/src/main/java/hae/component/board/message/MessageTableModel.java @@ -55,7 +55,6 @@ public class MessageTableModel extends AbstractTableModel { messageTable.setDefaultRenderer(Object.class, new MessageRenderer(filteredLog, messageTable)); messageTable.setAutoCreateRowSorter(true); - // Length字段根据大小进行排序 TableRowSorter sorter = getDefaultTableModelTableRowSorter(); messageTable.setRowSorter(sorter); messageTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); @@ -71,6 +70,8 @@ public class MessageTableModel extends AbstractTableModel { private TableRowSorter getDefaultTableModelTableRowSorter() { TableRowSorter sorter = (TableRowSorter) messageTable.getRowSorter(); + + // Length字段根据大小进行排序 sorter.setComparator(4, (Comparator) (s1, s2) -> { Integer age1 = Integer.parseInt(s1); Integer age2 = Integer.parseInt(s2); @@ -104,6 +105,14 @@ public class MessageTableModel extends AbstractTableModel { return; } + if (comment == null || comment.trim().isEmpty()) { + return; + } + + if (color == null || color.trim().isEmpty()) { + return; + } + boolean isDuplicate = false; try { if (!log.isEmpty() && flag) { @@ -241,131 +250,163 @@ public class MessageTableModel extends AbstractTableModel { } public void applyHostFilter(String filterText) { - filteredLog.clear(); - fireTableDataChanged(); + // 预分配合适的容量,避免频繁扩容 + final List newFilteredLog = new ArrayList<>(log.size() / 2); + + // 预处理过滤条件,优化性能 + final boolean isWildcardFilter = "*".equals(filterText) || filterText.contains("*"); + final String normalizedFilter = filterText.toLowerCase().trim(); + + // 创建log的安全副本 + final List logSnapshot; + synchronized (log) { + logSnapshot = new ArrayList<>(log); + } - int batchSize = 500; - - // 分批处理数据 - List batch = new ArrayList<>(batchSize); - int count = 0; - - for (MessageEntry entry : log) { - String host = StringProcessor.getHostByUrl(entry.getUrl()); - if (!host.isEmpty() && (StringProcessor.matchesHostPattern(host, filterText) || filterText.contains("*"))) { - batch.add(entry); - count++; - - // 当批次达到指定大小时,更新UI - if (count % batchSize == 0) { - final List currentBatch = new ArrayList<>(batch); - SwingUtilities.invokeLater(() -> { - filteredLog.addAll(currentBatch); - fireTableDataChanged(); - }); - batch.clear(); + // 使用并行流高效过滤,但保持有序 + logSnapshot.parallelStream() + .filter(entry -> { + // 快速通配符检查 + if (isWildcardFilter && "*".equals(filterText)) { + return true; } - } - } + + try { + String host = StringProcessor.getHostByUrl(entry.getUrl()); + if (host.isEmpty()) { + return false; + } + + // 优化后的匹配逻辑 + return StringProcessor.matchesHostPattern(host, filterText) || + (isWildcardFilter && host.toLowerCase().contains(normalizedFilter.replace("*", ""))); + } catch (Exception e) { + return false; + } + }) + .forEachOrdered(newFilteredLog::add); - // 处理最后一批 - if (!batch.isEmpty()) { - final List finalBatch = new ArrayList<>(batch); - SwingUtilities.invokeLater(() -> { - filteredLog.addAll(finalBatch); - fireTableDataChanged(); - }); - } + // 一次性更新UI,避免频繁刷新 + SwingUtilities.invokeLater(() -> { + synchronized (filteredLog) { + filteredLog.clear(); + filteredLog.addAll(newFilteredLog); + } + fireTableDataChanged(); + }); } public void applyMessageFilter(String tableName, String filterText) { - filteredLog.clear(); - for (MessageEntry entry : log) { + List newFilteredLog = new ArrayList<>(); + + // 创建log的安全副本以避免ConcurrentModificationException + List logSnapshot; + synchronized (log) { + logSnapshot = new ArrayList<>(log); + } + + for (MessageEntry entry : logSnapshot) { // 标志变量,表示是否满足过滤条件 AtomicBoolean isMatched = new AtomicBoolean(false); - HttpRequestResponse requestResponse = entry.getRequestResponse(); - HttpRequest httpRequest = requestResponse.request(); - HttpResponse httpResponse = requestResponse.response(); + try { + HttpRequestResponse requestResponse = entry.getRequestResponse(); + HttpRequest httpRequest = requestResponse.request(); + HttpResponse httpResponse = requestResponse.response(); - String requestString = new String(httpRequest.toByteArray().getBytes(), StandardCharsets.UTF_8); - String requestBody = new String(httpRequest.body().getBytes(), StandardCharsets.UTF_8); - String requestHeaders = httpRequest.headers().stream() - .map(HttpHeader::toString) - .collect(Collectors.joining("\r\n")); + String requestString = new String(httpRequest.toByteArray().getBytes(), StandardCharsets.UTF_8); + String requestBody = new String(httpRequest.body().getBytes(), StandardCharsets.UTF_8); + String requestHeaders = httpRequest.headers().stream() + .map(HttpHeader::toString) + .collect(Collectors.joining("\r\n")); - String responseString = new String(httpResponse.toByteArray().getBytes(), StandardCharsets.UTF_8); - String responseBody = new String(httpResponse.body().getBytes(), StandardCharsets.UTF_8); - String responseHeaders = httpResponse.headers().stream() - .map(HttpHeader::toString) - .collect(Collectors.joining("\r\n")); + String responseString = new String(httpResponse.toByteArray().getBytes(), StandardCharsets.UTF_8); + String responseBody = new String(httpResponse.body().getBytes(), StandardCharsets.UTF_8); + String responseHeaders = httpResponse.headers().stream() + .map(HttpHeader::toString) + .collect(Collectors.joining("\r\n")); - Config.globalRules.keySet().forEach(i -> { - for (Object[] objects : Config.globalRules.get(i)) { - String name = objects[1].toString(); - String format = objects[4].toString(); - String scope = objects[6].toString(); + Config.globalRules.keySet().forEach(i -> { + for (Object[] objects : Config.globalRules.get(i)) { + String name = objects[1].toString(); + String format = objects[4].toString(); + String scope = objects[6].toString(); - // 从注释中查看是否包含当前规则名,包含的再进行查询,有效减少无意义的检索时间 - if (entry.getComment().contains(name)) { - if (name.equals(tableName)) { - // 标志变量,表示当前规则是否匹配 - boolean isMatch = false; + // 从注释中查看是否包含当前规则名,包含的再进行查询,有效减少无意义的检索时间 + if (entry.getComment().contains(name)) { + if (name.equals(tableName)) { + // 标志变量,表示当前规则是否匹配 + boolean isMatch = false; - switch (scope) { - case "any": - isMatch = matchingString(format, filterText, requestString) || matchingString(format, filterText, responseString); - break; - case "request": - isMatch = matchingString(format, filterText, requestString); - break; - case "response": - isMatch = matchingString(format, filterText, responseString); - break; - case "any header": - isMatch = matchingString(format, filterText, requestHeaders) || matchingString(format, filterText, responseHeaders); - break; - case "request header": - isMatch = matchingString(format, filterText, requestHeaders); - break; - case "response header": - isMatch = matchingString(format, filterText, responseHeaders); - break; - case "any body": - isMatch = matchingString(format, filterText, requestBody) || matchingString(format, filterText, responseBody); - break; - case "request body": - isMatch = matchingString(format, filterText, requestBody); - break; - case "response body": - isMatch = matchingString(format, filterText, responseBody); - break; - case "request line": - String requestLine = requestString.split("\\r?\\n", 2)[0]; - isMatch = matchingString(format, filterText, requestLine); - break; - case "response line": - String responseLine = responseString.split("\\r?\\n", 2)[0]; - isMatch = matchingString(format, filterText, responseLine); - break; - default: - break; + switch (scope) { + case "any": + isMatch = matchingString(format, filterText, requestString) || matchingString(format, filterText, responseString); + break; + case "request": + isMatch = matchingString(format, filterText, requestString); + break; + case "response": + isMatch = matchingString(format, filterText, responseString); + break; + case "any header": + isMatch = matchingString(format, filterText, requestHeaders) || matchingString(format, filterText, responseHeaders); + break; + case "request header": + isMatch = matchingString(format, filterText, requestHeaders); + break; + case "response header": + isMatch = matchingString(format, filterText, responseHeaders); + break; + case "any body": + isMatch = matchingString(format, filterText, requestBody) || matchingString(format, filterText, responseBody); + break; + case "request body": + isMatch = matchingString(format, filterText, requestBody); + break; + case "response body": + isMatch = matchingString(format, filterText, responseBody); + break; + case "request line": + String requestLine = requestString.split("\\r?\\n", 2)[0]; + isMatch = matchingString(format, filterText, requestLine); + break; + case "response line": + String responseLine = responseString.split("\\r?\\n", 2)[0]; + isMatch = matchingString(format, filterText, responseLine); + break; + default: + break; + } + + isMatched.set(isMatch); + break; } - - isMatched.set(isMatch); - break; } } - } - }); + }); + + // 由于每个用户规则不同,如果进行项目文件共享则需要考虑全部匹配一下 + if (!isMatched.get()) { + isMatched.set(matchingString("{0}", filterText, requestString) || matchingString("{0}", filterText, responseString)); + } + + if (isMatched.get()) { + newFilteredLog.add(entry); + } + } catch (Exception ignored) { - if (isMatched.get()) { - filteredLog.add(entry); } } - fireTableDataChanged(); - messageTable.lastSelectedIndex = -1; + // 在EDT线程中更新UI + SwingUtilities.invokeLater(() -> { + synchronized (filteredLog) { + filteredLog.clear(); + filteredLog.addAll(newFilteredLog); + } + fireTableDataChanged(); + messageTable.lastSelectedIndex = -1; + }); } private boolean matchingString(String format, String filterText, String target) { @@ -398,7 +439,9 @@ public class MessageTableModel extends AbstractTableModel { @Override public int getRowCount() { - return filteredLog.size(); + synchronized (filteredLog) { + return filteredLog.size(); + } } @Override @@ -408,27 +451,31 @@ public class MessageTableModel extends AbstractTableModel { @Override public Object getValueAt(int rowIndex, int columnIndex) { - if (!filteredLog.isEmpty()) { + synchronized (filteredLog) { + if (rowIndex < 0 || rowIndex >= filteredLog.size()) { + return ""; + } + try { MessageEntry messageEntry = filteredLog.get(rowIndex); - - if (messageEntry != null) { - return switch (columnIndex) { - case 0 -> messageEntry.getMethod(); - case 1 -> messageEntry.getUrl(); - case 2 -> messageEntry.getComment(); - case 3 -> messageEntry.getStatus(); - case 4 -> messageEntry.getLength(); - case 5 -> messageEntry.getColor(); - default -> ""; - }; + if (messageEntry == null) { + return ""; } + + return switch (columnIndex) { + case 0 -> messageEntry.getMethod(); + case 1 -> messageEntry.getUrl(); + case 2 -> messageEntry.getComment(); + case 3 -> messageEntry.getStatus(); + case 4 -> messageEntry.getLength(); + case 5 -> messageEntry.getColor(); + default -> ""; + }; } catch (Exception e) { api.logging().logToError("getValueAt: " + e.getMessage()); + return ""; } } - - return ""; } @Override diff --git a/src/main/java/hae/component/rule/Rule.java b/src/main/java/hae/component/rule/Rule.java index d4ed8d2..28de2e2 100644 --- a/src/main/java/hae/component/rule/Rule.java +++ b/src/main/java/hae/component/rule/Rule.java @@ -7,9 +7,13 @@ import hae.utils.rule.RuleProcessor; import javax.swing.*; import javax.swing.table.DefaultTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; import javax.swing.table.TableRowSorter; import java.awt.*; import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.Vector; import static javax.swing.JOptionPane.YES_OPTION; @@ -19,6 +23,7 @@ public class Rule extends JPanel { private final ConfigLoader configLoader; private final RuleProcessor ruleProcessor; private final JTabbedPane tabbedPane; + private JCheckBox headerCheckBox; public Rule(MontoyaApi api, ConfigLoader configLoader, Object[][] data, JTabbedPane tabbedPane) { 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()).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 editButton = new JButton("Edit"); JButton removeButton = new JButton("Remove"); @@ -43,14 +49,13 @@ public class Rule extends JPanel { JTable ruleTable = new JTable(); JScrollPane scrollPane = new JScrollPane(); - ruleTable.setShowVerticalLines(false); - ruleTable.setShowHorizontalLines(false); ruleTable.setVerifyInputWhenFocusTarget(false); ruleTable.setUpdateSelectionOnSort(false); ruleTable.setSurrendersFocusOnKeystroke(true); scrollPane.setViewportView(ruleTable); // 按钮监听事件 + copyButton.addActionListener(e -> ruleCopyActionPerformed(e, ruleTable, tabbedPane)); addButton.addActionListener(e -> ruleAddActionPerformed(e, ruleTable, tabbedPane)); editButton.addActionListener(e -> ruleEditActionPerformed(e, ruleTable, tabbedPane)); removeButton.addActionListener(e -> ruleRemoveActionPerformed(e, ruleTable, tabbedPane)); @@ -76,88 +81,335 @@ public class Rule extends JPanel { if (e.getColumn() == 0 && ruleTable.getSelectedRow() != -1) { int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow()); 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, 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, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(15, 5, 5, 5), 0, 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); + } + + /** + * 设置表头复选框 + */ + 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 createRuleDataFromDisplay(Display ruleDisplay) { + Vector ruleData = new Vector<>(); + ruleData.add(false); + ruleData.add(ruleDisplay.ruleNameTextField.getText()); + ruleData.add(ruleDisplay.firstRegexTextField.getText()); + ruleData.add(ruleDisplay.secondRegexTextField.getText()); + ruleData.add(ruleDisplay.formatTextField.getText()); + ruleData.add(ruleDisplay.colorComboBox.getSelectedItem().toString()); + ruleData.add(ruleDisplay.scopeComboBox.getSelectedItem().toString()); + ruleData.add(ruleDisplay.engineComboBox.getSelectedItem().toString()); + 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 ruleData = createRuleDataFromDisplay(ruleDisplay); + DefaultTableModel model = (DefaultTableModel) ruleTable.getModel(); + model.insertRow(model.getRowCount(), ruleData); + 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}"); - int showState = JOptionPane.showConfirmDialog(this, ruleDisplay, "Add Rule", JOptionPane.YES_NO_OPTION); - if (showState == YES_OPTION) { - Vector ruleData = new Vector<>(); - ruleData.add(false); - ruleData.add(ruleDisplay.ruleNameTextField.getText()); - ruleData.add(ruleDisplay.firstRegexTextField.getText()); - ruleData.add(ruleDisplay.secondRegexTextField.getText()); - ruleData.add(ruleDisplay.formatTextField.getText()); - ruleData.add(ruleDisplay.colorComboBox.getSelectedItem().toString()); - ruleData.add(ruleDisplay.scopeComboBox.getSelectedItem().toString()); - ruleData.add(ruleDisplay.engineComboBox.getSelectedItem().toString()); - ruleData.add(ruleDisplay.sensitiveComboBox.getSelectedItem()); - + if (showRuleDialog(ruleDisplay, "Add Rule")) { + Vector ruleData = createRuleDataFromDisplay(ruleDisplay); DefaultTableModel model = (DefaultTableModel) ruleTable.getModel(); model.insertRow(model.getRowCount(), ruleData); - ruleProcessor.addRule(ruleData, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex())); + ruleProcessor.addRule(ruleData, getCurrentTabTitle()); + + // 添加规则后更新表头复选框状态 + updateHeaderCheckBoxState(model); + ruleTable.getTableHeader().repaint(); } } private void ruleEditActionPerformed(ActionEvent e, JTable ruleTable, JTabbedPane tabbedPane) { - if (ruleTable.getSelectedRowCount() >= 1) { + if (!hasSelectedRow(ruleTable)) { + return; + } + + Display ruleDisplay = new Display(); + int selectedRow = ruleTable.getSelectedRow(); + + populateDisplayFromTable(ruleDisplay, ruleTable, selectedRow); + + if (showRuleDialog(ruleDisplay, "Edit Rule")) { DefaultTableModel model = (DefaultTableModel) ruleTable.getModel(); - Display ruleDisplay = new Display(); + int modelIndex = ruleTable.convertRowIndexToModel(selectedRow); - 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())); + // 更新表格数据 + Vector 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) { - if (ruleTable.getSelectedRowCount() >= 1) { - if (JOptionPane.showConfirmDialog(this, "Are you sure you want to remove this rule?", "Info", JOptionPane.YES_NO_OPTION) == 0) { - DefaultTableModel model = (DefaultTableModel) ruleTable.getModel(); - int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow()); + if (!hasSelectedRow(ruleTable)) { + return; + } - model.removeRow(select); - ruleProcessor.removeRule(select, tabbedPane.getTitleAt(tabbedPane.getSelectedIndex())); - } + if (JOptionPane.showConfirmDialog(this, "Are you sure you want to remove this rule?", "Info", JOptionPane.YES_NO_OPTION) == 0) { + DefaultTableModel model = (DefaultTableModel) ruleTable.getModel(); + int select = ruleTable.convertRowIndexToModel(ruleTable.getSelectedRow()); + + model.removeRow(select); + ruleProcessor.removeRule(select, getCurrentTabTitle()); + + // 删除规则后更新表头复选框状态 + updateHeaderCheckBoxState(model); + ruleTable.getTableHeader().repaint(); } } } \ No newline at end of file diff --git a/src/main/java/hae/instances/http/utils/RegularMatcher.java b/src/main/java/hae/instances/http/utils/RegularMatcher.java index a0f0300..f6e07e0 100644 --- a/src/main/java/hae/instances/http/utils/RegularMatcher.java +++ b/src/main/java/hae/instances/http/utils/RegularMatcher.java @@ -23,6 +23,7 @@ import java.util.regex.Pattern; public class RegularMatcher { private static final Map nfaPatternCache = new ConcurrentHashMap<>(); private static final Map dfaAutomatonCache = new ConcurrentHashMap<>(); + private static final Pattern formatIndexPattern = Pattern.compile("\\{(\\d+)}"); private final MontoyaApi api; private final ConfigLoader configLoader; @@ -217,8 +218,8 @@ public class RegularMatcher { while (matcher.find()) { String matchContent = matcher.group(1); if (!matchContent.isEmpty()) { - matcher = createPatternMatcher(s_regex, matchContent, sensitive); - matches.addAll(formatMatchResults(matcher, format)); + Matcher secondMatcher = createPatternMatcher(s_regex, matchContent, sensitive); + matches.addAll(formatMatchResults(secondMatcher, format)); } } } @@ -242,9 +243,20 @@ public class RegularMatcher { } private List formatMatchResults(Matcher matcher, String format) { - List indexList = parseIndexesFromString(format); List 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 indexList = parseIndexesFromString(format); while (matcher.find()) { if (!matcher.group(1).isEmpty()) { Object[] params = indexList.stream().map(i -> { @@ -295,8 +307,7 @@ public class RegularMatcher { private LinkedList parseIndexesFromString(String input) { LinkedList indexes = new LinkedList<>(); - Pattern pattern = Pattern.compile("\\{(\\d+)}"); - Matcher matcher = pattern.matcher(input); + Matcher matcher = formatIndexPattern.matcher(input); while (matcher.find()) { String index = matcher.group(1); @@ -318,8 +329,7 @@ public class RegularMatcher { } private String normalizeFormatIndexes(String format) { - Pattern pattern = Pattern.compile("\\{(\\d+)}"); - Matcher matcher = pattern.matcher(format); + Matcher matcher = formatIndexPattern.matcher(format); int count = 0; while (matcher.find()) { String newStr = String.format("{%s}", count); diff --git a/src/main/java/hae/utils/string/StringProcessor.java b/src/main/java/hae/utils/string/StringProcessor.java index 6588f41..b9b0a8b 100644 --- a/src/main/java/hae/utils/string/StringProcessor.java +++ b/src/main/java/hae/utils/string/StringProcessor.java @@ -1,14 +1,6 @@ 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.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -64,19 +56,6 @@ public class StringProcessor { 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() { UUID uuid = UUID.randomUUID(); return uuid.toString(); diff --git a/src/main/resources/rules/Rules.yml b/src/main/resources/rules/Rules.yml index cdf4881..df6c249 100644 --- a/src/main/resources/rules/Rules.yml +++ b/src/main/resources/rules/Rules.yml @@ -57,7 +57,7 @@ rules: sensitive: false - name: Vite DevMode loaded: true - f_regex: (/@vite/client) + f_regex: (/\@vite/client) s_regex: '' format: '{0}' color: red @@ -133,8 +133,8 @@ rules: 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: '' + f_regex: (\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,5}\b) + s_regex: ^((?!.*\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico?)$).*@.*\..*)$ format: '{0}' color: yellow scope: response @@ -198,9 +198,9 @@ rules: sensitive: true - name: Password Field loaded: true - f_regex: (((|\\)(|'|")(|[\.\w]{1,10})([p](ass|wd|asswd|assword))(|[\.\w]{1,10})(|\\)(|'|")( - |)(:|[=]{1,3}|![=]{1,2}|[\)]{0,1}\.val\()( |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\)))|((|\\)('|")([^'"]+?)(|\\)('|")(|\\)(|'|")( - |)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,10})([p](ass|wd|asswd|assword))(|[\.\w]{1,10})(|\\)(|'|"))) + f_regex: (((|\\)(|'|")(|[\.\w]{1,32})([p](ass|wd|asswd|assword))(|[\.\w]{1,32})(|\\)(|'|")( + |)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(|'|")([^'"]+?)(|\\)(|'|")(|,|\)))|((|\\)(|'|")([^'"]+?)(|\\)(|'|")(|\\)(|'|")( + |)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,32})([p](ass|wd|asswd|assword))(|[\.\w]{1,32})(|\\)(|'|"))) s_regex: '' format: '{0}' color: yellow @@ -209,9 +209,9 @@ rules: 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})(|\\)(|'|")( - |)(:|=|!=|[\)]{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})(|\\)(|'|"))) + f_regex: (((|\\)(|'|")(|[\.\w]{1,32})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\.\w]{1,32})(|\\)(|'|")( + |)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(|'|")([^'"]+?)(|\\)(|'|")(|,|\)))|((|\\)(|'|")([^'"]+?)(|\\)(|'|")(|\\)(|'|")( + |)(:|[=]{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: '' format: '{0}' color: green @@ -247,9 +247,9 @@ rules: sensitive: false - name: Sensitive Field loaded: true - f_regex: (((\[)?('|")?([\.\w]{0,10})(key|secret|token|config|auth|access|admin|ticket)([\.\w]{0,10})('|")?(\])?( - |)(:|=|!=|[\)]{0,1}\.val\()( |)('|")([^'"]+?)('|")(|,|\)))|((|\\)('|")([^'"]+?)(|\\)('|")(|\\)(|'|")( - |)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,10})(key|secret|token|config|auth|access|admin|ticket)(|[\.\w]{1,10})(|\\)(|'|"))) + f_regex: (((|\\)(|'|")(|[\.\w]{1,32})(key|secret|token|config|auth|access|admin|ticket)(|[\.\w]{1,32})(|\\)(|'|")( + |)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(|'|")([^'"]+?)(|\\)(|'|")(|,|\)))|((|\\)(|'|")([^'"]+?)(|\\)(|'|")(|\\)(|'|")( + |)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,32})(key|secret|token|config|auth|access|admin|ticket)(|[\.\w]{1,32})(|\\)(|'|"))) s_regex: '' format: '{0}' color: yellow @@ -258,20 +258,29 @@ rules: sensitive: false - name: Mobile Number Field loaded: true - f_regex: '(((|\\)(|''|")(|[\w]{1,10})(mobile|phone|sjh|shoujihao|concat)(|[\.\w]{1,10})(|\\)(|''|")( - |)(:|=|!=|[\)]{0,1}\.val\()( |)(|\\)(''|")([^''"]+?)(|\\)(''|")(|,|\)))|((|\\)(''|")([^''"]+?)(|\\)(''|")(|\\)(|''|")( - |)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\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\()( |)(|\\)(|'|")([^'"]+?)(|\\)(|'|")(|,|\)))|((|\\)(|'|")([^'"]+?)(|\\)(|'|")(|\\)(|'|")( + |)(:|[=]{1,3}|![=]{1,2})( |)(|[\.\w]{1,32})(mobile|phone|sjh|shoujihao|concat)(|[\.\w]{1,32})(|\\)(|'|"))) s_regex: '' format: '{0}' color: green scope: response body engine: nfa 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 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,}|)))(?:"|') + 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 @@ -334,10 +343,19 @@ rules: sensitive: false - name: 302 Location loaded: true - f_regex: 'Location: (.*?)\n' + f_regex: 'Location: (.*?)\r\n' s_regex: '' format: '{0}' color: gray scope: response header engine: nfa sensitive: false + - name: OSKeys + loaded: false + f_regex: (.*?) + s_regex: '' + format: '{0}' + color: gray + scope: response body + engine: nfa + sensitive: true