diff --git a/build.gradle b/build.gradle index d2637ec..77037c4 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { } group 'com.synfron.reshaper.burp' -version '1.8.2' +version '1.8.2-next' targetCompatibility = '15' sourceCompatibility = '15' @@ -29,6 +29,7 @@ dependencies { implementation 'net.portswigger.burp.extender:burp-extender-api:2.3' implementation 'org.rypt:f8:1.1-RC1' implementation 'org.apache.commons:commons-csv:1.9.0' + implementation 'com.alexandriasoftware.swing:jsplitbutton:1.3.1' implementation files('libs/htmlchardet-1.0.2.1.jar') compileOnly 'org.projectlombok:lombok:1.18.22' annotationProcessor 'org.projectlombok:lombok:1.18.22' diff --git a/src/main/java/synfron/reshaper/burp/core/settings/GeneralSettings.java b/src/main/java/synfron/reshaper/burp/core/settings/GeneralSettings.java index cdc648c..805c36f 100644 --- a/src/main/java/synfron/reshaper/burp/core/settings/GeneralSettings.java +++ b/src/main/java/synfron/reshaper/burp/core/settings/GeneralSettings.java @@ -15,11 +15,12 @@ public class GeneralSettings { private boolean captureExtender; private boolean enableEventDiagnostics; private int diagnosticValueMaxLength = 200; - private String RemoteImportAddress = "https://github.com/synfron/ReshaperForBurp"; private boolean enableSanityCheckWarnings = true; private boolean logInExtenderOutput = true; private int logTabCharacterLimit = 1000000; private String defaultEncoding = Encoder.getDefaultEncoderName(); + private ImportMethod importMethod = ImportMethod.File; + private String importUrl; public void importSettings(GeneralSettings other) { if (other != null) { @@ -35,7 +36,8 @@ public void importSettings(GeneralSettings other) { this.enableSanityCheckWarnings = other.enableSanityCheckWarnings; this.logInExtenderOutput = other.logInExtenderOutput; this.logTabCharacterLimit = other.logTabCharacterLimit; - this.RemoteImportAddress = other.RemoteImportAddress; + this.importMethod = other.importMethod; + this.importUrl = other.importUrl; } } @@ -51,5 +53,10 @@ public boolean isCapture(BurpTool burpTool) { case Session -> true; }; } + + public enum ImportMethod { + File, + Url + } } diff --git a/src/main/java/synfron/reshaper/burp/core/settings/SettingsManager.java b/src/main/java/synfron/reshaper/burp/core/settings/SettingsManager.java index 713c4f7..f6ac162 100644 --- a/src/main/java/synfron/reshaper/burp/core/settings/SettingsManager.java +++ b/src/main/java/synfron/reshaper/burp/core/settings/SettingsManager.java @@ -18,17 +18,21 @@ public class SettingsManager { public void importSettings(File file, boolean overwriteDuplicates) { try { - ExportSettings exportSettings = Serializer.deserialize( - Files.readString(file.toPath()), - new TypeReference<>() {} - ); - getGlobalVariables().importVariables(exportSettings.getVariables(), overwriteDuplicates); - getRulesRegistry().importRules(exportSettings.getRules(), overwriteDuplicates); + importSettings(Files.readString(file.toPath()), overwriteDuplicates); } catch (IOException e) { throw new WrappedException(e); } } + public void importSettings(String settingsJson, boolean overwriteDuplicates) { + ExportSettings exportSettings = Serializer.deserialize( + settingsJson, + new TypeReference<>() {} + ); + getGlobalVariables().importVariables(exportSettings.getVariables(), overwriteDuplicates); + getRulesRegistry().importRules(exportSettings.getRules(), overwriteDuplicates); + } + public void exportSettings(File file, List variables, List rules) { try { ExportSettings exportSettings = new ExportSettings(); diff --git a/src/main/java/synfron/reshaper/burp/ui/components/settings/SettingsTabComponent.java b/src/main/java/synfron/reshaper/burp/ui/components/settings/SettingsTabComponent.java index 547071c..706f473 100644 --- a/src/main/java/synfron/reshaper/burp/ui/components/settings/SettingsTabComponent.java +++ b/src/main/java/synfron/reshaper/burp/ui/components/settings/SettingsTabComponent.java @@ -1,6 +1,7 @@ package synfron.reshaper.burp.ui.components.settings; import burp.BurpExtender; +import com.alexandriasoftware.swing.JSplitButton; import net.miginfocom.swing.MigLayout; import org.apache.commons.io.IOUtils; import synfron.reshaper.burp.core.messages.Encoder; @@ -22,14 +23,12 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; +import java.awt.event.ItemEvent; import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.Charset; import java.util.Objects; -import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -53,7 +52,7 @@ public class SettingsTabComponent extends JPanel implements IFormComponent { private JCheckBox spider; private JCheckBox target; private JCheckBox extender; - private JTextField RemoteImportAddress; + private ButtonGroup importMethod; public SettingsTabComponent() { initComponent(); @@ -161,10 +160,6 @@ private void onLogTabCharacterLimitFocusChanged(ActionEvent actionEvent) { } } - private void onRemoteImportDataFocusChanged(ActionEvent actionEvent) { - generalSettings.setRemoteImportAddress(RemoteImportAddress.getText()); - } - private void onLogInExtenderOutputChanged(ActionEvent actionEvent) { generalSettings.setLogInExtenderOutput(logInExtenderOutput.isSelected()); } @@ -253,23 +248,45 @@ private Component getImportSettings() { ); overwriteDuplicates = new JCheckBox("Overwrite Duplicates"); - JButton importData = new JButton("Import Data"); - - RemoteImportAddress = createTextField(false); - RemoteImportAddress.setText(Objects.toString(generalSettings.getRemoteImportAddress())); - RemoteImportAddress.addFocusListener(new FocusActionListener(this::onRemoteImportDataFocusChanged)); - JButton RemoteImportData = new JButton("Remote Import Data"); - - importData.addActionListener(this::onImportData); - RemoteImportData.addActionListener(this::onRemoteImportData); + JSplitButton importData = getImportDataButton(); container.add(overwriteDuplicates); - container.add(importData,"wrap"); - container.add(getLabeledField("Remote Import Address", RemoteImportAddress), "wrap"); - container.add(RemoteImportData,"wrap"); + container.add(importData); return container; } + private JSplitButton getImportDataButton() { + JSplitButton importData = new JSplitButton("Import Data "); + + JPopupMenu importOptions = new JPopupMenu(); + + importMethod = new ButtonGroup(); + JRadioButtonMenuItem importFromFile = new JRadioButtonMenuItem("From File"); + JRadioButtonMenuItem importFromUrl = new JRadioButtonMenuItem("From URL"); + + importFromFile.setSelected(generalSettings.getImportMethod() == GeneralSettings.ImportMethod.File); + importFromFile.setActionCommand(GeneralSettings.ImportMethod.File.name()); + importFromUrl.setSelected(generalSettings.getImportMethod() == GeneralSettings.ImportMethod.Url); + importFromUrl.setActionCommand(GeneralSettings.ImportMethod.Url.name()); + + importData.addButtonClickedActionListener(this::onImportData); + importFromFile.addItemListener(this::onImportMethodChange); + + importMethod.add(importFromFile); + importMethod.add(importFromUrl); + + importOptions.add(importFromFile); + importOptions.add(importFromUrl); + + importData.setPopupMenu(importOptions); + + return importData; + } + + private void onImportMethodChange(ItemEvent itemEvent) { + generalSettings.setImportMethod(GeneralSettings.ImportMethod.valueOf(importMethod.getSelection().getActionCommand())); + } + private JFileChooser createFileChooser(String title) { FileNameExtensionFilter fileFiler = new FileNameExtensionFilter("JSON backup file", "json"); JFileChooser fileChooser = new JFileChooser(); @@ -317,10 +334,19 @@ private void onExportData(ActionEvent actionEvent) { } private void onImportData(ActionEvent actionEvent) { + switch (generalSettings.getImportMethod()) { + case File -> onImportFromFile(); + case Url -> onImportFromUrl(); + } + } + + private void onImportFromFile() { + String file = null; try { JFileChooser fileChooser = createFileChooser("Import"); int result = fileChooser.showOpenDialog(this); if (result == JFileChooser.APPROVE_OPTION) { + file = fileChooser.getSelectedFile().getAbsolutePath(); settingsManager.importSettings(fileChooser.getSelectedFile(), overwriteDuplicates.isSelected()); refreshLists(); @@ -331,33 +357,27 @@ private void onImportData(ActionEvent actionEvent) { ); } } catch (Exception e) { - Log.get().withMessage("Error importing data").withException(e).logErr(); + Log.get().withMessage("Error importing data from file").withException(e).logErr(); JOptionPane.showMessageDialog(this, - "Error importing data", + String.format("Error importing data from file: %s", file), "Import Error", JOptionPane.ERROR_MESSAGE ); } } - private void onRemoteImportData(ActionEvent actionEvent) { + private void onImportFromUrl() { + String url = null; try { - //获取远程规则 - File tmpFile = new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID()); - URL remoteUrl = new URL(Objects.toString(RemoteImportAddress.getText())); - HttpURLConnection connection = (HttpURLConnection) remoteUrl.openConnection(); - connection.setConnectTimeout(10000); - connection.setReadTimeout(10000); - InputStream responseStream = connection.getInputStream(); - if (responseStream != null) { - OutputStream outStream = new FileOutputStream(tmpFile); - outStream.write(responseStream.readAllBytes()); - IOUtils.closeQuietly(outStream); - } - - if (tmpFile.exists()) { - settingsManager.importSettings(tmpFile,overwriteDuplicates.isSelected()); + url = JOptionPane.showInputDialog("Import URL", generalSettings.getImportUrl()); + if (url != null) { + generalSettings.setImportUrl(url); + URLConnection connection = new URL(url).openConnection(); + connection.setConnectTimeout(10000); + connection.setReadTimeout(10000); + String settingsJson = IOUtils.toString(connection.getInputStream(), Charset.defaultCharset()); + settingsManager.importSettings(settingsJson, overwriteDuplicates.isSelected()); refreshLists(); JOptionPane.showMessageDialog(this, @@ -367,10 +387,10 @@ private void onRemoteImportData(ActionEvent actionEvent) { ); } } catch (Exception e) { - Log.get().withMessage("Error importing data").withException(e).logErr(); + Log.get().withMessage("Error importing data from URL").withException(e).logErr(); JOptionPane.showMessageDialog(this, - "Error importing data", + String.format("Error importing data from URL: %s", url), "Import Error", JOptionPane.ERROR_MESSAGE );