Skip to content

Commit 2135cf5

Browse files
committed
feat(ui): add verifiable indicators and editable issue properties
1 parent f5b5a03 commit 2135cf5

File tree

3 files changed

+137
-14
lines changed

3 files changed

+137
-14
lines changed

src/main/java/com/arqsz/burpgitleaks/BurpExtender.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ private RegisteredComponents registerComponents(MontoyaApi api, GitleaksConfigur
122122
templateManager, issuesTab);
123123
api.userInterface().registerContextMenuItemsProvider(menuProvider);
124124

125-
SettingsTab settingsTab = new SettingsTab(api, scanCheck, settings, config.rules(), (visible) -> {
125+
SettingsTab settingsTab = new SettingsTab(api, scanCheck, settings, config.rules(), templateManager, (visible) -> {
126126
if (visible)
127127
registerIssuesTab();
128128
else

src/main/java/com/arqsz/burpgitleaks/ui/IssuesTab.java

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import java.util.Set;
1818
import java.util.concurrent.ConcurrentHashMap;
1919

20+
import javax.swing.DefaultCellEditor;
2021
import javax.swing.JButton;
22+
import javax.swing.JComboBox;
2123
import javax.swing.JEditorPane;
2224
import javax.swing.JLabel;
2325
import javax.swing.JMenuItem;
@@ -45,6 +47,7 @@
4547
import burp.api.montoya.core.Marker;
4648
import burp.api.montoya.http.message.HttpRequestResponse;
4749
import burp.api.montoya.scanner.audit.issues.AuditIssue;
50+
import burp.api.montoya.scanner.audit.issues.AuditIssueConfidence;
4851
import burp.api.montoya.scanner.audit.issues.AuditIssueSeverity;
4952
import burp.api.montoya.ui.editor.HttpRequestEditor;
5053
import burp.api.montoya.ui.editor.HttpResponseEditor;
@@ -98,6 +101,13 @@ public IssuesTab(MontoyaApi api, String tabTitle, TemplateManager templateManage
98101
add(topPanel, BorderLayout.NORTH);
99102

100103
table = new JTable(model);
104+
105+
JComboBox<AuditIssueSeverity> severityCombo = new JComboBox<>(AuditIssueSeverity.values());
106+
table.getColumnModel().getColumn(4).setCellEditor(new DefaultCellEditor(severityCombo));
107+
108+
JComboBox<AuditIssueConfidence> confidenceCombo = new JComboBox<>(AuditIssueConfidence.values());
109+
table.getColumnModel().getColumn(5).setCellEditor(new DefaultCellEditor(confidenceCombo));
110+
101111
table.setAutoCreateRowSorter(true);
102112
table.setFillsViewportHeight(true);
103113
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
@@ -157,13 +167,17 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
157167
JMenuItem copyUrl = new JMenuItem("Copy URL");
158168
copyUrl.addActionListener(e -> copySelectedUrls());
159169

160-
JMenuItem deleteItem = new JMenuItem("Delete Selected Issue(s)");
170+
JMenuItem deleteItem = new JMenuItem("Delete selected issue(s)");
161171
deleteItem.addActionListener(e -> deleteSelectedIssues());
162172

173+
JMenuItem restoreItem = new JMenuItem("Restore original value");
174+
restoreItem.addActionListener(e -> restoreSelectedDefaults());
175+
163176
popupMenu.add(sendToRepeater);
164177
popupMenu.add(copyUrl);
165178
popupMenu.addSeparator();
166179
popupMenu.add(deleteItem);
180+
popupMenu.add(restoreItem);
167181

168182
final int staticItemCount = popupMenu.getComponentCount();
169183

@@ -240,6 +254,17 @@ public void popupMenuCanceled(PopupMenuEvent e) {
240254
});
241255
}
242256

257+
private void restoreSelectedDefaults() {
258+
int[] selectedRows = table.getSelectedRows();
259+
if (selectedRows.length == 0)
260+
return;
261+
262+
for (int viewRow : selectedRows) {
263+
int modelRow = table.convertRowIndexToModel(viewRow);
264+
model.restoreDefaults(modelRow);
265+
}
266+
}
267+
243268
public boolean addIssue(AuditIssue issue) {
244269
String sig = generateSignature(issue);
245270
if (threadSafeSignatures.contains(sig)) {
@@ -505,7 +530,30 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
505530
}
506531
}
507532

508-
private record IssueEntry(int id, LocalDateTime timestamp, AuditIssue issue) {
533+
private static class IssueEntry {
534+
final int id;
535+
final LocalDateTime timestamp;
536+
final AuditIssue originalIssue;
537+
538+
AuditIssueSeverity userSeverity;
539+
AuditIssueConfidence userConfidence;
540+
541+
public IssueEntry(int id, LocalDateTime timestamp, AuditIssue issue) {
542+
this.id = id;
543+
this.timestamp = timestamp;
544+
this.originalIssue = issue;
545+
this.userSeverity = issue.severity();
546+
this.userConfidence = issue.confidence();
547+
}
548+
549+
public void reset() {
550+
this.userSeverity = originalIssue.severity();
551+
this.userConfidence = originalIssue.confidence();
552+
}
553+
554+
public AuditIssue issue() {
555+
return originalIssue;
556+
}
509557
}
510558

511559
private static class IssuesTableModel extends AbstractTableModel {
@@ -526,6 +574,14 @@ public void removeRow(int row) {
526574
}
527575
}
528576

577+
public void restoreDefaults(int row) {
578+
if (row >= 0 && row < entries.size()) {
579+
entries.get(row).reset();
580+
fireTableCellUpdated(row, 4);
581+
fireTableCellUpdated(row, 5);
582+
}
583+
}
584+
529585
public void clear() {
530586
entries.clear();
531587
nextId = 1;
@@ -536,6 +592,11 @@ public AuditIssue getIssue(int row) {
536592
return entries.get(row).issue();
537593
}
538594

595+
@Override
596+
public boolean isCellEditable(int rowIndex, int columnIndex) {
597+
return columnIndex == 4 || columnIndex == 5;
598+
}
599+
539600
@Override
540601
public int getRowCount() {
541602
return entries.size();
@@ -561,23 +622,35 @@ public Class<?> getColumnClass(int columnIndex) {
561622
return String.class;
562623
}
563624

625+
@Override
626+
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
627+
IssueEntry e = entries.get(rowIndex);
628+
if (columnIndex == 4 && aValue instanceof AuditIssueSeverity) {
629+
e.userSeverity = (AuditIssueSeverity) aValue;
630+
fireTableCellUpdated(rowIndex, columnIndex);
631+
} else if (columnIndex == 5 && aValue instanceof AuditIssueConfidence) {
632+
e.userConfidence = (AuditIssueConfidence) aValue;
633+
fireTableCellUpdated(rowIndex, columnIndex);
634+
}
635+
}
636+
564637
@Override
565638
public Object getValueAt(int row, int col) {
566639
IssueEntry e = entries.get(row);
567-
AuditIssue i = e.issue();
640+
AuditIssue i = e.originalIssue;
568641
switch (col) {
569642
case 0:
570-
return e.id();
643+
return e.id;
571644
case 1:
572-
return e.timestamp();
645+
return e.timestamp;
573646
case 2:
574647
return i.name();
575648
case 3:
576649
return i.baseUrl();
577650
case 4:
578-
return i.severity().name();
651+
return e.userSeverity.name();
579652
case 5:
580-
return i.confidence().name();
653+
return e.userConfidence.name();
581654
default:
582655
return "";
583656
}

src/main/java/com/arqsz/burpgitleaks/ui/SettingsTab.java

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.awt.BorderLayout;
44
import java.awt.Color;
5+
import java.awt.Component;
56
import java.awt.FlowLayout;
67
import java.awt.Font;
78
import java.awt.Graphics;
@@ -39,13 +40,15 @@
3940
import javax.swing.SwingUtilities;
4041
import javax.swing.UIManager;
4142
import javax.swing.table.AbstractTableModel;
43+
import javax.swing.table.TableCellRenderer;
4244
import javax.swing.table.TableRowSorter;
4345

4446
import com.arqsz.burpgitleaks.config.GitleaksRule;
4547
import com.arqsz.burpgitleaks.config.PluginSettings;
4648
import com.arqsz.burpgitleaks.config.RuleLoader;
4749
import com.arqsz.burpgitleaks.config.RuleLoader.GitleaksConfiguration;
4850
import com.arqsz.burpgitleaks.scan.GitleaksScanCheck;
51+
import com.arqsz.burpgitleaks.verification.TemplateManager;
4952

5053
import burp.api.montoya.MontoyaApi;
5154

@@ -55,6 +58,7 @@ public class SettingsTab extends JPanel {
5558
private final GitleaksScanCheck scanCheck;
5659
private final PluginSettings settings;
5760
private final ExecutorService executor;
61+
private final TemplateManager templateManager;
5862
private final RulesTableModel rulesModel;
5963
private List<GitleaksRule> currentRules;
6064

@@ -73,13 +77,15 @@ protected void paintComponent(Graphics g) {
7377
private final Consumer<Boolean> onIssuesTabVisibilityChange;
7478

7579
public SettingsTab(MontoyaApi api, GitleaksScanCheck scanCheck, PluginSettings settings,
76-
List<GitleaksRule> initialRules, Consumer<Boolean> onIssuesTabVisibilityChange) {
80+
List<GitleaksRule> initialRules, TemplateManager templateManager,
81+
Consumer<Boolean> onIssuesTabVisibilityChange) {
7782
this.api = api;
7883
this.scanCheck = scanCheck;
7984
this.settings = settings;
85+
this.templateManager = templateManager;
8086
this.currentRules = initialRules;
8187
this.onIssuesTabVisibilityChange = onIssuesTabVisibilityChange;
82-
this.rulesModel = new RulesTableModel(initialRules, settings.getDisabledRules());
88+
this.rulesModel = new RulesTableModel(initialRules, settings.getDisabledRules(), templateManager);
8389
this.executor = Executors.newSingleThreadExecutor();
8490

8591
setLayout(new BorderLayout());
@@ -280,6 +286,8 @@ public String getToolTipText(java.awt.event.MouseEvent e) {
280286
table.setFillsViewportHeight(true);
281287
table.setRowHeight(24);
282288

289+
table.setDefaultRenderer(Boolean.class, new BooleanRenderer());
290+
283291
JPopupMenu popup = new JPopupMenu();
284292
addCopyMenuItem(popup, table, "Copy Source", 1);
285293
addCopyMenuItem(popup, table, "Copy Rule ID", 2);
@@ -316,7 +324,8 @@ private void filter() {
316324
table.getColumnModel().getColumn(1).setPreferredWidth(100);
317325
table.getColumnModel().getColumn(1).setMaxWidth(350);
318326
table.getColumnModel().getColumn(2).setPreferredWidth(200);
319-
table.getColumnModel().getColumn(3).setPreferredWidth(400);
327+
table.getColumnModel().getColumn(3).setMaxWidth(80);
328+
table.getColumnModel().getColumn(4).setPreferredWidth(400);
320329

321330
p.add(new JScrollPane(table), BorderLayout.CENTER);
322331

@@ -560,11 +569,13 @@ private void setConfigBadge(String text, Color bg) {
560569
}
561570

562571
private static class RulesTableModel extends AbstractTableModel {
563-
private final String[] cols = { "Enabled", "Source", "Rule ID", "Description" };
572+
private final String[] cols = { "Enabled", "Source", "Rule ID", "Verifiable", "Description" };
564573
private List<GitleaksRule> rules;
565574
private List<Boolean> enabledState;
575+
private final TemplateManager templateManager;
566576

567-
public RulesTableModel(List<GitleaksRule> rules, List<String> disabledIds) {
577+
public RulesTableModel(List<GitleaksRule> rules, List<String> disabledIds, TemplateManager templateManager) {
578+
this.templateManager = templateManager;
568579
setRules(rules, disabledIds);
569580
}
570581

@@ -603,7 +614,7 @@ public String getColumnName(int col) {
603614

604615
@Override
605616
public Class<?> getColumnClass(int col) {
606-
return col == 0 ? Boolean.class : String.class;
617+
return (col == 0 || col == 3) ? Boolean.class : String.class;
607618
}
608619

609620
@Override
@@ -622,6 +633,8 @@ public Object getValueAt(int row, int col) {
622633
case 2:
623634
return rule.getId();
624635
case 3:
636+
return templateManager.hasTemplate(rule.getId());
637+
case 4:
625638
return rule.getDescription();
626639
default:
627640
return null;
@@ -635,4 +648,41 @@ public void setValueAt(Object val, int row, int col) {
635648
fireTableCellUpdated(row, col);
636649
}
637650
}
651+
652+
private static class BooleanRenderer extends JPanel implements TableCellRenderer {
653+
private final JCheckBox checkBox = new JCheckBox();
654+
655+
public BooleanRenderer() {
656+
super(new GridBagLayout());
657+
658+
setOpaque(true);
659+
checkBox.setOpaque(false);
660+
checkBox.setHorizontalAlignment(JLabel.CENTER);
661+
662+
add(checkBox);
663+
}
664+
665+
@Override
666+
public Component getTableCellRendererComponent(JTable table, Object value,
667+
boolean isSelected, boolean hasFocus, int row, int column) {
668+
669+
if (isSelected) {
670+
setBackground(table.getSelectionBackground());
671+
checkBox.setForeground(table.getSelectionForeground());
672+
} else {
673+
Color alternateColor = UIManager.getColor("Table.alternateRowColor");
674+
if (alternateColor != null && row % 2 != 0) {
675+
setBackground(alternateColor);
676+
} else {
677+
setBackground(table.getBackground());
678+
}
679+
680+
checkBox.setForeground(table.getForeground());
681+
}
682+
683+
checkBox.setSelected(value != null && (Boolean) value);
684+
685+
return this;
686+
}
687+
}
638688
}

0 commit comments

Comments
 (0)