├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── burp ├── BurpExtender.java ├── ButtonTabComponent.java ├── EachRowEditor.java ├── Menu.java ├── Parser.java ├── SwaggerEntry.java ├── SwaggerParserTab.java ├── SwaggerSchemaTab.java ├── SwaggerTab.java └── Worker.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # burp-suite-swaggy 2 | Burp Suite extension for parsing Swagger web service definition files 3 | 4 | Many thanks to the [Wsdler](https://github.com/NetSPI/Wsdler) team for much of the UI code that made this possible. 5 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.codemagi 5 | burp-suite-swaggy 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | com.codemagi 11 | burp-suite-utils 12 | 1.0.9 13 | 14 | 15 | io.swagger 16 | swagger-compat-spec-parser 17 | 1.0.56 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-shade-plugin 30 | 2.4.3 31 | 32 | 33 | package 34 | 35 | shade 36 | 37 | 38 | false 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import com.codemagi.burp.BaseExtender; 4 | 5 | /** 6 | * Swaggy: A BurpSuite extension to parse Swagger web service definition files and produce HTTP requests 7 | * @author adetlefsen 8 | */ 9 | public class BurpExtender extends BaseExtender implements IBurpExtender, IMessageEditorTabFactory { 10 | 11 | public static final String TAB_NAME = "Swaggy"; 12 | public static final String EXTENSION_NAME = "Swaggy"; 13 | private static BurpExtender instance; 14 | private SwaggerParserTab parserTab; 15 | 16 | @Override 17 | protected void initialize() { 18 | extensionName = EXTENSION_NAME; 19 | 20 | parserTab = new SwaggerParserTab(callbacks); 21 | 22 | callbacks.registerContextMenuFactory(new Menu(callbacks, parserTab)); 23 | 24 | // register ourselves as a message editor tab factory 25 | callbacks.registerMessageEditorTabFactory(this); 26 | 27 | instance = this; 28 | } 29 | 30 | public static BurpExtender getInstance() { 31 | return instance; 32 | } 33 | 34 | @Override 35 | public IMessageEditorTab createNewInstance(IMessageEditorController controller, boolean editable) { 36 | // create a new instance of our custom editor tab 37 | return new SwaggerSchemaTab(controller, callbacks); 38 | } 39 | 40 | public SwaggerParserTab getParserTab() { 41 | return parserTab; 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/burp/ButtonTabComponent.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.awt.*; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | import java.awt.event.MouseAdapter; 7 | import java.awt.event.MouseEvent; 8 | import java.awt.event.MouseListener; 9 | 10 | import javax.swing.*; 11 | import javax.swing.plaf.basic.BasicButtonUI; 12 | 13 | /** 14 | * Component to be used as tabComponent; Contains a JLabel to show the text and a JButton to close the tab it belongs to 15 | */ 16 | public class ButtonTabComponent extends JPanel { 17 | 18 | private final JTabbedPane pane; 19 | 20 | public ButtonTabComponent(final JTabbedPane pane) { 21 | //unset default FlowLayout' gaps 22 | super(new FlowLayout(FlowLayout.LEFT, 0, 0)); 23 | if (pane == null) { 24 | throw new NullPointerException("TabbedPane is null"); 25 | } 26 | this.pane = pane; 27 | setOpaque(false); 28 | 29 | //make JLabel read titles from JTabbedPane 30 | JLabel label = new JLabel() { 31 | @Override 32 | public String getText() { 33 | int i = pane.indexOfTabComponent(ButtonTabComponent.this); 34 | if (i != -1) { 35 | return pane.getTitleAt(i); 36 | } 37 | return null; 38 | } 39 | }; 40 | 41 | add(label); 42 | //add more space between the label and the button 43 | label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); 44 | //tab button 45 | JButton button = new TabButton(); 46 | add(button); 47 | //add more space to the top of the component 48 | setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0)); 49 | } 50 | 51 | private class TabButton extends JButton implements ActionListener { 52 | 53 | public TabButton() { 54 | int size = 17; 55 | setPreferredSize(new Dimension(size, size)); 56 | setToolTipText("close this tab"); 57 | //Make the button looks the same for all Laf's 58 | setUI(new BasicButtonUI()); 59 | //Make it transparent 60 | setContentAreaFilled(false); 61 | //No need to be focusable 62 | setFocusable(false); 63 | setBorder(BorderFactory.createEtchedBorder()); 64 | setBorderPainted(false); 65 | //Making nice rollover effect 66 | //we use the same listener for all buttons 67 | addMouseListener(buttonMouseListener); 68 | setRolloverEnabled(true); 69 | //Close the proper tab by clicking the button 70 | addActionListener(this); 71 | } 72 | 73 | @Override 74 | public void actionPerformed(ActionEvent e) { 75 | int i = pane.indexOfTabComponent(ButtonTabComponent.this); 76 | if (i != -1) { 77 | pane.remove(i); 78 | SwaggerParserTab.removedTabCount++; 79 | } 80 | } 81 | 82 | //we don't want to update UI for this button 83 | @Override 84 | public void updateUI() { 85 | } 86 | 87 | //paint the cross 88 | @Override 89 | protected void paintComponent(Graphics g) { 90 | super.paintComponent(g); 91 | Graphics2D g2 = (Graphics2D) g.create(); 92 | //shift the image for pressed buttons 93 | if (getModel().isPressed()) { 94 | g2.translate(1, 1); 95 | } 96 | g2.setStroke(new BasicStroke(2)); 97 | g2.setColor(Color.BLACK); 98 | if (getModel().isRollover()) { 99 | g2.setColor(Color.BLACK); 100 | } 101 | int delta = 6; 102 | g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1); 103 | g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1); 104 | g2.dispose(); 105 | } 106 | } 107 | 108 | private final static MouseListener buttonMouseListener = new MouseAdapter() { 109 | @Override 110 | public void mouseEntered(MouseEvent e) { 111 | Component component = e.getComponent(); 112 | if (component instanceof AbstractButton) { 113 | AbstractButton button = (AbstractButton) component; 114 | button.setBorderPainted(true); 115 | } 116 | } 117 | 118 | @Override 119 | public void mouseExited(MouseEvent e) { 120 | Component component = e.getComponent(); 121 | if (component instanceof AbstractButton) { 122 | AbstractButton button = (AbstractButton) component; 123 | button.setBorderPainted(false); 124 | } 125 | } 126 | }; 127 | } -------------------------------------------------------------------------------- /src/main/java/burp/EachRowEditor.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.awt.*; 4 | import java.awt.event.MouseEvent; 5 | import java.util.EventObject; 6 | import java.util.HashMap; 7 | 8 | import javax.swing.*; 9 | import javax.swing.event.CellEditorListener; 10 | import javax.swing.table.TableCellEditor; 11 | 12 | class EachRowEditor implements TableCellEditor { 13 | 14 | protected HashMap editors; 15 | protected TableCellEditor editor, defaultEditor; 16 | JTable table; 17 | 18 | /** 19 | * Constructs a EachRowEditor. create default editor 20 | * 21 | * @see javax.swing.table.TableCellEditor 22 | * @see javax.swing.DefaultCellEditor 23 | */ 24 | public EachRowEditor(JTable table) { 25 | this.table = table; 26 | editors = new HashMap(); 27 | defaultEditor = new DefaultCellEditor(new JComboBox()); 28 | } 29 | 30 | /** 31 | * @param row table row 32 | * @param editor table cell editor 33 | */ 34 | public void setEditorAt(int row, TableCellEditor editor) { 35 | editors.put(row, editor); 36 | } 37 | 38 | @Override 39 | public Component getTableCellEditorComponent(JTable table, Object value, 40 | boolean isSelected, int row, int column) { 41 | 42 | return editor.getTableCellEditorComponent(table, value, isSelected, 43 | row, column); 44 | } 45 | 46 | 47 | @Override 48 | public Object getCellEditorValue() { 49 | return editor.getCellEditorValue(); 50 | } 51 | 52 | @Override 53 | public boolean stopCellEditing() { 54 | return editor.stopCellEditing(); 55 | } 56 | 57 | @Override 58 | public void cancelCellEditing() { 59 | editor.cancelCellEditing(); 60 | } 61 | 62 | @Override 63 | public boolean isCellEditable(EventObject anEvent) { 64 | selectEditor((MouseEvent) anEvent); 65 | return editor.isCellEditable(anEvent); 66 | } 67 | 68 | @Override 69 | public void addCellEditorListener(CellEditorListener l) { 70 | editor.addCellEditorListener(l); 71 | } 72 | 73 | @Override 74 | public void removeCellEditorListener(CellEditorListener l) { 75 | editor.removeCellEditorListener(l); 76 | } 77 | 78 | @Override 79 | public boolean shouldSelectCell(EventObject anEvent) { 80 | selectEditor((MouseEvent) anEvent); 81 | return editor.shouldSelectCell(anEvent); 82 | } 83 | 84 | protected void selectEditor(MouseEvent e) { 85 | int row; 86 | if (e == null) { 87 | row = table.getSelectionModel().getAnchorSelectionIndex(); 88 | } else { 89 | row = table.rowAtPoint(e.getPoint()); 90 | } 91 | editor = (TableCellEditor) editors.get(row); 92 | if (editor == null) { 93 | editor = defaultEditor; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/burp/Menu.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import javax.swing.*; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Timer; 9 | 10 | public class Menu implements IContextMenuFactory { 11 | 12 | private IExtensionHelpers helpers; 13 | private IBurpExtenderCallbacks callbacks; 14 | private IContextMenuInvocation invocation; 15 | private IHttpRequestResponse[] requestResponse; 16 | 17 | private SwaggerParserTab parserTab; 18 | public static Timer timer; 19 | 20 | public Menu(IBurpExtenderCallbacks callbacks, SwaggerParserTab parserTab) { 21 | helpers = callbacks.getHelpers(); 22 | this.parserTab = parserTab; 23 | this.callbacks = callbacks; 24 | timer = new Timer(); 25 | } 26 | 27 | @Override 28 | public List createMenuItems(final IContextMenuInvocation invocation) { 29 | 30 | this.invocation = invocation; 31 | 32 | //get information from the invocation 33 | IHttpRequestResponse[] ihrrs = invocation.getSelectedMessages(); 34 | 35 | JMenuItem item = new JMenuItem("Parse Swagger definition"); 36 | item.addActionListener(new MenuItemListener(ihrrs)); 37 | 38 | List list = new ArrayList<>(); 39 | list.add(item); 40 | 41 | return list; 42 | } 43 | 44 | class MenuItemListener implements ActionListener { 45 | 46 | //private IHttpRequestResponse[] requestResponse; 47 | public MenuItemListener(IHttpRequestResponse[] ihrrs) { 48 | requestResponse = ihrrs; 49 | } 50 | 51 | @Override 52 | public void actionPerformed(ActionEvent ae) { 53 | Parser parser = new Parser(callbacks, helpers, parserTab); 54 | try { 55 | new Worker(parser, invocation, parserTab, callbacks).execute(); 56 | } catch (Exception e1) { 57 | BurpExtender.getInstance().printStackTrace(e1); 58 | } 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/burp/Parser.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import com.codemagi.burp.Utils; 4 | import com.codemagi.burp.parser.HttpRequest; 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import com.fasterxml.jackson.databind.node.ObjectNode; 7 | import io.swagger.models.HttpMethod; 8 | import io.swagger.models.Model; 9 | import io.swagger.models.Operation; 10 | import io.swagger.models.Path; 11 | import io.swagger.models.Swagger; 12 | import io.swagger.models.apideclaration.ApiDeclaration; 13 | import io.swagger.models.parameters.AbstractSerializableParameter; 14 | import io.swagger.models.parameters.BodyParameter; 15 | import io.swagger.models.parameters.Parameter; 16 | import io.swagger.parser.SwaggerParser; 17 | import io.swagger.util.Json; 18 | 19 | import io.swagger.models.properties.ArrayProperty; 20 | import io.swagger.models.properties.Property; 21 | import io.swagger.models.properties.RefProperty; 22 | import io.swagger.models.resourcelisting.ResourceListing; 23 | import io.swagger.parser.SwaggerCompatConverter; 24 | import io.swagger.parser.util.SwaggerDeserializationResult; 25 | import io.swagger.report.MessageBuilder; 26 | import io.swagger.transform.migrate.ApiDeclarationMigrator; 27 | import io.swagger.transform.migrate.ResourceListingMigrator; 28 | 29 | import javax.swing.*; 30 | import java.net.URL; 31 | import java.util.ArrayList; 32 | import java.util.Date; 33 | import java.util.HashMap; 34 | import java.util.List; 35 | import java.util.Map; 36 | import java.util.Set; 37 | import org.apache.commons.lang3.StringUtils; 38 | 39 | public class Parser { 40 | 41 | public static IExtensionHelpers helpers; 42 | public static IBurpExtenderCallbacks callbacks; 43 | public static IHttpRequestResponse httpRequestResponse; 44 | public static List headers; 45 | //contains the (HashMap) structure of our built models 46 | private final Map> hashModels = new HashMap<>(); 47 | private Swagger swagger; 48 | private final SwaggerParserTab tab; 49 | 50 | public Parser(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers, SwaggerParserTab tab) { 51 | Parser.callbacks = callbacks; 52 | Parser.helpers = helpers; 53 | this.tab = tab; 54 | } 55 | 56 | public int parseSwagger(IHttpRequestResponse requestResponse, IBurpExtenderCallbacks callbacks) { 57 | callbacks.printOutput("parseSwagger"); 58 | httpRequestResponse = requestResponse; 59 | byte[] response = requestResponse.getResponse(); 60 | 61 | if (response == null) { 62 | IHttpRequestResponse request = callbacks.makeHttpRequest(requestResponse.getHttpService(), requestResponse.getRequest()); 63 | response = request.getResponse(); 64 | } 65 | if (response == null) { 66 | JOptionPane.showMessageDialog(tab.getUiComponent().getParent(), "Can't Read Response", "Error", JOptionPane.ERROR_MESSAGE); 67 | return -1; 68 | } 69 | 70 | IRequestInfo requestInfo = helpers.analyzeRequest(requestResponse); 71 | headers = requestInfo.getHeaders(); 72 | callbacks.printOutput("got headers"); 73 | 74 | URL url = requestInfo.getUrl(); 75 | callbacks.printOutput("url: " + url.toString()); 76 | 77 | String requestName = url.getHost(); 78 | callbacks.printOutput("domain: " + requestName); 79 | 80 | try { 81 | String responseBody = new String(Utils.getResponseBody(response, helpers)); 82 | callbacks.printOutput("RESPONSE BODY ----- \n" + responseBody); 83 | SwaggerParser sp = new SwaggerParser(); 84 | callbacks.printOutput("sp: " + sp); 85 | 86 | SwaggerDeserializationResult sdr = sp.readWithInfo(responseBody); 87 | callbacks.printOutput("sdr: " + sdr); 88 | callbacks.printOutput("sdr: " + sdr.getMessages()); 89 | 90 | swagger = sp.parse(responseBody); 91 | callbacks.printOutput("swagger: " + swagger); 92 | 93 | if (swagger == null) { 94 | callbacks.printOutput("Not Swagger 2.0. Trying older version"); 95 | //maybe its an older version of the swagger spec... 96 | MessageBuilder messages = new MessageBuilder(); 97 | 98 | ResourceListing resourceListing = readResourceListing(responseBody, messages); 99 | callbacks.printOutput("resourceListing: " + resourceListing); 100 | 101 | ApiDeclaration apiDeclaration = readDeclaration(responseBody, messages); 102 | callbacks.printOutput("apiDeclaration: " + apiDeclaration); 103 | if (apiDeclaration == null) apiDeclaration = new ApiDeclaration(); 104 | List apis = new ArrayList<>(); 105 | apis.add(apiDeclaration); 106 | 107 | swagger = new SwaggerCompatConverter().convert(resourceListing, apis); 108 | } 109 | 110 | callbacks.printOutput("swagger: " + swagger.getHost() + swagger.getBasePath()); 111 | 112 | //generate the models (JSON request body) 113 | List allModels = new ArrayList<>(); 114 | generateModels(swagger, allModels); 115 | 116 | //initialize the GUI tab to display the results 117 | SwaggerTab swaggerTab = tab.createTab(requestName); 118 | 119 | //create the subtabs 120 | Map paths = swagger.getPaths(); 121 | for (String pathName : paths.keySet()) { 122 | Path path = paths.get(pathName); 123 | 124 | callbacks.printOutput("path: " + pathName); 125 | callbacks.printOutput(Json.pretty(path)); 126 | 127 | URL pathUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), pathName); 128 | 129 | Map operations = path.getOperationMap(); 130 | for (HttpMethod method : operations.keySet()) { 131 | String operationName = method.name(); 132 | callbacks.printOutput(" method: " + operationName); 133 | callbacks.printOutput(Json.pretty(method)); 134 | 135 | Operation op = operations.get(method); 136 | callbacks.printOutput(" op: " + op); 137 | callbacks.printOutput(Json.pretty(op)); 138 | 139 | //create a request for this operation 140 | HttpRequest request = new HttpRequest(pathUrl, operationName); 141 | request.setHeader("Content-Type", Utils.getFirst(op.getConsumes())); 142 | request.setHeader("Accept", Utils.getFirst(op.getProduces())); 143 | request.setHeader("Origin", url.toString()); 144 | 145 | //add the params 146 | List parameters = op.getParameters(); 147 | for (Parameter p : parameters) { 148 | callbacks.printOutput(" param: " + Json.pretty(p)); 149 | switch (p.getIn()) { 150 | case "query": 151 | request.setParameter(p.getName(), getDefaultValue(p)); 152 | break; 153 | case "path": 154 | String requestPath = request.getPath(); 155 | request.setPath(requestPath.replace("{" + p.getName() + "}", getDefaultValue(p))); 156 | break; 157 | case "body": 158 | request.setBody(getDefaultValue(p)); 159 | break; 160 | case "header": 161 | request.setHeader(p.getName(), getDefaultValue(p)); 162 | break; 163 | case "form": 164 | request.setParameter(p.getName(), getDefaultValue(p)); 165 | request.convertToPost(); 166 | break; 167 | } 168 | } 169 | 170 | callbacks.printOutput("REQUEST: " + request.toString()); 171 | 172 | swaggerTab.addEntry(new SwaggerEntry(pathName, request.getBytes(), operationName, op.getDescription(), requestResponse, Json.pretty(op))); 173 | } 174 | } 175 | } catch (Exception e) { 176 | BurpExtender.getInstance().printStackTrace(e); 177 | } 178 | return 0; 179 | } 180 | 181 | public ResourceListing readResourceListing(String input, MessageBuilder messages) { 182 | ResourceListing output = null; 183 | JsonNode jsonNode; 184 | try { 185 | jsonNode = Json.mapper().readTree(input); 186 | 187 | if (jsonNode.get("swaggerVersion") == null) { 188 | return null; 189 | } 190 | ResourceListingMigrator migrator = new ResourceListingMigrator(); 191 | JsonNode transformed = migrator.migrate(messages, jsonNode); 192 | output = Json.mapper().convertValue(transformed, ResourceListing.class); 193 | } catch (java.lang.IllegalArgumentException e) { 194 | return null; 195 | } catch (Exception e) { 196 | BurpExtender.getInstance().printStackTrace(e); 197 | } 198 | return output; 199 | } 200 | 201 | public ApiDeclaration readDeclaration(String input, MessageBuilder messages) { 202 | ApiDeclaration output = null; 203 | try { 204 | JsonNode jsonNode = Json.mapper().readTree(input); 205 | 206 | // this should be moved to a json patch 207 | if (jsonNode.isObject()) { 208 | ((ObjectNode) jsonNode).remove("authorizations"); 209 | } 210 | 211 | ApiDeclarationMigrator migrator = new ApiDeclarationMigrator(); 212 | JsonNode transformed = migrator.migrate(messages, jsonNode); 213 | output = Json.mapper().convertValue(transformed, ApiDeclaration.class); 214 | } catch (java.lang.IllegalArgumentException e) { 215 | return null; 216 | } catch (Exception e) { 217 | BurpExtender.getInstance().printStackTrace(e); 218 | } 219 | return output; 220 | } 221 | 222 | private String getDefaultValue(Parameter p) { 223 | callbacks.printOutput("***** getDefaultValue ***** Parameter " + p); 224 | if (p == null) { 225 | return ""; 226 | } 227 | 228 | if (p instanceof AbstractSerializableParameter) { 229 | String type = ((AbstractSerializableParameter) p).getType(); 230 | 231 | switch (type) { 232 | case "integer": 233 | return "1234"; 234 | case "long": 235 | return "2147483648"; 236 | case "float": 237 | return "1.23"; 238 | case "double": 239 | return "3.149"; 240 | case "string": 241 | return "aeiou"; 242 | case "byte": 243 | return "1"; 244 | case "boolean": 245 | return "true"; 246 | case "date": 247 | return "2000-01-23"; 248 | case "dateTime": 249 | return "2000-01-23T04:56:07.000+00:00"; 250 | case "array": 251 | return "1,2,3"; 252 | } 253 | } else if (p instanceof BodyParameter) { 254 | Model schema = ((BodyParameter) p).getSchema(); 255 | callbacks.printOutput("BodyParameter: " + Json.pretty(schema)); 256 | String datatype = getDatatypeName(schema.getReference()); 257 | return Json.pretty(hashModels.get(datatype)); 258 | } 259 | return ""; 260 | } 261 | 262 | private Map createModelHash(String modelName, Model model) { 263 | callbacks.printOutput("***** createModelHash ***** modelName " + modelName + " Model: " + model); 264 | 265 | Map output = new HashMap<>(); 266 | 267 | //if we don't have a valid model, just return an empty output 268 | if (model == null) return output; 269 | 270 | //if we already have this model cached, return it 271 | callbacks.printOutput("hashModels: " + hashModels); 272 | if (hashModels.containsKey(modelName)) { 273 | callbacks.printOutput("CACHE HIT!"); 274 | 275 | output = hashModels.get(modelName); 276 | callbacks.printOutput(Json.pretty(output)); 277 | return output; 278 | } 279 | 280 | //first add a dummy value to the hashModels in case this is self-referential 281 | hashModels.put(modelName, output); 282 | 283 | //get the properties from the model 284 | Map properties = model.getProperties(); 285 | if (properties == null) { 286 | return output; 287 | } 288 | 289 | //for each property, add name and default value to the hash structure 290 | for (String propertyName : properties.keySet()) { 291 | Property prop = properties.get(propertyName); 292 | output.put(propertyName, getDefaultValue(prop, modelName)); 293 | } 294 | 295 | //finally, cache this representation 296 | hashModels.put(modelName, output); 297 | 298 | return output; 299 | } 300 | 301 | private Object getDefaultValue(Property prop, String modelName) { 302 | callbacks.printOutput("***** getDefaultValue ***** Property " + prop + " Model: " + modelName); 303 | if (prop == null) { 304 | return ""; 305 | } 306 | 307 | if (prop instanceof RefProperty) { 308 | //a reference property is a reference to another model object 309 | //so get a reference to the object type 310 | RefProperty r = (RefProperty) prop; 311 | String datatype = r.get$ref(); 312 | datatype = getDatatypeName(datatype); 313 | callbacks.printOutput("RefProperty! " + datatype); 314 | 315 | //check for self-referential 316 | if (datatype.equals(modelName)) { 317 | return new Object(); 318 | 319 | } else if (hashModels.containsKey(datatype)) { 320 | //we have already constructed this data type as a hashmodel, 321 | //add it to the output 322 | Object hashModel = hashModels.get(datatype); 323 | return hashModel; 324 | 325 | } else { 326 | //we don't yet have a hashmodel, so construct one 327 | Model refModel = getModel(datatype); 328 | Map hashModel = createModelHash(datatype, refModel); 329 | return hashModel; 330 | 331 | } 332 | } else if (prop instanceof ArrayProperty) { 333 | //an array property is an array of some other type of property 334 | callbacks.printOutput("ArryProperty! " + prop); 335 | ArrayProperty a = (ArrayProperty) prop; 336 | 337 | //get the type of object this array holds 338 | Property arrayItem = a.getItems(); 339 | 340 | //get the default value for the object in the array 341 | Object arrayIemValue = getDefaultValue(arrayItem, modelName); 342 | 343 | //construct an array containing the item 344 | Object[] arrayModel = {arrayIemValue}; 345 | 346 | //add it to the output 347 | return arrayModel; 348 | 349 | } else { 350 | //we are dealing with a primitive type, so just get a default value 351 | String type = prop.getType(); 352 | 353 | switch (type) { 354 | case "integer": 355 | return 1234; 356 | case "long": 357 | return 2147483648l; 358 | case "float": 359 | return 1.23f; 360 | case "double": 361 | return 3.149d; 362 | case "string": 363 | if ("date-time".equals(prop.getFormat())) { 364 | return "2016-12-15 13:25:30:00"; 365 | } else if ("date".equals(prop.getFormat())) { 366 | return "2016-12-15"; 367 | } else { 368 | return "aeiou"; 369 | } 370 | case "byte": 371 | return (byte) 1; 372 | case "boolean": 373 | return true; 374 | case "date": 375 | return new Date(); 376 | case "dateTime": 377 | return new Date(); 378 | } 379 | } 380 | 381 | //if all else fails... 382 | return ""; 383 | } 384 | 385 | private String getDatatypeName(String datatype) { 386 | if (datatype.indexOf("#/definitions/") == 0) { 387 | datatype = datatype.substring("#/definitions/".length()); 388 | } 389 | return datatype; 390 | } 391 | 392 | private Model getModel(String modelName) { 393 | final Map definitions = swagger.getDefinitions(); 394 | return definitions.get(modelName); 395 | } 396 | 397 | private void generateModels(Swagger swagger, List allModels) { 398 | callbacks.printOutput("***** generateModels *****"); 399 | 400 | final Map definitions = swagger.getDefinitions(); 401 | callbacks.printOutput("definitions: " + definitions); 402 | if (definitions == null) { 403 | return; 404 | } 405 | 406 | Set modelKeys = definitions.keySet(); 407 | callbacks.printOutput("modelKeys: " + StringUtils.join(definitions, ",")); 408 | 409 | // process models only 410 | for (String modelName : modelKeys) { 411 | callbacks.printOutput("PROCESSING -------------------"); 412 | callbacks.printOutput(modelName); 413 | 414 | try { 415 | Model model = definitions.get(modelName); 416 | callbacks.printOutput("MODEL -----------------------\n"); 417 | callbacks.printOutput(Json.pretty(model)); 418 | 419 | //construct a hashmap representation of our model which can be output as JSON 420 | Map modelHash = createModelHash(modelName, model); 421 | callbacks.printOutput("JSON ------------------------ " + modelName + "\n"); 422 | try { 423 | callbacks.printOutput(Json.pretty(modelHash)); 424 | } catch (Exception e) { 425 | BurpExtender.getInstance().printStackTrace(e); 426 | } 427 | 428 | } catch (Exception e) { 429 | callbacks.printError("Could not process model '" + modelName + "'" + ".Please make sure that your schema is correct!"); 430 | BurpExtender.getInstance().printStackTrace(e); 431 | throw new RuntimeException("Could not process model '" + modelName + "'" + ".Please make sure that your schema is correct!", e); 432 | } 433 | } 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /src/main/java/burp/SwaggerEntry.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | public class SwaggerEntry { 4 | 5 | final String path; 6 | final byte[] request; 7 | final String operationName; 8 | final IHttpRequestResponse requestResponse; 9 | final String endpoints; 10 | final String json; 11 | 12 | SwaggerEntry(String bindingName, byte[] request, String operationName, String endpoints, IHttpRequestResponse requestResponse, String json) { 13 | this.path = bindingName; 14 | this.request = request; 15 | this.operationName = operationName; 16 | this.endpoints = endpoints; 17 | this.requestResponse = requestResponse; 18 | this.json = json; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/burp/SwaggerParserTab.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.awt.*; 4 | import java.util.ArrayList; 5 | 6 | import javax.swing.*; 7 | 8 | public class SwaggerParserTab implements ITab { 9 | 10 | JTabbedPane tabbedPane; 11 | private IBurpExtenderCallbacks callbacks; 12 | static int tabCount = 0; 13 | static int removedTabCount = 0; 14 | private java.util.List tabs = new ArrayList<>(); 15 | 16 | public SwaggerParserTab(final IBurpExtenderCallbacks callbacks) { 17 | this.callbacks = callbacks; 18 | 19 | tabbedPane = new JTabbedPane(); 20 | 21 | callbacks.customizeUiComponent(tabbedPane); 22 | 23 | callbacks.addSuiteTab(SwaggerParserTab.this); 24 | } 25 | 26 | public SwaggerTab createTab(String request) { 27 | 28 | SwaggerTab swaggerTab = new SwaggerTab(callbacks, tabbedPane, request); 29 | tabbedPane.setSelectedIndex(tabCount - removedTabCount); 30 | tabCount++; 31 | 32 | tabs.add(swaggerTab); 33 | 34 | return swaggerTab; 35 | } 36 | 37 | @Override 38 | public String getTabCaption() { 39 | return "Swaggy"; 40 | } 41 | 42 | @Override 43 | public Component getUiComponent() { 44 | return tabbedPane; 45 | } 46 | 47 | public boolean isSwaggerMessage(byte[] message) { 48 | for (SwaggerTab tab : tabs) { 49 | if (tab.containsEntry(message)) return true; 50 | } 51 | return false; 52 | } 53 | 54 | public String getSwaggerJson(byte[] message) { 55 | for (SwaggerTab tab : tabs) { 56 | SwaggerEntry entry = tab.getEntry(message); 57 | if (entry != null) return entry.json; 58 | } 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/burp/SwaggerSchemaTab.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.awt.Component; 4 | 5 | /** 6 | * 7 | * @author adetlefsen 8 | */ 9 | public class SwaggerSchemaTab implements IMessageEditorTab { 10 | 11 | private ITextEditor txtInput; 12 | private byte[] currentMessage; 13 | 14 | public SwaggerSchemaTab(IMessageEditorController controller, IBurpExtenderCallbacks callbacks) { 15 | // create an instance of Burp's text editor, to display our deserialized data 16 | txtInput = callbacks.createTextEditor(); 17 | txtInput.setEditable(false); 18 | } 19 | 20 | @Override 21 | public String getTabCaption() { 22 | return "Swagger"; 23 | } 24 | 25 | @Override 26 | public Component getUiComponent() { 27 | return txtInput.getComponent(); 28 | } 29 | 30 | @Override 31 | public boolean isEnabled(byte[] content, boolean isRequest) { 32 | BurpExtender extender = BurpExtender.getInstance(); 33 | 34 | return isRequest && extender.getParserTab().isSwaggerMessage(content); 35 | } 36 | 37 | @Override 38 | public void setMessage(byte[] content, boolean isRequest) { 39 | //save the message 40 | currentMessage = content; 41 | 42 | //set the JSON definition into the text area 43 | BurpExtender extender = BurpExtender.getInstance(); 44 | String json = extender.getParserTab().getSwaggerJson(content); 45 | 46 | txtInput.setText(json.getBytes()); 47 | } 48 | 49 | @Override 50 | public byte[] getMessage() { 51 | return currentMessage; 52 | } 53 | 54 | @Override 55 | public boolean isModified() { 56 | return txtInput.isTextModified(); 57 | } 58 | 59 | @Override 60 | public byte[] getSelectedData() { 61 | return txtInput.getSelectedText(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/burp/SwaggerTab.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.awt.*; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import javax.swing.*; 8 | import javax.swing.table.AbstractTableModel; 9 | import javax.swing.table.TableModel; 10 | 11 | public class SwaggerTab extends AbstractTableModel implements IMessageEditorController { 12 | 13 | private final List entries = new ArrayList<>(); 14 | private SwaggerTable swaggerTable; 15 | private EachRowEditor rowEditor; 16 | private IMessageEditor requestViewer; 17 | private IHttpRequestResponse currentlyDisplayedItem; 18 | JSplitPane splitPane; 19 | JTabbedPane tabbedPane; 20 | 21 | private final IBurpExtenderCallbacks callbacks; 22 | 23 | public SwaggerTab(final IBurpExtenderCallbacks callbacks, JTabbedPane tabbedPane, String request) { 24 | this.callbacks = callbacks; 25 | this.tabbedPane = tabbedPane; 26 | splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); 27 | swaggerTable = new SwaggerTable(this); 28 | swaggerTable.setAutoCreateRowSorter(true); 29 | 30 | rowEditor = new EachRowEditor(swaggerTable); 31 | JScrollPane scrollPane = new JScrollPane(swaggerTable); 32 | 33 | splitPane.setLeftComponent(scrollPane); 34 | 35 | JTabbedPane tabs = new JTabbedPane(); 36 | requestViewer = callbacks.createMessageEditor(this, false); 37 | tabs.addTab("Request", requestViewer.getComponent()); 38 | splitPane.setTopComponent(scrollPane); 39 | splitPane.setBottomComponent(tabs); 40 | tabbedPane.add(request, splitPane); 41 | tabbedPane.setTabComponentAt(SwaggerParserTab.tabCount - SwaggerParserTab.removedTabCount, new ButtonTabComponent(tabbedPane)); 42 | 43 | } 44 | 45 | public void addEntry(SwaggerEntry entry) { 46 | synchronized (entries) { 47 | int row = entries.size(); 48 | entries.add(entry); 49 | fireTableRowsInserted(row, row); 50 | UIManager.put("tabbedPane.selected", 51 | new javax.swing.plaf.ColorUIResource(Color.RED)); 52 | } 53 | } 54 | 55 | public boolean containsEntry(byte[] message) { 56 | for (SwaggerEntry entry : entries) { 57 | if (message == entry.request) return true; 58 | } 59 | 60 | return false; 61 | } 62 | 63 | public SwaggerEntry getEntry(byte[] message) { 64 | for (SwaggerEntry entry : entries) { 65 | if (message == entry.request) return entry; 66 | } 67 | 68 | return null; 69 | } 70 | 71 | @Override 72 | public int getRowCount() { 73 | return entries.size(); 74 | } 75 | 76 | @Override 77 | public int getColumnCount() { 78 | return 3; 79 | } 80 | 81 | @Override 82 | public String getColumnName(int columnIndex) { 83 | switch (columnIndex) { 84 | case 0: 85 | return "Operation"; 86 | case 1: 87 | return "Path"; 88 | case 2: 89 | return "Description"; 90 | default: 91 | return ""; 92 | } 93 | } 94 | 95 | @Override 96 | public Class getColumnClass(int columnIndex) { 97 | return String.class; 98 | } 99 | 100 | @Override 101 | public Object getValueAt(int rowIndex, int columnIndex) { 102 | 103 | SwaggerEntry swaggerEntry = entries.get(rowIndex); 104 | 105 | switch (columnIndex) { 106 | case 0: 107 | return swaggerEntry.operationName; 108 | case 1: 109 | return swaggerEntry.path; 110 | case 2: 111 | return swaggerEntry.endpoints; 112 | default: 113 | return ""; 114 | } 115 | } 116 | 117 | @Override 118 | public boolean isCellEditable(int row, int col) { 119 | return col >= 2; 120 | } 121 | 122 | @Override 123 | public byte[] getRequest() { 124 | return currentlyDisplayedItem.getRequest(); 125 | } 126 | 127 | @Override 128 | public byte[] getResponse() { 129 | return currentlyDisplayedItem.getResponse(); 130 | } 131 | 132 | @Override 133 | public IHttpService getHttpService() { 134 | return currentlyDisplayedItem.getHttpService(); 135 | } 136 | 137 | private class SwaggerTable extends JTable { 138 | 139 | public SwaggerTable(TableModel tableModel) { 140 | super(tableModel); 141 | } 142 | 143 | @Override 144 | public void changeSelection(int row, int col, boolean toggle, boolean extend) { 145 | 146 | SwaggerEntry swaggerEntry = entries.get(super.convertRowIndexToModel(row)); 147 | requestViewer.setMessage(swaggerEntry.request, true); 148 | currentlyDisplayedItem = swaggerEntry.requestResponse; 149 | super.changeSelection(row, col, toggle, extend); 150 | } 151 | 152 | private boolean painted; 153 | 154 | @Override 155 | public void paint(Graphics g) { 156 | super.paint(g); 157 | 158 | if (!painted) { 159 | painted = true; 160 | splitPane.setResizeWeight(.30); 161 | splitPane.setDividerLocation(0.30); 162 | } 163 | } 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/burp/Worker.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.awt.Color; 4 | import java.util.TimerTask; 5 | import javax.swing.JDialog; 6 | import javax.swing.JProgressBar; 7 | import javax.swing.JTabbedPane; 8 | import javax.swing.SwingWorker; 9 | 10 | /** 11 | * 12 | * @author adetlefsen 13 | */ 14 | class Worker extends SwingWorker { 15 | 16 | private JDialog dialog = new JDialog(); 17 | private Parser parser; 18 | private IContextMenuInvocation invocation; 19 | private SwaggerParserTab tab; 20 | private IBurpExtenderCallbacks callbacks; 21 | private int status; 22 | 23 | public Worker(Parser parser, IContextMenuInvocation invocation, SwaggerParserTab tab, IBurpExtenderCallbacks callbacks) { 24 | JProgressBar progressBar = new JProgressBar(); 25 | progressBar.setString("Parsing Swagger definition"); 26 | progressBar.setStringPainted(true); 27 | progressBar.setIndeterminate(true); 28 | dialog.getContentPane().add(progressBar); 29 | dialog.pack(); 30 | dialog.setLocationRelativeTo(tab.getUiComponent().getParent()); 31 | dialog.setModal(false); 32 | dialog.setVisible(true); 33 | this.parser = parser; 34 | this.invocation = invocation; 35 | this.tab = tab; 36 | this.callbacks = callbacks; 37 | } 38 | 39 | @Override 40 | protected Void doInBackground() throws Exception { 41 | status = parser.parseSwagger(invocation.getSelectedMessages()[0], callbacks); 42 | return null; 43 | } 44 | 45 | @Override 46 | protected void done() { 47 | dialog.dispose(); 48 | if (status != -1 && status != -2 && status != -3) { 49 | { 50 | final JTabbedPane parent = (JTabbedPane) tab.getUiComponent().getParent(); 51 | final int index = parent.indexOfComponent(tab.getUiComponent()); 52 | parent.setBackgroundAt(index, new Color(229, 137, 1)); 53 | 54 | Menu.timer.schedule(new TimerTask() { 55 | @Override 56 | public void run() { 57 | parent.setBackgroundAt(index, new Color(0, 0, 0)); 58 | } 59 | }, 5000); 60 | 61 | } 62 | } 63 | } 64 | } --------------------------------------------------------------------------------