""" TypeDefinitionTab is a top-level Burp Suite tab which allows saved/named types to be added/modified at any time. It also adds options for importing/exporting protobuf types to .json files. """ import os import json import burp import blackboxprotobuf from javax.swing import JSplitPane, JScrollPane, JPanel, JButton, BoxLayout from javax.swing import JOptionPane, JList, ListSelectionModel, JFileChooser from javax.swing.filechooser import FileNameExtensionFilter from java.awt import Component from java.awt.event import ActionListener from blackboxprotobuf.burp import typedef_editor class TypeDefinitionTab(burp.ITab): """Implements an interface for editing known message type definitions.""" def __init__(self, burp_callbacks): self._burp_callbacks = burp_callbacks self._type_list_component = JList(blackboxprotobuf.known_messages.keys()) self._type_list_component.setSelectionMode(ListSelectionModel.SINGLE_SELECTION) self._component = JSplitPane(JSplitPane.HORIZONTAL_SPLIT) self._component.setLeftComponent(JScrollPane(self._type_list_component)) self._component.setRightComponent(self.createButtonPane()) self._component.setResizeWeight(0.9) def getTabCaption(self): """Returns name on tab""" return "Protobuf Type Editor" def getUiComponent(self): """Returns Java AWT component for tab""" return self._component def createButtonPane(self): """Create AWT window panel for buttons""" self._button_listener = TypeDefinitionButtonListener(self) panel = JPanel() panel.setLayout(BoxLayout(panel, BoxLayout.Y_AXIS)) panel.add(self.createButton("New Type", "new-type")) panel.add(self.createButton("Edit Type", "edit-type")) panel.add(self.createButton("Delete Type", "delete-type")) panel.add(self.createButton("Save All Types To File", "save-types")) panel.add(self.createButton("Load All Types To File", "load-types")) return panel def createButton(self, text, command): """Generate new button with the given text and command string""" button = JButton(text) button.setAlignmentX(Component.CENTER_ALIGNMENT) button.setActionCommand(command) button.addActionListener(self._button_listener) return button def updateList(self): """Let the UI know that the list of message types has been updated""" self._type_list_component.setListData(blackboxprotobuf.known_messages.keys()) class TypeDefinitionButtonListener(ActionListener): """Callback listener for buttons in the TypeDefinition interface""" def __init__(self, type_def_tab): self._type_def_tab = type_def_tab def create_save_callback(self, name): """Generate a callback for when the save button inside an opened type editor window is saved. Saves the type and tells the list to refresh. """ def save_callback(type_def): """Save typedef and update list for a given message name""" blackboxprotobuf.known_messages[name] = type_def self._type_def_tab.updateList() return save_callback def actionPerformed(self, event): """Called when a button is pressed.""" if event.getActionCommand() == "new-type": type_name = JOptionPane.showInputDialog("Enter new name") # Error out if already defined if type_name in blackboxprotobuf.known_messages: JOptionPane.showMessageDialog(self._type_def_tab._component, "Message type %s already exists" % type_name) return typedef_editor.TypeEditorWindow(self._type_def_tab._burp_callbacks, {}, self.create_save_callback(type_name)).show() elif event.getActionCommand() == "edit-type": list_component = self._type_def_tab._type_list_component # Check if something is selected if list_component.isSelectionEmpty(): return type_name = list_component.getSelectedValue() typedef_editor.TypeEditorWindow(self._type_def_tab._burp_callbacks, blackboxprotobuf.known_messages[type_name], self.create_save_callback(type_name)).show() elif event.getActionCommand() == "delete-type": list_component = self._type_def_tab._type_list_component # Check if something is selected if list_component.isSelectionEmpty(): return type_name = list_component.getSelectedValue() #TODO Confirm delete? del blackboxprotobuf.known_messages[type_name] self._type_def_tab.updateList() elif event.getActionCommand() == 'save-types': chooser = JFileChooser() chooser.setFileFilter(FileNameExtensionFilter("JSON Type Definition", ["json"])) chooser.setMultiSelectionEnabled(False) action = chooser.showSaveDialog(self._type_def_tab.getUiComponent()) if action == JFileChooser.CANCEL_OPTION or action == JFileChooser.ERROR_OPTION: return file_name = chooser.getSelectedFile().getCanonicalPath() ext = os.path.splitext(file_name)[1] if ext == '': #No extension, add .json file_name += '.json' with open(file_name, 'w+') as selected_file: json.dump(blackboxprotobuf.known_messages, selected_file, indent=4, sort_keys=True) elif event.getActionCommand() == 'load-types': chooser = JFileChooser() chooser.setFileFilter(FileNameExtensionFilter("JSON Type Definition", ["json"])) chooser.setMultiSelectionEnabled(False) action = chooser.showOpenDialog(self._type_def_tab.getUiComponent()) if action == JFileChooser.CANCEL_OPTION or action == JFileChooser.ERROR_OPTION: return file_name = chooser.getSelectedFile().getCanonicalPath() types = {} with open(file_name, 'r') as selected_file: types = json.load(selected_file) for key, value in types.items(): blackboxprotobuf.known_messages[key] = value self._type_def_tab.updateList()