From f6e6ed3c5c3c21eef151213ae762ebcceabbe606 Mon Sep 17 00:00:00 2001 From: Lorenzo Coppi Date: Fri, 25 Oct 2024 17:21:16 +0200 Subject: [PATCH] feat: filter for unique results --- .../sensitivediscoverer/ui/tab/LoggerTab.java | 66 +++++++++++++------ .../ui/table/LogsTableModel.java | 11 ++++ src/main/resources/TextUI_en_US.properties | 1 + 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/cys4/sensitivediscoverer/ui/tab/LoggerTab.java b/src/main/java/com/cys4/sensitivediscoverer/ui/tab/LoggerTab.java index a852043..81ab843 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/ui/tab/LoggerTab.java +++ b/src/main/java/com/cys4/sensitivediscoverer/ui/tab/LoggerTab.java @@ -26,6 +26,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.function.Consumer; @@ -201,6 +202,7 @@ private JPanel createResultsFilterBar() { })); gbc = createGridConstraints(2, 0, 0, 0, GridBagConstraints.HORIZONTAL); resultsFilterBar.add(totalCountValueLabel, gbc); + JLabel resultsCountSeparator = new JLabel("│"); gbc = createGridConstraints(3, 0, 0, 0, GridBagConstraints.HORIZONTAL); gbc.insets = new Insets(0, 10, 0, 10); @@ -233,7 +235,24 @@ private JPanel createResultsFilterBar() { gbc.insets = new Insets(0, 10, 0, 0); resultsFilterBar.add(URLCheckbox, gbc); - Runnable doUpdateRowFilter = () -> updateRowFilter(searchField.getText(), regexCheckbox.isSelected(), matchCheckbox.isSelected(), URLCheckbox.isSelected()); + JLabel filterUniqueSeparator = new JLabel("│"); + gbc = createGridConstraints(9, 0, 0, 0, GridBagConstraints.HORIZONTAL); + gbc.insets = new Insets(0, 10, 0, 0); + resultsFilterBar.add(filterUniqueSeparator, gbc); + + JCheckBox UniqueCheckbox = new JCheckBox(getLocaleString("logger-uniqueResults-label")); + UniqueCheckbox.setSelected(false); + gbc = createGridConstraints(10, 0, 0, 0, GridBagConstraints.HORIZONTAL); + gbc.insets = new Insets(0, 10, 0, 0); + resultsFilterBar.add(UniqueCheckbox, gbc); + + Runnable doUpdateRowFilter = () -> updateRowFilter( + searchField.getText(), + regexCheckbox.isSelected(), + matchCheckbox.isSelected(), + URLCheckbox.isSelected(), + UniqueCheckbox.isSelected() + ); regexCheckbox.addActionListener(event -> doUpdateRowFilter.run()); matchCheckbox.addActionListener(event -> doUpdateRowFilter.run()); URLCheckbox.addActionListener(event -> doUpdateRowFilter.run()); @@ -253,6 +272,7 @@ public void changedUpdate(DocumentEvent documentEvent) { doUpdateRowFilter.run(); } }); + UniqueCheckbox.addActionListener(event -> doUpdateRowFilter.run()); return resultsFilterBar; } @@ -301,27 +321,35 @@ private JPanel createAnalysisBar(JScrollPane logEntriesPane) { /** * Filter rows of LogsTable that contains text string * - * @param text text to search - * @param includeRegex if true, also search in Regex column - * @param includeMatch if true, also search in Match column - * @param includeURL if true, also search in URL column + * @param text text to search + * @param includeRegex if true, also search in Regex column + * @param includeMatch if true, also search in Match column + * @param includeURL if true, also search in URL column + * @param uniqueResults if true, remove duplicate results */ - private void updateRowFilter(String text, boolean includeRegex, boolean includeMatch, boolean includeURL) { + private void updateRowFilter(String text, boolean includeRegex, boolean includeMatch, boolean includeURL, boolean uniqueResults) { SwingUtilities.invokeLater(() -> { - if (text.isBlank()) { - logsTableRowSorter.setRowFilter(null); - } else { - logsTableRowSorter.setRowFilter(new RowFilter<>() { - @Override - public boolean include(Entry entry) { - List places = new ArrayList<>(); - if (includeRegex) places.add(LogsTableModel.Column.REGEX); - if (includeMatch) places.add(LogsTableModel.Column.MATCH); - if (includeURL) places.add(LogsTableModel.Column.URL); - return places.stream().anyMatch(column -> entry.getStringValue(column.getIndex()).toLowerCase().contains(text.toLowerCase())); + // hashmap to keep track of unique rows in the table + final HashSet uniqueResultsMap = new HashSet<>(); + + logsTableRowSorter.setRowFilter(new RowFilter<>() { + @Override + public boolean include(Entry entry) { + if (uniqueResults) { + int hashcode = entry.getModel().getRowHashcode(entry.getIdentifier()); + if (uniqueResultsMap.contains(hashcode)) return false; + uniqueResultsMap.add(hashcode); } - }); - } + if (text.isBlank()) { + return true; + } + List places = new ArrayList<>(); + if (includeRegex) places.add(LogsTableModel.Column.REGEX); + if (includeMatch) places.add(LogsTableModel.Column.MATCH); + if (includeURL) places.add(LogsTableModel.Column.URL); + return places.stream().anyMatch(column -> entry.getStringValue(column.getIndex()).toLowerCase().contains(text.toLowerCase())); + } + }); }); } diff --git a/src/main/java/com/cys4/sensitivediscoverer/ui/table/LogsTableModel.java b/src/main/java/com/cys4/sensitivediscoverer/ui/table/LogsTableModel.java index da8bb5e..43b46dc 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/ui/table/LogsTableModel.java +++ b/src/main/java/com/cys4/sensitivediscoverer/ui/table/LogsTableModel.java @@ -5,6 +5,7 @@ import javax.swing.table.AbstractTableModel; import java.util.List; +import java.util.Objects; import static com.cys4.sensitivediscoverer.utils.Messages.getLocaleString; @@ -49,6 +50,16 @@ public Object getValueAt(int rowIndex, int columnIndex) { }; } + public int getRowHashcode(int rowIndex) { + LogEntity logEntity = logEntries.get(rowIndex); + return Objects.hash( + logEntity.getRequestUrl(), + logEntity.getRegexEntity().getRegex(), + logEntity.getMatchedSection().toString(), + logEntity.getMatch() + ); + } + /** * Enum representing the columns of the table model for logs */ diff --git a/src/main/resources/TextUI_en_US.properties b/src/main/resources/TextUI_en_US.properties index 54a6e55..12b16d5 100644 --- a/src/main/resources/TextUI_en_US.properties +++ b/src/main/resources/TextUI_en_US.properties @@ -21,6 +21,7 @@ logger-clearLogs-title=Clear list? logger-exportLogs-label=Export list logs... logger-searchBar-label=Filter results: logger-resultsCount-label=Results: +logger-uniqueResults-label=Unique results logger-ctxMenu-sendToRepeater=Send to Repeater logger-ctxMenu-sendToIntruder=Send to Intruder logger-ctxMenu-sendToOrganizer=Send to Organizer