Skip to content

Commit cb77ba8

Browse files
authored
Merge pull request #148 from RevEngAI/feat-improvements
feat: branding improvements and easier configuration
2 parents 3b4ab2f + 7f1b281 commit cb77ba8

6 files changed

Lines changed: 82 additions & 34 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ You are now ready to analyse a binary.
100100
Import `src/test/resources/fdupes` into Ghidra and then create a new RevEng analysis, by going to `RevEng.AI -> Analysis -> Create New`.
101101
Usually it's enough to use the default options, but you can also select specific platforms or architectures if you want to.
102102

103-
![Upload Dialog](screenshots/upload-dialog.png)
103+
![Upload Dialog](screenshots/upload-dialog-v2.png)
104104

105105
> We are using `fdupes` with symbols to allow the model to learn what these functions look like, and to provide meaningful labels that we can use later to rename similar binaries.
106106

screenshots/upload-dialog-v2.png

68.7 KB
Loading

screenshots/upload-dialog.png

-216 KB
Binary file not shown.

src/main/java/ai/reveng/toolkit/ghidra/binarysimilarity/ui/analysiscreation/RevEngAIAnalysisOptionsDialog.java

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,56 @@
11
package ai.reveng.toolkit.ghidra.binarysimilarity.ui.analysiscreation;
22

3+
import ai.reveng.toolkit.ghidra.binarysimilarity.ui.dialog.RevEngDialogComponentProvider;
34
import ai.reveng.toolkit.ghidra.core.services.api.AnalysisOptionsBuilder;
45
import ai.reveng.toolkit.ghidra.core.services.api.GhidraRevengService;
56
import ai.reveng.toolkit.ghidra.core.services.api.types.AnalysisScope;
6-
import docking.DialogComponentProvider;
7+
import ai.reveng.toolkit.ghidra.plugins.ReaiPluginPackage;
78
import ghidra.program.model.listing.Program;
89

910
import javax.annotation.Nullable;
1011
import javax.swing.*;
1112
import java.awt.*;
1213
import java.util.List;
1314

14-
public class RevEngAIAnalysisOptionsDialog extends DialogComponentProvider {
15-
private final JCheckBox advancedAnalysisCheckBox;
16-
private final JCheckBox dynamicExecutionCheckBox;
15+
public class RevEngAIAnalysisOptionsDialog extends RevEngDialogComponentProvider {
16+
private JCheckBox advancedAnalysisCheckBox;
17+
private JCheckBox dynamicExecutionCheckBox;
1718
private final Program program;
18-
private final JRadioButton privateScope;
19-
private final JRadioButton publicScope;
20-
private final JTextField tagsTextBox;
21-
private final JCheckBox scrapeExternalTagsBox;
22-
private final JCheckBox identifyCapabilitiesCheckBox;
23-
private final JCheckBox identifyCVECheckBox;
24-
private final JCheckBox generateSBOMCheckBox;
25-
private final JComboBox<String> architectureComboBox;
19+
private JRadioButton privateScope;
20+
private JRadioButton publicScope;
21+
private JTextField tagsTextBox;
22+
private JCheckBox scrapeExternalTagsBox;
23+
private JCheckBox identifyCapabilitiesCheckBox;
24+
private JCheckBox identifyCVECheckBox;
25+
private JCheckBox generateSBOMCheckBox;
26+
private JComboBox<String> architectureComboBox;
2627
private boolean okPressed = false;
2728

2829
public static RevEngAIAnalysisOptionsDialog withModelsFromServer(Program program, GhidraRevengService reService) {
2930
return new RevEngAIAnalysisOptionsDialog(program);
3031
}
3132

3233
public RevEngAIAnalysisOptionsDialog(Program program) {
33-
super("Configure Analysis for %s".formatted(program.getName()), true, false, true, true);
34+
super(ReaiPluginPackage.WINDOW_PREFIX + "Configure Analysis for %s".formatted(program.getName()), true);
3435
this.program = program;
3536

37+
buildInterface();
38+
setPreferredSize(320, 380);
39+
}
3640

41+
private void buildInterface() {
3742
var workPanel = new JPanel();
3843
workPanel.setLayout(new BoxLayout(workPanel, BoxLayout.Y_AXIS));
44+
3945
addWorkPanel(workPanel);
4046

47+
// Create title panel
48+
JPanel titlePanel = createTitlePanel("Create new analysis for this binary");
49+
workPanel.add(titlePanel, BorderLayout.NORTH);
4150

4251
// Add Platform Drop Down
4352
var platformComboBox = new JComboBox<>(new String[]{
4453
"Auto", "windows", "linux",
45-
// "macos", "android"
4654
});
4755
platformComboBox.setEditable(false);
4856
// Center the text
@@ -53,8 +61,6 @@ public RevEngAIAnalysisOptionsDialog(Program program) {
5361
workPanel.add(platformLabel);
5462
workPanel.add(platformComboBox);
5563

56-
57-
5864
// Add Drop down for AnalysisScope
5965
// Currently just public and private, but in the future this will include teams
6066
var scopePanel = new JPanel();
@@ -94,7 +100,6 @@ public RevEngAIAnalysisOptionsDialog(Program program) {
94100

95101
// Add Two Check boxes for Dynamic Execution and Advanced Analysis next to each other (horizantally)
96102
var checkBoxPanel = new JPanel();
97-
// checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.X_AXIS));
98103
checkBoxPanel.setLayout(new GridLayout(0, 2));
99104
dynamicExecutionCheckBox = new JCheckBox("Dynamic Execution");
100105
dynamicExecutionCheckBox.setToolTipText("Include Dynamic Execution inside a Sandbox Environment with the Analysis");

src/main/java/ai/reveng/toolkit/ghidra/binarysimilarity/ui/recentanalyses/RecentAnalysisDialog.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package ai.reveng.toolkit.ghidra.binarysimilarity.ui.recentanalyses;
22

3+
import ai.reveng.toolkit.ghidra.binarysimilarity.ui.dialog.RevEngDialogComponentProvider;
34
import ai.reveng.toolkit.ghidra.core.RevEngAIAnalysisStatusChangedEvent;
45
import ai.reveng.toolkit.ghidra.core.services.api.GhidraRevengService;
56
import ai.reveng.toolkit.ghidra.core.services.api.types.LegacyAnalysisResult;
67
import ai.reveng.toolkit.ghidra.core.services.api.types.BinaryHash;
78
import ai.reveng.toolkit.ghidra.core.types.ProgramWithBinaryID;
8-
import docking.DialogComponentProvider;
9+
import ai.reveng.toolkit.ghidra.plugins.ReaiPluginPackage;
910
import ghidra.framework.plugintool.PluginTool;
1011
import ghidra.program.model.listing.Program;
11-
import ghidra.util.Msg;
1212
import ghidra.util.table.GhidraFilterTable;
1313

1414
import javax.swing.*;
15+
import java.awt.*;
1516
import java.awt.event.MouseAdapter;
1617
import java.awt.event.MouseEvent;
1718
import java.util.Comparator;
@@ -21,22 +22,35 @@
2122
* Shows a dialog with a table of {@link LegacyAnalysisResult} for a given {@link BinaryHash},
2223
* and fires an event when the user picks an analysis
2324
*/
24-
public class RecentAnalysisDialog extends DialogComponentProvider {
25+
public class RecentAnalysisDialog extends RevEngDialogComponentProvider {
2526
private final RecentAnalysesTableModel recentAnalysesTableModel;
2627
private final GhidraFilterTable<LegacyAnalysisResult> recentAnalysesTable;
2728
private final PluginTool tool;
2829
private final Program program;
2930
private final GhidraRevengService ghidraRevengService;
3031

3132
public RecentAnalysisDialog(PluginTool tool, Program program) {
32-
super("Recent Analyses", true);
33+
super(ReaiPluginPackage.WINDOW_PREFIX + "Recent Analyses", true);
3334
this.tool = tool;
3435
this.program = program;
3536
this.ghidraRevengService = tool.getService(GhidraRevengService.class);
37+
3638
var hash = new BinaryHash(program.getExecutableSHA256());
3739
recentAnalysesTableModel = new RecentAnalysesTableModel(tool, hash, this.program.getImageBase());
3840
recentAnalysesTable = new GhidraFilterTable<>(recentAnalysesTableModel);
3941

42+
buildInterface();
43+
setPreferredSize(600, 400);
44+
}
45+
46+
private void buildInterface() {
47+
JPanel mainPanel = new JPanel(new BorderLayout());
48+
49+
// Create title panel
50+
JPanel titlePanel = createTitlePanel("Find existing analyses for this binary");
51+
mainPanel.add(titlePanel, BorderLayout.NORTH);
52+
53+
// Create the table content
4054
// Add mouse listener to handle clicks on the Analysis ID column
4155
recentAnalysesTable.getTable().addMouseListener(new MouseAdapter() {
4256
@Override
@@ -58,6 +72,7 @@ public void mouseClicked(MouseEvent e) {
5872
}
5973
}
6074
});
75+
mainPanel.add(recentAnalysesTable, BorderLayout.CENTER);
6176

6277
JButton pickMostRecentButton = new JButton("Pick most recent");
6378
pickMostRecentButton.setName("Pick most recent");
@@ -77,8 +92,7 @@ public void mouseClicked(MouseEvent e) {
7792
});
7893
addButton(pickSelectedButton);
7994

80-
rootPanel.add(recentAnalysesTable);
81-
95+
addWorkPanel(mainPanel);
8296
}
8397

8498
private void pickAnalysis(LegacyAnalysisResult result) {

src/main/java/ai/reveng/toolkit/ghidra/core/ui/wizard/panels/UserCredentialsPanel.java

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import java.awt.GridBagLayout;
1616
import java.awt.GridBagConstraints;
1717
import java.awt.Insets;
18+
import java.awt.Desktop;
19+
import java.net.URI;
1820
import javax.swing.event.DocumentEvent;
1921
import javax.swing.event.DocumentListener;
2022

@@ -109,14 +111,49 @@ public void changedUpdate(DocumentEvent e) {
109111
tfPortalHostname.setText("https://portal.reveng.ai");
110112
userDetailsPanel.add(tfPortalHostname, gbc);
111113

112-
// Validate button row
114+
// Retrieve API Key link row (above validate button)
113115
gbc.gridx = 0;
114116
gbc.gridy = 3;
115117
gbc.gridwidth = 2;
116118
gbc.fill = GridBagConstraints.NONE;
117119
gbc.weightx = 0.0;
118120
gbc.anchor = GridBagConstraints.CENTER;
119-
gbc.insets = new Insets(15, 5, 5, 5);
121+
gbc.insets = new Insets(10, 5, 5, 5);
122+
JLabel retrieveApiKeyLink = new JLabel("<html><a href=''>Retrieve API Key</a></html>");
123+
retrieveApiKeyLink.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
124+
retrieveApiKeyLink.addMouseListener(new java.awt.event.MouseAdapter() {
125+
@Override
126+
public void mouseClicked(java.awt.event.MouseEvent e) {
127+
try {
128+
String portalHostname = tfPortalHostname.getText().trim();
129+
if (portalHostname.isEmpty()) {
130+
portalHostname = "https://portal.reveng.ai";
131+
}
132+
// Remove trailing slash if present
133+
if (portalHostname.endsWith("/")) {
134+
portalHostname = portalHostname.substring(0, portalHostname.length() - 1);
135+
}
136+
String settingsUrl = portalHostname + "/settings";
137+
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
138+
Desktop.getDesktop().browse(new URI(settingsUrl));
139+
}
140+
} catch (Exception ex) {
141+
if (loggingService != null) {
142+
loggingService.error("Failed to open API key link: " + ex.getMessage());
143+
}
144+
}
145+
}
146+
});
147+
userDetailsPanel.add(retrieveApiKeyLink, gbc);
148+
149+
// Validate button row
150+
gbc.gridx = 0;
151+
gbc.gridy = 4;
152+
gbc.gridwidth = 2;
153+
gbc.fill = GridBagConstraints.NONE;
154+
gbc.weightx = 0.0;
155+
gbc.anchor = GridBagConstraints.CENTER;
156+
gbc.insets = new Insets(5, 5, 0, 5);
120157
JButton runTestsButton = new JButton("Validate Credentials");
121158
runTestsButton.addActionListener(e -> {
122159
var apiInfo = new ApiInfo(tfApiHostname.getText(), tfPortalHostname.getText(), tfApiKey.getText());
@@ -156,17 +193,14 @@ public void enterPanel(WizardState<SetupWizardStateKey> state) throws IllegalPan
156193
Boolean existingValidationState = (Boolean) state.get(SetupWizardStateKey.CREDENTIALS_VALIDATED);
157194

158195
if (existingApiKey != null && !existingApiKey.isEmpty()) {
159-
loggingService.info("Pre-filling API key from existing state");
160196
tfApiKey.setText(existingApiKey);
161197
}
162198

163199
if (existingHostname != null && !existingHostname.isEmpty()) {
164-
loggingService.info("Pre-filling API hostname from existing state");
165200
tfApiHostname.setText(existingHostname);
166201
}
167202

168203
if (existingPortalHostname != null && !existingPortalHostname.isEmpty()) {
169-
loggingService.info("Pre-filling Portal hostname from existing state");
170204
tfPortalHostname.setText(existingPortalHostname);
171205
}
172206

@@ -175,7 +209,6 @@ public void enterPanel(WizardState<SetupWizardStateKey> state) throws IllegalPan
175209
existingApiKey != null && !existingApiKey.isEmpty() &&
176210
existingHostname != null && !existingHostname.isEmpty()) {
177211
credentialsValidated = true;
178-
loggingService.info("Restored credentials validation state from previous session");
179212
notifyListenersOfValidityChanged();
180213
}
181214
}
@@ -198,10 +231,6 @@ public void updateStateObjectWithPanelInfo(WizardState<SetupWizardStateKey> stat
198231
state.put(SetupWizardStateKey.HOSTNAME, tfApiHostname.getText());
199232
state.put(SetupWizardStateKey.PORTAL_HOSTNAME, tfPortalHostname.getText());
200233
state.put(SetupWizardStateKey.CREDENTIALS_VALIDATED, credentialsValidated);
201-
202-
if (loggingService != null) {
203-
loggingService.info("Saved form data and validation state to state");
204-
}
205234
}
206235

207236
@Override

0 commit comments

Comments
 (0)