Skip to content

Commit

Permalink
feat(logger): add results count
Browse files Browse the repository at this point in the history
Add a label to show total count of results and also the count of the filtered items.
  • Loading branch information
beryxz committed Apr 5, 2024
1 parent 48df30f commit 6164d5d
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.cys4.sensitivediscoverer;

public interface LogEntriesListener {
void onSizeChange(int entriesCount);
}
59 changes: 59 additions & 0 deletions src/main/java/com/cys4/sensitivediscoverer/LogEntriesManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.cys4.sensitivediscoverer;

import com.cys4.sensitivediscoverer.model.LogEntity;

import java.util.ArrayList;
import java.util.List;

public class LogEntriesManager {
private final List<LogEntity> logEntries;
private final List<LogEntriesListener> listeners;

public LogEntriesManager() {
this.logEntries = new ArrayList<>();
this.listeners = new ArrayList<>();
}

public void add(LogEntity entry) {
logEntries.add(entry);
listeners.forEach(listener -> listener.onSizeChange(logEntries.size()));
}

public void remove(LogEntity entry) {
logEntries.remove(entry);
listeners.forEach(listener -> listener.onSizeChange(logEntries.size()));
}

public void clear() {
logEntries.clear();
listeners.forEach(listener -> listener.onSizeChange(logEntries.size()));
}

public int size() {
return logEntries.size();
}

public LogEntity get(int index) {
return logEntries.get(index);
}

/**
* Returns an unmodifiable List containing all the log entries.
* @return a <code>List</code> containing the log entries.
*/
public List<LogEntity> getAll() {
return List.copyOf(logEntries);
}

public boolean contains(LogEntity entry) {
return logEntries.contains(entry);
}

public void subscribeChangeListener(LogEntriesListener listener) {
listeners.add(listener);
}

public void unsubscribeChangeListener(LogEntriesListener listener) {
listeners.remove(listener);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.cys4.sensitivediscoverer.model;

import com.cys4.sensitivediscoverer.LogEntriesManager;

import javax.swing.table.AbstractTableModel;
import java.util.List;

Expand All @@ -8,9 +10,9 @@
public class LogsTableModel extends AbstractTableModel {

// get the reference of the array of entries
private final List<LogEntity> logEntries;
private final LogEntriesManager logEntries;

public LogsTableModel(List<LogEntity> logEntries) {
public LogsTableModel(LogEntriesManager logEntries) {
this.logEntries = logEntries;
}

Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/cys4/sensitivediscoverer/ui/LogsTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.HttpResponseEditor;
import com.cys4.sensitivediscoverer.LogEntriesManager;
import com.cys4.sensitivediscoverer.model.LogEntity;
import com.cys4.sensitivediscoverer.model.LogsTableModel;

import javax.swing.*;
import java.util.List;

/**
* JTable for Viewing Logs
*/
public class LogsTable extends JTable {

private final List<LogEntity> logEntries;
private final LogEntriesManager logEntries;
private final HttpRequestEditor requestViewer;
private final HttpResponseEditor responseViewer;

public LogsTable(LogsTableModel logsTableModel, List<LogEntity> logEntries, HttpRequestEditor requestViewer, HttpResponseEditor responseViewer) {
public LogsTable(LogsTableModel logsTableModel, LogEntriesManager logEntries, HttpRequestEditor requestViewer, HttpResponseEditor responseViewer) {
super(logsTableModel);

this.setAutoCreateRowSorter(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.HttpResponseEditor;
import com.cys4.sensitivediscoverer.LogEntriesManager;
import com.cys4.sensitivediscoverer.model.LogEntity;
import com.cys4.sensitivediscoverer.model.LogsTableModel;
import com.cys4.sensitivediscoverer.model.RegexEntity;
Expand All @@ -17,14 +18,13 @@
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.util.List;

import static com.cys4.sensitivediscoverer.Messages.getLocaleString;

public class LogsTableContextMenu extends JPopupMenu {

public LogsTableContextMenu(LogEntity logEntry,
List<LogEntity> logEntries,
LogEntriesManager logEntries,
HttpRequestEditor originalRequestViewer,
HttpResponseEditor originalResponseViewer,
LogsTableModel logsTableModel,
Expand Down
57 changes: 39 additions & 18 deletions src/main/java/com/cys4/sensitivediscoverer/ui/tab/LoggerTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import burp.api.montoya.http.message.responses.HttpResponse;
import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.HttpResponseEditor;
import com.cys4.sensitivediscoverer.LogEntriesManager;
import com.cys4.sensitivediscoverer.MainUI;
import com.cys4.sensitivediscoverer.RegexScanner;
import com.cys4.sensitivediscoverer.model.LogEntity;
Expand Down Expand Up @@ -44,13 +45,13 @@ public class LoggerTab implements ApplicationTab {
private final MainUI mainUI;
private final JPanel panel;
/**
* List containing the findings history (log entries).
* Manager for the list containing the findings history (log entries).
* <br><br>
* When running multiple analysis on the same RegexScanner instance,
* this list remains the same unless manually cleared.
* This is required for not logging the same finding twice.
*/
private final List<LogEntity> logEntries;
private final LogEntriesManager logEntriesManager;
private final Object analyzeLock = new Object();
private final Object loggerLock = new Object();
private final RegexScanner regexScanner;
Expand All @@ -70,7 +71,7 @@ public LoggerTab(MainUI mainUI) {
this.mainUI = mainUI;
this.isAnalysisRunning = false;
this.analyzeProxyHistoryThread = null;
this.logEntries = new ArrayList<>();
this.logEntriesManager = new LogEntriesManager();
this.regexScanner = new RegexScanner(
this.mainUI.getBurpApi(),
this.mainUI.getScannerOptions(),
Expand Down Expand Up @@ -167,16 +168,18 @@ private JPanel createCenterBox(JScrollPane logEntriesPane) {

private JPanel createHeaderBox(JScrollPane logEntriesPane) {
JPanel headerBox;
JPanel analysisBar;
JPanel resultsFilterBar;
GridBagConstraints gbc;

headerBox = new JPanel();
headerBox.setLayout(new GridBagLayout());

JPanel analysisBar = createAnalysisBar(logEntriesPane);
analysisBar = createAnalysisBar(logEntriesPane);
gbc = createGridConstraints(0, 0, 1.0, 0.0, GridBagConstraints.HORIZONTAL);
headerBox.add(analysisBar, gbc);

JPanel resultsFilterBar = createResultsFilterBar();
resultsFilterBar = createResultsFilterBar();
gbc = createGridConstraints(0, 1, 1.0, 0.0, GridBagConstraints.HORIZONTAL);
gbc.insets = new Insets(2, 10, 5, 10);
headerBox.add(resultsFilterBar, gbc);
Expand All @@ -191,30 +194,50 @@ private JPanel createResultsFilterBar() {
resultsFilterBar = new JPanel();
resultsFilterBar.setLayout(new GridBagLayout());

JLabel searchLabel = new JLabel(getLocaleString("logger-searchBar-label"));
JLabel resultsCountLabel = new JLabel(getLocaleString("logger-resultsCount-label"));
gbc = createGridConstraints(0, 0, 0, 0, GridBagConstraints.HORIZONTAL);
gbc.insets = new Insets(0, 0, 0, 5);
resultsFilterBar.add(resultsCountLabel, gbc);
JLabel filteredCountValueLabel = new JLabel("0");
gbc = createGridConstraints(1, 0, 0, 0, GridBagConstraints.HORIZONTAL);
logsTableRowSorter.addRowSorterListener(rowSorterEvent -> filteredCountValueLabel.setText(String.valueOf(logsTable.getRowSorter().getViewRowCount())));
resultsFilterBar.add(filteredCountValueLabel, gbc);
JLabel totalCountValueLabel = new JLabel("/0");
logEntriesManager.subscribeChangeListener(entriesCount -> {
filteredCountValueLabel.setText(String.valueOf(Math.min(entriesCount, logsTable.getRowSorter().getViewRowCount())));
totalCountValueLabel.setText("/" + entriesCount);
});
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);
resultsFilterBar.add(resultsCountSeparator, gbc);

JLabel searchLabel = new JLabel(getLocaleString("logger-searchBar-label"));
gbc = createGridConstraints(4, 0, 0, 0, GridBagConstraints.HORIZONTAL);
gbc.insets = new Insets(0, 0, 0, 5);
resultsFilterBar.add(searchLabel, gbc);

JTextField searchField = new JTextField();
gbc = createGridConstraints(1, 0, 1, 0, GridBagConstraints.HORIZONTAL);
gbc = createGridConstraints(5, 0, 1, 0, GridBagConstraints.HORIZONTAL);
resultsFilterBar.add(searchField, gbc);

JCheckBox regexCheckbox = new JCheckBox(getLocaleString(LogsTableModel.Column.REGEX.getLocaleKey()));
regexCheckbox.setSelected(true);
gbc = createGridConstraints(2, 0, 0, 0, GridBagConstraints.HORIZONTAL);
gbc = createGridConstraints(6, 0, 0, 0, GridBagConstraints.HORIZONTAL);
gbc.insets = new Insets(0, 10, 0, 0);
resultsFilterBar.add(regexCheckbox, gbc);

JCheckBox matchCheckbox = new JCheckBox(getLocaleString(LogsTableModel.Column.MATCH.getLocaleKey()));
matchCheckbox.setSelected(true);
gbc = createGridConstraints(3, 0, 0, 0, GridBagConstraints.HORIZONTAL);
gbc = createGridConstraints(7, 0, 0, 0, GridBagConstraints.HORIZONTAL);
gbc.insets = new Insets(0, 10, 0, 0);
resultsFilterBar.add(matchCheckbox, gbc);

JCheckBox URLCheckbox = new JCheckBox(getLocaleString(LogsTableModel.Column.URL.getLocaleKey()));
URLCheckbox.setSelected(true);
gbc = createGridConstraints(4, 0, 0, 0, GridBagConstraints.HORIZONTAL);
gbc = createGridConstraints(8, 0, 0, 0, GridBagConstraints.HORIZONTAL);
gbc.insets = new Insets(0, 10, 0, 0);
resultsFilterBar.add(URLCheckbox, gbc);

Expand Down Expand Up @@ -371,19 +394,17 @@ private Runnable setupProgressBarCallback(int maxItems) {

private void setupScan() {
analysisButton.setText(getLocaleString("logger-analysis-stop"));
//todo logsTable.setAutoCreateRowSorter(false);
LoggerTab.this.analyzedItems = 0;
progressBar.setValue(LoggerTab.this.analyzedItems);
}

private void startScan() {
Consumer<LogEntity> addLogEntryCallback = LoggerUtils.createAddLogEntryCallback(logEntries, loggerLock, Optional.of(logsTableModel));
Consumer<LogEntity> addLogEntryCallback = LoggerUtils.createAddLogEntryCallback(logEntriesManager, loggerLock, Optional.of(logsTableModel));
regexScanner.analyzeProxyHistory(this::setupProgressBarCallback, addLogEntryCallback);
}

private void finalizeScan() {
analysisButton.setText((String) analysisButton.getClientProperty("initialText"));
//todo logsTable.setAutoCreateRowSorter(true);
analyzeProxyHistoryThread = null;
isAnalysisRunning = false;
LoggerTab.this.postAnalysisOperations();
Expand All @@ -396,10 +417,10 @@ private void finalizeScan() {
}

private JScrollPane createLogEntriesTable() {
logsTableModel = new LogsTableModel(logEntries);
logsTableModel = new LogsTableModel(logEntriesManager);
this.originalRequestViewer = this.mainUI.getBurpApi().userInterface().createHttpRequestEditor();
this.originalResponseViewer = this.mainUI.getBurpApi().userInterface().createHttpResponseEditor();
this.logsTable = new LogsTable(logsTableModel, logEntries, this.originalRequestViewer, this.originalResponseViewer);
this.logsTable = new LogsTable(logsTableModel, logEntriesManager, this.originalRequestViewer, this.originalResponseViewer);
// disable sorting on columns while scanning. This helps to prevent Swing exceptions.
logsTable.getTableHeader().putClientProperty("analysisDependent", "1");
logsTable.setAutoCreateRowSorter(false);
Expand All @@ -418,10 +439,10 @@ private void onMouseEvent(MouseEvent e) {
int row = logsTable.getSelectedRow();
if (row == -1) return;
int realRow = logsTable.convertRowIndexToModel(row);
LogEntity logEntry = logEntries.get(realRow);
LogEntity logEntry = logEntriesManager.get(realRow);

if (e.getComponent() instanceof LogsTable) {
new LogsTableContextMenu(logEntry, logEntries, originalRequestViewer, originalResponseViewer, logsTableModel, logsTable, mainUI.getBurpApi(), isAnalysisRunning)
new LogsTableContextMenu(logEntry, logEntriesManager, originalRequestViewer, originalResponseViewer, logsTableModel, logsTable, mainUI.getBurpApi(), isAnalysisRunning)
.show(e.getComponent(), e.getX(), e.getY());
}
}
Expand Down Expand Up @@ -496,7 +517,7 @@ private JButton createClearLogsButton(JScrollPane scrollPaneLogger) {
btnClearLogs.addActionListener(e -> {
int dialog = JOptionPane.showConfirmDialog(null, getLocaleString("logger-clearLogs-confirm"));
if (dialog == JOptionPane.YES_OPTION) {
logEntries.clear();
logEntriesManager.clear();
logsTableModel.clear();

originalResponseViewer.setResponse(HttpResponse.httpResponse(""));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.cys4.sensitivediscoverer.utils;

import com.cys4.sensitivediscoverer.LogEntriesManager;
import com.cys4.sensitivediscoverer.model.LogEntity;
import com.cys4.sensitivediscoverer.model.LogsTableModel;

import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

public class LoggerUtils {
public static Consumer<LogEntity> createAddLogEntryCallback(List<LogEntity> logEntries, Object logEntriesLock, Optional<LogsTableModel> logsTableModel) {
public static Consumer<LogEntity> createAddLogEntryCallback(LogEntriesManager logEntries, Object logEntriesLock, Optional<LogsTableModel> logsTableModel) {
return (LogEntity logEntry) -> {
synchronized (logEntriesLock) {
int row = logEntries.size();
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/TextUI_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ logger-clearLogs-label=Clear all logs
logger-clearLogs-confirm=Delete ALL the logs in the list?
logger-exportLogs-label=Export list logs...
logger-searchBar-label=Filter results:
logger-resultsCount-label=Results:
logger-ctxMenu-sendToRepeater=Send to Repeater
logger-ctxMenu-sendToIntruder=Send to Intruder
logger-ctxMenu-sendToOrganizer=Send to Organizer
Expand Down
20 changes: 10 additions & 10 deletions src/test/java/com/cys4/sensitivediscoverer/RegexScannerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class RegexScannerTest {
private RegexScanner regexScanner;
private BurpMontoyaApiMock burpApi;
private ScannerOptions scannerOptions;
private List<LogEntity> logEntries;
private LogEntriesManager logEntriesManager;
private Function<Integer, Runnable> progressBarCallbackSetupMock;
private Consumer<LogEntity> logEntityConsumer;

Expand All @@ -45,12 +45,12 @@ void setUp() {

@BeforeEach
void setUpCallbacks() {
this.logEntries = new ArrayList<>();
this.logEntriesManager = new LogEntriesManager();
final Object loggerLock = new Object();

progressBarCallbackSetupMock = (maxItems) -> () -> {
};
logEntityConsumer = LoggerUtils.createAddLogEntryCallback(logEntries, loggerLock, Optional.empty());
logEntityConsumer = LoggerUtils.createAddLogEntryCallback(logEntriesManager, loggerLock, Optional.empty());
}

@Test
Expand All @@ -68,8 +68,8 @@ void testGeneralRegexesWithFindings() {
extensionsRegexes);
regexScanner.analyzeProxyHistory(progressBarCallbackSetupMock, logEntityConsumer);

assertThat(logEntries).as("Check count of entries found").hasSize(10);
assertThat(logEntries).containsExactly(
assertThat(logEntriesManager.size()).as("Check count of entries found").isEqualTo(10);
assertThat(logEntriesManager.getAll()).containsExactly(
new LogEntity(request2.finalRequest(), request2.response(), generalRegexes.get(0), HttpSection.REQ_URL, "test.com"),
new LogEntity(request2.finalRequest(), request2.response(), generalRegexes.get(0), HttpSection.REQ_HEADERS, "test.com"),
new LogEntity(request2.finalRequest(), request2.response(), generalRegexes.get(0), HttpSection.REQ_BODY, "testing 2"),
Expand Down Expand Up @@ -104,8 +104,8 @@ void testGeneralRegexesNoDuplicatesInFindings() {
regexScanner.analyzeProxyHistory(progressBarCallbackSetupMock, logEntityConsumer);
regexScanner.analyzeProxyHistory(progressBarCallbackSetupMock, logEntityConsumer);

assertThat(logEntries).as("Check duplicates aren't inserted more than once").hasSize(10);
assertThat(logEntries).containsExactly(
assertThat(logEntriesManager.size()).as("Check duplicates aren't inserted more than once").isEqualTo(10);
assertThat(logEntriesManager.getAll()).containsExactly(
new LogEntity(request2.finalRequest(), request2.response(), generalRegexes.get(0), HttpSection.REQ_URL, "test.com"),
new LogEntity(request2.finalRequest(), request2.response(), generalRegexes.get(0), HttpSection.REQ_HEADERS, "test.com"),
new LogEntity(request2.finalRequest(), request2.response(), generalRegexes.get(0), HttpSection.REQ_BODY, "testing 2"),
Expand Down Expand Up @@ -136,7 +136,7 @@ void testGeneralRegexesNoFindings() {
extensionsRegexes);
regexScanner.analyzeProxyHistory(progressBarCallbackSetupMock, logEntityConsumer);

assertThat(logEntries).as("Check count of entries found").hasSize(0);
assertThat(logEntriesManager.size()).as("Check count of entries found").isEqualTo(0);
}

private void setProxyHistory(ProxyHttpRequestResponseMock... proxyElements) {
Expand All @@ -158,8 +158,8 @@ void testRefinerRegex() {
extensionsRegexes);
regexScanner.analyzeProxyHistory(progressBarCallbackSetupMock, logEntityConsumer);

assertThat(logEntries).as("Check count of entries found").hasSize(2);
assertThat(logEntries).containsExactly(
assertThat(logEntriesManager.size()).as("Check count of entries found").isEqualTo(2);
assertThat(logEntriesManager.getAll()).containsExactly(
new LogEntity(request.finalRequest(), request.response(), generalRegexes.get(0), HttpSection.REQ_BODY, "randomstring.example.com"),
new LogEntity(request.finalRequest(), request.response(), generalRegexes.get(0), HttpSection.RES_BODY, "bucket-name.test.example.com")
);
Expand Down

0 comments on commit 6164d5d

Please sign in to comment.