├── LICENSE ├── README.md └── swagger_parser.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Trendyol 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwaggerParser-BurpExtension 2 | 3 | With this extension, you can parse Swagger Documents. You can view the parsed requests in the table and send them to Repeater, Intruder, Scanner. 4 | 5 | ## How to use 6 | 7 | **1- Extension written in Python. That's why he works with Jython. We need to add the Jython jar file to Burp.** 8 | 9 | ![jython_install](https://github.com/bulutenes/SwaggerParser-BurpExtension/assets/150332295/1a657087-b9ed-4b3d-9fc3-352c15cf855c) 10 | 11 | 12 | **2- After adding Jython to Burp, we can also add the Extension to Burp with the Extension's python file.** 13 | 14 | ![extension_install](https://github.com/bulutenes/SwaggerParser-BurpExtension/assets/150332295/3a178569-db57-420b-93ef-88be59b528c0) 15 | 16 | 17 | **3- If the extension has been installed successfully, the "Swagger Parser" tab will be added. You can see the extension screen by clicking this tab.** 18 | 19 | main_screen 20 | 21 | **Add New Swagger Document Panel:** This is the part where new Swagger Documents are added and edited. 22 | 23 | add_new_doc 24 | 25 | 26 | **Request Detail Panel:** This is the section where the details of the parsed requests are displayed. 27 | 28 | request_detail 29 | 30 | 31 | **Custom Headers Panel:** Headers written below in this panel are added to all requests while parsing. 32 | 33 | ![custom_headers](https://github.com/bulutenes/SwaggerParser-BurpExtension/assets/150332295/b3e5e47a-b668-4d15-9b34-31e37f3637c8) 34 | 35 | 36 | **Output Panel:** After the parse process is completed, all endpoints are listed in Markdown format. 37 | 38 | ![markdown_output](https://github.com/bulutenes/SwaggerParser-BurpExtension/assets/150332295/3ad59bc9-05f6-426e-b4be-71548c954217) 39 | 40 | 41 | **Request History Panel:** After the parse process is completed, the requests are listed in the table and can be sent to the Repeater, Intruder, Scanner. 42 | 43 | ![send_to_feature](https://github.com/bulutenes/SwaggerParser-BurpExtension/assets/150332295/c8536ba6-ca77-40bc-9c4c-d3202f7ed2bd) 44 | 45 | 46 | **4- We right-click on the Swagger Document request we want to parse and select the "Send to Swagger Parser" option and the parsing process begins.** 47 | 48 | ![send_to_swagger_parser](https://github.com/bulutenes/SwaggerParser-BurpExtension/assets/150332295/23acef55-b256-48f1-a3a5-3f5abec63345) 49 | -------------------------------------------------------------------------------- /swagger_parser.py: -------------------------------------------------------------------------------- 1 | from burp import IBurpExtender 2 | from burp import IProxyListener 3 | from burp import IContextMenuFactory 4 | from java.util import ArrayList 5 | from javax.swing import JMenuItem 6 | from java.awt.event import MouseAdapter, MouseEvent 7 | from javax.swing import (JTabbedPane, DefaultComboBoxModel, BoxLayout,GroupLayout, JPanel, JComboBox, JCheckBox, JTextField, JTextArea, JLabel, JButton, JScrollPane, JTable, JPopupMenu, JTextPane, JFrame) 8 | from java.awt import (Insets, BorderLayout, GridBagLayout, GridBagConstraints, Dimension, Toolkit, FlowLayout, GridLayout) 9 | from javax import swing 10 | from burp import ITab 11 | from javax.swing.table import (DefaultTableModel) 12 | import javax.swing.KeyStroke as KeyStroke 13 | import java.awt.event.KeyEvent as KeyEvent 14 | import javax.swing.AbstractAction as AbstractAction 15 | import java.awt.event.ComponentAdapter as ComponentAdapter 16 | 17 | import threading 18 | import json 19 | import re 20 | import random 21 | import string 22 | 23 | class SwaggerParser: 24 | def __init__(self, swagger_url, headers): 25 | self.swagger_url = swagger_url 26 | self.headers = headers 27 | 28 | 29 | def urlEncodingForEnum(self, param): 30 | return str(param).replace(" ","%20").replace(":", "%3A").replace("=","%3D").replace(",","%2C").replace(";","%3B") 31 | 32 | def randomValueGenerator(self, _param_name, _param_type, _schema): 33 | 34 | try: 35 | _temp_value = None 36 | _enum = _schema.get("enum") 37 | _default = _schema.get("default") 38 | _items = _schema.get("items") 39 | _format = _schema.get("format") 40 | 41 | if _param_type is None: 42 | _param_type = _schema.get("type") 43 | 44 | if _param_type == "string": 45 | 46 | _temp_value = ''.join(random.sample(string.ascii_lowercase, 8)) 47 | 48 | if _default is not None: 49 | 50 | if str(_default).strip() == "": 51 | _default = "xxx" 52 | 53 | _temp_value = _default 54 | elif _enum is not None and type(_enum) == list and len(_enum) > 0: 55 | _temp_value = self.urlEncodingForEnum(_enum[0]) 56 | elif _format is not None: 57 | if _format in ["date", "date-time"]: 58 | _temp_value = "01-01-2023" 59 | 60 | elif _param_type == "integer": 61 | 62 | _temp_value = random.randrange(100) 63 | elif _param_type == "number": 64 | 65 | _temp_value = random.randrange(100) 66 | elif _param_type == "boolean": 67 | 68 | _temp_value = True 69 | elif _param_type == "file": 70 | 71 | _temp_value = "RandomStringInput"#''.join(random.choices(string.ascii_lowercase, k=5)) 72 | elif _param_type == "array": 73 | 74 | return [self.randomValueGenerator(None, None, _items)] 75 | elif len(_schema.keys()) > 0: 76 | 77 | _temp_obj = {} 78 | for _item_key in _schema: 79 | _temp_obj[_item_key] = self.randomValueGenerator(None, None, _schema.get(_item_key)) 80 | return _temp_obj 81 | else: 82 | "" #for debugging 83 | 84 | if _param_name is not None: 85 | return {_param_name: _temp_value} 86 | else: 87 | return _temp_value 88 | 89 | except Exception as e: 90 | print("randomValueGenerator error") 91 | print(e) 92 | 93 | def refObjectParser(self, _swagger_obj, _ref_value): 94 | 95 | _request_body_parameters_obj = _swagger_obj 96 | 97 | if "/" in _ref_value: 98 | _splitted_ref = str(_ref_value).split("/") 99 | for _ref_path in _splitted_ref: 100 | if _ref_path != "#" and _ref_path != "": 101 | if _ref_path in _request_body_parameters_obj: 102 | 103 | _request_body_parameters_obj = _request_body_parameters_obj.get(_ref_path) 104 | 105 | if "properties" in _request_body_parameters_obj.keys() and _request_body_parameters_obj.get( 106 | "type") == "object": 107 | _request_body_parameters_obj = _request_body_parameters_obj["properties"] 108 | 109 | else: 110 | return {} 111 | 112 | return _request_body_parameters_obj 113 | 114 | def findAndParseAllRefs(self, _swagger_obj, _constat_swagger_obj): 115 | 116 | if type(_swagger_obj) == dict: 117 | for _key in _swagger_obj.keys(): 118 | if _key == "$ref": 119 | _temp_parsed_ref_obj = self.refObjectParser(_constat_swagger_obj, _swagger_obj.get(_key)) 120 | 121 | for _temp_key in _temp_parsed_ref_obj: 122 | if _temp_key == "-": 123 | continue 124 | _swagger_obj[_temp_key] = _temp_parsed_ref_obj.get(_temp_key) 125 | _swagger_obj.pop(_key) 126 | break 127 | 128 | self.findAndParseAllRefs(_swagger_obj.get(_key), _constat_swagger_obj) 129 | elif type(_swagger_obj) == list: 130 | for _item in _swagger_obj: 131 | self.findAndParseAllRefs(_item, _constat_swagger_obj) 132 | else: 133 | "" #for debugging 134 | 135 | return _swagger_obj 136 | 137 | _all_keys = {} 138 | 139 | def generateRequest(self, _path, _method, _request_obj, _output_obj): 140 | 141 | if type(_request_obj) == dict: 142 | for _key in _request_obj.keys(): 143 | self._all_keys[_key] = {} 144 | _temp_key = _key 145 | 146 | _schema = _request_obj.get("schema") 147 | _value = _request_obj.get(_temp_key) 148 | _name = _request_obj.get("name") 149 | _type = _request_obj.get("type") 150 | _enum = _request_obj.get("enum") 151 | 152 | if _temp_key in ["responses"]: 153 | continue 154 | 155 | if _temp_key == "requestBody": 156 | _value = "body" 157 | _temp_key = "in" 158 | 159 | if _request_obj.get("requestBody").get("content").get("application/json") is not None: 160 | _schema = _request_obj.get("requestBody").get("content").get("application/json").get("schema") 161 | elif _request_obj.get("requestBody").get("content").get("multipart/form-data") is not None: 162 | _schema = _request_obj.get("requestBody").get("content").get("multipart/form-data").get("schema") 163 | 164 | if _schema is None: 165 | _schema = _request_obj 166 | 167 | if _temp_key == "in": 168 | 169 | _new_value = self.randomValueGenerator(_name, _type, _schema) 170 | 171 | if _value == "path": 172 | 173 | if _output_obj.get("path") is not None: 174 | _path = _output_obj.get("path") 175 | 176 | _temp_value = None 177 | 178 | if type(_new_value) == dict: 179 | _temp_value = str(_new_value.get(_name)) 180 | elif type(_new_value) == list: 181 | _temp_value = ",".join(str(v) for v in _new_value) 182 | else: 183 | _temp_value = str(_new_value) 184 | 185 | _path = str(_path).replace("{" + _name + "}", _temp_value) 186 | 187 | _output_obj["path"] = _path 188 | 189 | elif _value == "query": 190 | 191 | if _output_obj.get("query_string") is None: 192 | _output_obj["query_string"] = {} 193 | 194 | if type(_new_value) == dict: 195 | _temp_value = _new_value.get(_name) 196 | 197 | for _query_key in _new_value.keys(): 198 | _output_obj["query_string"][_query_key] = _new_value.get(_query_key) 199 | 200 | else: 201 | _output_obj["query_string"][_name] = _new_value 202 | 203 | 204 | elif _value == "header": 205 | 206 | if _output_obj.get("header") is None: 207 | _output_obj["header"] = {} 208 | 209 | _output_obj["header"][_name] = str(_new_value.get(_name)) 210 | 211 | elif _value == "body": 212 | 213 | _output_obj["body"] = _new_value 214 | 215 | if _output_obj.get("header") is None: 216 | _output_obj["header"] = {} 217 | 218 | _output_obj["header"]["Content-Type"] = "application/json" 219 | 220 | elif _value == "formData": 221 | 222 | #application/x-www-form-urlencoded or multipart/form-data 223 | 224 | _output_obj["formData"] = _new_value 225 | 226 | if _output_obj.get("header") is None: 227 | _output_obj["header"] = {} 228 | 229 | _output_obj["header"]["Content-Type"] = "application/x-www-form-urlencoded" 230 | 231 | #todo 232 | 233 | if type(_request_obj.get(_key)) == str: 234 | continue 235 | self.generateRequest(_path, _method, _request_obj.get(_key), _output_obj) 236 | elif type(_request_obj) == list: 237 | for _item in _request_obj: 238 | if type(_item) == str: 239 | continue 240 | self.generateRequest(_path, _method, _item, _output_obj) 241 | else: 242 | "" #for debugging 243 | 244 | if _output_obj.get("path") is None: 245 | _output_obj["path"] = _path 246 | 247 | _output_obj["method"] = _method 248 | 249 | return _output_obj 250 | 251 | 252 | def parseResponse(self, _url, _response): 253 | global SERVICE_URL 254 | 255 | _swagger_raw_json = "" 256 | _swagger_json_object = {} 257 | 258 | if "/swagger/swagger-ui-init.js" in _url or "/docs/swagger-ui-init.js" in _url: 259 | _search_result = re.search("[var|let] options = (.*?);", str(_response).replace("\n","")) 260 | 261 | if _search_result and len(_search_result.groups()) > 0: 262 | _swagger_raw_json = _search_result.groups()[0] 263 | _swagger_raw_json = _swagger_raw_json.replace("\n", "").replace(" ", "").replace("\\n", "") 264 | _temp_json_obj = json.loads(_swagger_raw_json) 265 | if "swaggerDoc" in _temp_json_obj.keys(): 266 | _swagger_json_object = _temp_json_obj["swaggerDoc"] 267 | else: 268 | _swagger_json_object = json.loads(_response) 269 | 270 | _parsed_swagger_json_object = self.findAndParseAllRefs(_swagger_json_object, _swagger_json_object) 271 | 272 | _total_path = 0 273 | _endpoints = [] 274 | _markup_endpoints = [] 275 | 276 | _root_url = self.getRootUrl(self.swagger_url) 277 | 278 | for _path in _swagger_json_object["paths"].keys(): 279 | _path_obj = _swagger_json_object["paths"][_path] 280 | 281 | if len(_path_obj.keys()) > 0: 282 | for _method in _path_obj.keys(): 283 | _method_obj = _path_obj[_method] 284 | 285 | _temp_tag = "default" 286 | 287 | if "tags" in _method_obj.keys(): 288 | _temp_tag = _method_obj["tags"][0] 289 | 290 | 291 | 292 | _all_root_keys = list(_method_obj.keys()) 293 | 294 | for _key in _all_root_keys: 295 | if _key not in ["parameters", "requestBody"]: 296 | _method_obj.pop(_key) 297 | 298 | _request_obj = self.generateRequest(_path, _method, _method_obj, {}) 299 | 300 | _request_obj["raw_path"] = _path 301 | 302 | _markup_endpoints.append("- [ ] " + str(_method).upper() + " " + _path) 303 | _endpoints.append(_request_obj) 304 | 305 | 306 | _total_path += len(_swagger_json_object["paths"][_path].keys()) 307 | 308 | print("Total endpoint: " + str(_total_path)) 309 | 310 | basePath = "" 311 | 312 | if _swagger_json_object.get("basePath") != None: 313 | basePath = _swagger_json_object.get("basePath") 314 | 315 | return {"endpoints": _endpoints, "base_path": basePath, "markup_endpoints": _markup_endpoints} 316 | 317 | 318 | def generateQueryString(self, param_obj): 319 | param_obj = dict(param_obj) 320 | temp_query_string = "" 321 | 322 | for _key in param_obj.keys(): 323 | 324 | _value = param_obj.get(_key) 325 | 326 | if type(_value) == list: 327 | _value = json.dumps(_value) 328 | else: 329 | _value = str(_value) 330 | 331 | temp_query_string += str(_key) + "=" + _value + "&" 332 | 333 | 334 | if temp_query_string.endswith("&"): 335 | temp_query_string = temp_query_string.strip("&") 336 | 337 | return temp_query_string 338 | 339 | def bytesToString(self, _bytes): 340 | 341 | char_arr = [] 342 | 343 | for b in _bytes: 344 | print(b) 345 | if b < 257 and b > -1: 346 | char_arr.append(chr(b)) 347 | 348 | return "".join(char_arr) 349 | 350 | def getRootUrl(self, _url): 351 | 352 | _protocol = "https://" 353 | 354 | if str(_url).startswith("https://"): 355 | _protocol = "https://" 356 | elif str(_url).startswith("http://"): 357 | _protocol = "http://" 358 | 359 | 360 | _url = str(_url).replace(_protocol, "") 361 | 362 | if "/" in _url: 363 | _url = str(_url).split("/")[0] 364 | 365 | return _protocol + _url 366 | 367 | 368 | main_panel = None 369 | header_text_editor = None 370 | request_detail_text_editor = None 371 | output_text_editor = None 372 | history_table = None 373 | popup_menu = None 374 | extracted_requests = [] 375 | output_scroll_pane = None 376 | parsable_docs_combobox = None 377 | parsable_docs = {} 378 | popup_frame = None 379 | remove_confirmation_popup_frame = None 380 | global_parent_self = None 381 | tabbedPane = None 382 | tabbedPane2 = None 383 | 384 | 385 | def isValidSwaggerDoc(doc): 386 | doc = str(doc) 387 | return (doc.startswith("http://") or doc.startswith("https://")) and ( 388 | doc.endswith("json") or "api-docs" in doc or doc.endswith("swagger-ui-init.js")) 389 | 390 | class MenuClickListener(MouseAdapter): 391 | def __init__(self, extender, invocation): 392 | self._extender = extender 393 | self._invocation = invocation 394 | 395 | def mouseReleased(self, e): 396 | self._extender.menuItemClicked(self._invocation) 397 | 398 | last_table_selections = [] 399 | 400 | 401 | class MoveAction(AbstractAction): 402 | def __init__(self, table, direction): 403 | self.table = table 404 | self.direction = direction 405 | 406 | def actionPerformed(self, e): 407 | row = self.table.getSelectedRow() + self.direction 408 | 409 | if row >= 0 and row < self.table.getRowCount(): 410 | self.table.setRowSelectionInterval(row, row) 411 | 412 | request_item = extracted_requests[row] 413 | 414 | http_request = request_item["http_request"] 415 | 416 | request_detail_text_editor.setText(global_parent_self._helpers.bytesToString(http_request)) 417 | 418 | tabbedPane.setSelectedIndex(1) 419 | 420 | 421 | class TableMenuClickListener(MouseAdapter): 422 | def __init__(self, extender, invocation): 423 | self._extender = extender 424 | self._invocation = invocation 425 | 426 | def mouseReleased(self, e): 427 | global popup_menu 428 | global history_table 429 | global header_text_editor 430 | global last_table_selections 431 | global extracted_requests 432 | global global_parent_self 433 | global request_detail_text_editor 434 | global tabbedPane 435 | 436 | if e.getButton() == MouseEvent.BUTTON3: 437 | popup_menu.show(e.getComponent(), e.getX(), e.getY()) 438 | 439 | if e.getButton() == MouseEvent.BUTTON1: 440 | current_selections = list(history_table.getSelectedRows()) 441 | 442 | temp_selection = list(set(current_selections) - set(last_table_selections)) 443 | single_selection = -1 444 | 445 | if len(temp_selection) > 0: 446 | single_selection = temp_selection[0] 447 | 448 | if single_selection != -1: 449 | 450 | request_item = extracted_requests[single_selection] 451 | 452 | http_request = request_item["http_request"] 453 | 454 | request_detail_text_editor.setText(global_parent_self._helpers.bytesToString(http_request)) 455 | 456 | tabbedPane.setSelectedIndex(1) 457 | 458 | last_table_selections = current_selections 459 | 460 | 461 | 462 | class RemoveConfirmationPopup(swing.JPanel): 463 | parent_self = None 464 | 465 | def __init__(self, remove_all, parent): 466 | super(RemoveConfirmationPopup, self).__init__() 467 | 468 | self.parent_self = parent 469 | popup_title = "Are you sure you want to remove Selected Items?" 470 | 471 | if remove_all: 472 | popup_title = "Are you sure you want to remove All Items?" 473 | 474 | layout = GridBagLayout() 475 | self.setLayout(layout) 476 | gbc = GridBagConstraints() 477 | 478 | label = swing.JLabel(popup_title) 479 | gbc.gridx = 0 480 | gbc.gridy = 0 481 | gbc.gridwidth = 2 482 | gbc.insets = Insets(5, 5, 5, 5) 483 | self.add(label, gbc) 484 | 485 | blank_panel = swing.JPanel() 486 | blank_panel.setPreferredSize(Dimension(1, 100)) 487 | gbc.gridx = 0 488 | gbc.gridy = 1 489 | gbc.gridwidth = 2 490 | self.add(blank_panel, gbc) 491 | 492 | button_panel = swing.JPanel() 493 | button_panel.setLayout(GridLayout(1, 2, 10, 0)) 494 | 495 | no_button = swing.JButton("No", actionPerformed=self.close_popup) 496 | no_button.setPreferredSize(Dimension(60, 25)) 497 | button_panel.add(no_button) 498 | 499 | if remove_all: 500 | yes_button = swing.JButton("Yes", actionPerformed=self.confirm_all_removal) 501 | yes_button.setPreferredSize(Dimension(60, 25)) 502 | button_panel.add(yes_button) 503 | else: 504 | yes_button = swing.JButton("Yes", actionPerformed=self.confirm_removal) 505 | yes_button.setPreferredSize(Dimension(60, 25)) 506 | button_panel.add(yes_button) 507 | 508 | 509 | 510 | gbc.gridx = 0 511 | gbc.gridy = 2 512 | gbc.gridwidth = 2 513 | gbc.insets = Insets(0, 0, 10, 0) 514 | self.add(button_panel, gbc) 515 | 516 | def close_popup(self, event): 517 | frame = swing.SwingUtilities.getWindowAncestor(self) 518 | frame.dispose() 519 | 520 | def confirm_all_removal(self, event): 521 | self.parent_self.clearTable(event) 522 | self.close_popup(None) 523 | 524 | def confirm_removal(self, event): 525 | self.parent_self.removeSelectedItems(event) 526 | self.close_popup(None) 527 | 528 | 529 | class ResizeListener(ComponentAdapter): 530 | def componentResized(self, e): 531 | 532 | if main_panel is not None: 533 | screen_size = main_panel.getSize() 534 | screen_height = screen_size.height 535 | screen_width = screen_size.width 536 | 537 | output_components_max_height = int(screen_height / 3) 538 | output_components_max_width = int(screen_width / 2) 539 | 540 | tabbedPane.setMinimumSize( 541 | Dimension(output_components_max_width - 10, output_components_max_height)) 542 | 543 | tabbedPane.setMaximumSize( 544 | Dimension(output_components_max_width - 10, output_components_max_height)) 545 | 546 | tabbedPane2.setMinimumSize( 547 | Dimension(output_components_max_width - 10, output_components_max_height - 2)) 548 | 549 | tabbedPane2.setMaximumSize( 550 | Dimension(output_components_max_width - 10, output_components_max_height - 2)) 551 | 552 | 553 | class SwaggerParserTab(ITab): 554 | parent_self = None 555 | 556 | def __init__(self, callbacks, parent): 557 | global global_parent_self 558 | 559 | self._callbacks = callbacks 560 | self.parent_self = parent 561 | global_parent_self = parent 562 | 563 | class NonEditableTableModel(DefaultTableModel): 564 | def isCellEditable(self, row, column): 565 | return False 566 | 567 | def getTabCaption(self): 568 | return "Swagger Parser" 569 | 570 | def stringToBytes(self, text, encoding='utf-8'): 571 | return text.encode(encoding) 572 | 573 | def bytesToString(self, _bytes): 574 | 575 | char_arr = [] 576 | 577 | for b in _bytes: 578 | if b < 257 and b > -1: 579 | char_arr.append(chr(b)) 580 | 581 | return "".join(char_arr) 582 | 583 | def sendHttpRequest(self, url): 584 | global parsable_docs 585 | 586 | url = str(url) 587 | 588 | protocol = "https" 589 | port = 443 590 | 591 | if not url.startswith(protocol): 592 | protocol = "http" 593 | port = 80 594 | 595 | hostname = url.replace(protocol + "://", "").split("/")[0] 596 | 597 | if ":" in hostname: 598 | temp_hostname_split = hostname.split(":") 599 | hostname = temp_hostname_split[0] 600 | port = int(temp_hostname_split[1]) 601 | 602 | 603 | if port in [80, 443]: 604 | temp_url_1 = url.replace("://" + hostname + "/", "://" + hostname + ":" + str(port) + "/") 605 | temp_url_2 = url.replace("://" + hostname + ":" + str(port) + "/", "://" + hostname + "/") 606 | 607 | if temp_url_1 != temp_url_2 and temp_url_1 in list(parsable_docs.keys()) and temp_url_2 in list(parsable_docs.keys()): 608 | del parsable_docs[temp_url_1] 609 | url = temp_url_2 610 | 611 | 612 | 613 | swagger_doc_path = url.replace(protocol + "://" + hostname, "") 614 | 615 | http_service = self.parent_self._helpers.buildHttpService(hostname, port, protocol) 616 | 617 | request = "GET " + swagger_doc_path + " HTTP/2\r\nHost: " + hostname + "\r\n\r\n" 618 | 619 | 620 | def make_request(): 621 | global parsable_docs 622 | 623 | response = self.parent_self._callbacks.makeHttpRequest(http_service, request.encode()) 624 | 625 | ref_url = str(response.getUrl()) 626 | 627 | parsable_docs[ref_url] = response 628 | 629 | 630 | thread = threading.Thread(target=make_request) 631 | thread.start() 632 | 633 | return 634 | 635 | def add_component(self, component, gridx, gridy, anchor): 636 | gbc = GridBagConstraints() 637 | gbc.gridx = gridx 638 | gbc.gridy = gridy 639 | gbc.anchor = anchor 640 | self.right_panel.add(component, gbc) 641 | 642 | 643 | def openRemoveConfirmationPopup(self, event, remove_all): 644 | global remove_confirmation_popup_frame 645 | 646 | if remove_confirmation_popup_frame is not None: 647 | remove_confirmation_popup_frame.dispose() 648 | 649 | frame_width = 300 650 | 651 | if not remove_all: 652 | frame_width = 320 653 | 654 | remove_confirmation_popup_frame = JFrame("Confirm", size=(frame_width, 125)) 655 | remove_confirmation_popup_frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE) 656 | remove_confirmation_popup_frame.setLayout(BorderLayout()) 657 | remove_confirmation_popup_frame.setResizable(False) 658 | 659 | remove_confirmation_popup = RemoveConfirmationPopup(remove_all, self) 660 | remove_confirmation_popup_frame.add(remove_confirmation_popup) 661 | 662 | remove_confirmation_popup_frame.setLocationRelativeTo(None) 663 | remove_confirmation_popup_frame.setVisible(True) 664 | 665 | def addNewUrl(self, event): 666 | global popup_frame 667 | 668 | if popup_frame is not None: 669 | popup_frame.dispose() 670 | 671 | popup_frame = JFrame("Add New Swagger Document", size=(612, 300)) 672 | popup_frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE) 673 | popup_frame.setLayout(BorderLayout()) 674 | popup_frame.setResizable(False) 675 | 676 | self.main_panel = JPanel() 677 | self.main_panel.setLayout(BorderLayout()) 678 | self.main_panel.setPreferredSize(Dimension(608, 300)) 679 | 680 | self.left_panel = JPanel() 681 | self.left_panel.setLayout(BorderLayout()) 682 | 683 | self.text_field = JTextField(20) 684 | self.text_field.setPreferredSize(Dimension(500, 25)) 685 | 686 | self.table_model = self.NonEditableTableModel() 687 | self.table_model.addColumn("URL") 688 | 689 | temp_combobox_items = self.getComboboxItems() 690 | 691 | if len(temp_combobox_items) > 0: 692 | for item in temp_combobox_items: 693 | self.table_model.addRow([item]) 694 | 695 | self.table = JTable(self.table_model) 696 | self.table.setPreferredScrollableViewportSize(Dimension(500, 150)) 697 | self.table.setTableHeader(None) 698 | 699 | self.scroll_pane = JScrollPane(self.table) 700 | 701 | self.left_panel.add(self.text_field, BorderLayout.NORTH) 702 | self.left_panel.add(self.scroll_pane, BorderLayout.CENTER) 703 | 704 | self.right_panel = JPanel() 705 | self.right_panel.setPreferredSize(Dimension(110, 300)) 706 | self.right_panel.setLayout(GridBagLayout()) 707 | 708 | self.button1 = JButton("Add") 709 | self.button2 = JButton("Remove") 710 | self.button3 = JButton("Remove All") 711 | 712 | self.empty_panel = JPanel() 713 | self.empty_panel.setPreferredSize(Dimension(10, 176)) 714 | 715 | button_size = Dimension(100, 25) 716 | self.button1.setPreferredSize(button_size) 717 | self.button2.setPreferredSize(button_size) 718 | self.button3.setPreferredSize(button_size) 719 | 720 | gbc = GridBagConstraints() 721 | gbc.fill = GridBagConstraints.HORIZONTAL 722 | gbc.anchor = GridBagConstraints.NORTHWEST 723 | 724 | gbc.insets = Insets(2, 0, 2, 0) 725 | gbc.gridx = 0 726 | gbc.gridy = 0 727 | self.right_panel.add(self.button1, gbc) 728 | 729 | gbc.gridy = 1 730 | self.right_panel.add(self.button2, gbc) 731 | 732 | gbc.gridy = 2 733 | self.right_panel.add(self.button3, gbc) 734 | 735 | gbc.gridy = 3 736 | self.right_panel.add(self.empty_panel, gbc) 737 | 738 | self.button1.addActionListener(self.addUrlToTable) 739 | self.button2.addActionListener(lambda event: self.openRemoveConfirmationPopup(event, False)) 740 | self.button3.addActionListener(lambda event: self.openRemoveConfirmationPopup(event, True)) 741 | 742 | self.main_panel.add(self.left_panel, BorderLayout.WEST) 743 | self.main_panel.add(self.right_panel, BorderLayout.EAST) 744 | 745 | popup_frame.add(self.main_panel, BorderLayout.CENTER) 746 | 747 | popup_frame.setLocationRelativeTo(None) 748 | popup_frame.setVisible(True) 749 | 750 | 751 | def syncTables(self): 752 | global parsable_docs 753 | global parsable_docs_combobox 754 | 755 | model = self.table.getModel() 756 | model.setRowCount(0) 757 | 758 | parsable_docs_combobox.setModel(DefaultComboBoxModel([])) 759 | 760 | for doc_item in sorted(list(dict(parsable_docs).keys())): 761 | model.addRow([doc_item]) 762 | parsable_docs_combobox.addItem(doc_item) 763 | 764 | def addToParcableDocsDict(self, new_item): 765 | global parsable_docs 766 | 767 | if new_item not in list(dict(parsable_docs).keys()): 768 | parsable_docs[new_item] = "" #TODO request will be generate 769 | 770 | self.sendHttpRequest(new_item) 771 | 772 | def addUrlToTable(self, event): 773 | global parsable_docs 774 | 775 | text = str(self.text_field.getText()) 776 | 777 | if not isValidSwaggerDoc(text): 778 | return 779 | 780 | self.addToParcableDocsDict(text) 781 | self.syncTables() 782 | 783 | self.text_field.setText("") 784 | self.text_field.requestFocus() 785 | 786 | def removeSelectedItems(self, event): 787 | global parsable_docs 788 | 789 | selected_rows = list(self.table.getSelectedRows()) 790 | table_model = self.table.getModel() 791 | table_changed = False 792 | 793 | for row in selected_rows: 794 | value = table_model.getValueAt(row, 0) 795 | if value in parsable_docs.keys(): 796 | del parsable_docs[value] 797 | table_changed = True 798 | 799 | 800 | if table_changed: 801 | self.syncTables() 802 | 803 | 804 | 805 | def clearTable(self, event): 806 | global parsable_docs 807 | parsable_docs = {} 808 | self.syncTables() 809 | self.text_field.requestFocus() 810 | 811 | 812 | def getComboboxItems(self): 813 | global parsable_docs 814 | return sorted(list(dict(parsable_docs).keys())) 815 | 816 | def getSelectedComboboxItem(self, event): 817 | global parsable_docs_combobox 818 | global parsable_docs 819 | 820 | selected_item = parsable_docs_combobox.getSelectedItem() 821 | 822 | if selected_item != None: 823 | self.parent_self.startParseFromUI(parsable_docs[str(selected_item)]) 824 | 825 | def getUiComponent(self): 826 | global header_text_editor 827 | global output_text_editor 828 | global history_table 829 | global output_scroll_pane 830 | global parsable_docs_combobox 831 | global request_detail_text_editor 832 | global tabbedPane 833 | global tabbedPane2 834 | global main_panel 835 | 836 | main_panel = JPanel() 837 | main_panel.addComponentListener(ResizeListener()) 838 | layout = GroupLayout(main_panel) 839 | main_panel.setLayout(layout) 840 | layout.setAutoCreateGaps(True) 841 | layout.setAutoCreateContainerGaps(True) 842 | 843 | header_label = JLabel("Swagger Docs: ") 844 | header_text_editor = JTextPane() 845 | header_text_editor.setText("X-Forwarded-For: 127.0.0.1\nAuthorization: Bearer [TOKEN]") 846 | header_scroll_pane = JScrollPane(header_text_editor) 847 | 848 | request_detail_text_editor = JTextPane() 849 | request_detail_text_editor.setText("") 850 | request_detail_scroll_pane = JScrollPane(request_detail_text_editor) 851 | 852 | 853 | 854 | tabbedPane = JTabbedPane() 855 | 856 | tabbedPane.addTab("Custom Headers", header_scroll_pane) 857 | tabbedPane.addTab("Request Detail", request_detail_scroll_pane) 858 | 859 | 860 | 861 | output_label = JLabel("") 862 | output_text_editor = JTextPane() 863 | output_scroll_pane = JScrollPane(output_text_editor) 864 | 865 | tabbedPane2 = JTabbedPane() 866 | 867 | tabbedPane2.addTab("Output", output_scroll_pane) 868 | 869 | table_model = self.NonEditableTableModel() 870 | table_model.addColumn("Method") 871 | table_model.addColumn("URL") 872 | table_model.addColumn("Status Code") 873 | table_model.addColumn("Length") 874 | 875 | parsable_docs_combobox = JComboBox([]) 876 | add_button = JButton("Start Parsing", actionPerformed=self.getSelectedComboboxItem) 877 | add_button2 = JButton("Add New Doc", actionPerformed=self.addNewUrl) 878 | 879 | history_table = JTable(table_model) 880 | 881 | column_model = history_table.getColumnModel() 882 | column_model.getColumn(0).setMinWidth(80) 883 | column_model.getColumn(0).setMaxWidth(80) 884 | column_model.getColumn(2).setMinWidth(120) 885 | column_model.getColumn(2).setMaxWidth(120) 886 | column_model.getColumn(3).setMinWidth(80) 887 | column_model.getColumn(3).setMaxWidth(80) 888 | 889 | history_scroll_pane = JScrollPane(history_table) 890 | 891 | history_table.addMouseListener(TableMenuClickListener(self, history_table)) 892 | 893 | input_map = history_table.getInputMap() 894 | action_map = history_table.getActionMap() 895 | 896 | input_map.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Up") 897 | action_map.put("Up", MoveAction(history_table, -1)) 898 | 899 | input_map.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Down") 900 | action_map.put("Down", MoveAction(history_table, 1)) 901 | 902 | screen_size = Toolkit.getDefaultToolkit().getScreenSize() 903 | screen_height = screen_size.getHeight() 904 | screen_width = screen_size.getWidth() 905 | 906 | output_components_max_height = int(screen_height / 3) 907 | output_components_max_width = int(screen_width / 2) 908 | 909 | tabbedPane.setMinimumSize( 910 | Dimension(output_components_max_width - 10, output_components_max_height)) 911 | 912 | tabbedPane.setMaximumSize( 913 | Dimension(output_components_max_width - 10, output_components_max_height)) 914 | 915 | tabbedPane2.setMinimumSize( 916 | Dimension(output_components_max_width - 10, output_components_max_height - 2)) 917 | 918 | tabbedPane2.setMaximumSize( 919 | Dimension(output_components_max_width - 10, output_components_max_height - 2)) 920 | 921 | 922 | layout.setHorizontalGroup( 923 | layout.createParallelGroup() 924 | .addGroup(layout.createSequentialGroup() 925 | .addComponent(header_label) 926 | .addComponent(parsable_docs_combobox) 927 | .addComponent(add_button) 928 | .addComponent(add_button2) 929 | .addComponent(output_label)) 930 | .addGroup(layout.createSequentialGroup() 931 | .addComponent(tabbedPane) 932 | .addComponent(tabbedPane2)) 933 | .addComponent(history_scroll_pane) 934 | ) 935 | 936 | layout.setVerticalGroup( 937 | layout.createSequentialGroup() 938 | .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 939 | .addComponent(header_label) 940 | .addComponent(parsable_docs_combobox) 941 | .addComponent(add_button) 942 | .addComponent(add_button2) 943 | .addComponent(output_label)) 944 | .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 945 | .addComponent(tabbedPane) 946 | .addComponent(tabbedPane2)) 947 | .addComponent(history_scroll_pane) 948 | ) 949 | 950 | return main_panel 951 | 952 | class SequentialThread(threading.Thread): 953 | def __init__(self, func, args): 954 | super(SequentialThread, self).__init__() 955 | self.func = func 956 | self.args = args 957 | 958 | def run(self): 959 | self.func(*self.args) 960 | 961 | class BurpExtender(IBurpExtender, IContextMenuFactory, IProxyListener): 962 | 963 | def bytesToString(self, _bytes): 964 | 965 | char_arr = [] 966 | 967 | for b in _bytes: 968 | if b < 257 and b > -1: 969 | char_arr.append(chr(b)) 970 | 971 | return "".join(char_arr) 972 | 973 | def registerExtenderCallbacks(self, callbacks): 974 | global popup_menu 975 | 976 | self._callbacks = callbacks 977 | self._helpers = callbacks.getHelpers() 978 | callbacks.setExtensionName("Swagger Parser") 979 | 980 | 981 | popup_menu = JPopupMenu() 982 | menu_item = JMenuItem("Send to Repeater") 983 | menu_item.addActionListener(self.tableMenuItemClickedToRepeater) 984 | popup_menu.add(menu_item) 985 | menu_item = JMenuItem("Send to Intruder") 986 | menu_item.addActionListener(self.tableMenuItemClickedToIntruder) 987 | popup_menu.add(menu_item) 988 | menu_item = JMenuItem("Send to Scanner") 989 | menu_item.addActionListener(self.tableMenuItemClickedToScanner) 990 | popup_menu.add(menu_item) 991 | 992 | 993 | callbacks.registerContextMenuFactory(self) 994 | 995 | callbacks.registerProxyListener(self) 996 | 997 | tab = SwaggerParserTab(callbacks, self) 998 | callbacks.addSuiteTab(tab) 999 | 1000 | def createMenuItems(self, invocation): 1001 | self.invocation = invocation 1002 | menuList = ArrayList() 1003 | 1004 | menuItem = JMenuItem("Send to Swagger Parser") 1005 | menuItem.addMouseListener(MenuClickListener(self, invocation)) 1006 | 1007 | 1008 | menuList.add(menuItem) 1009 | 1010 | history_table.addMouseListener(TableMenuClickListener(self, invocation)) 1011 | 1012 | 1013 | return menuList 1014 | 1015 | def processProxyMessage(self, messageIsRequest, message): 1016 | global parsable_docs 1017 | global parsable_docs_combobox 1018 | 1019 | if not messageIsRequest: 1020 | message = message.getMessageInfo() 1021 | 1022 | request = message.getRequest() 1023 | 1024 | analyzedRequest = self._helpers.analyzeRequest(request) 1025 | 1026 | doc_url = str(message.getUrl().toString()).strip() 1027 | 1028 | response = message.getResponse() 1029 | analyzedResponse = self._helpers.analyzeResponse(response) 1030 | headers = analyzedResponse.getHeaders() 1031 | responseBody = response[analyzedResponse.getBodyOffset():].tostring() 1032 | 1033 | if ("swagger" in responseBody or "openapi" in responseBody) and "paths" in responseBody and "info" in responseBody and (doc_url.endswith("json") or "api-docs" in doc_url or doc_url.endswith("swagger-ui-init.js")): 1034 | parsable_docs[doc_url] = message 1035 | 1036 | parsable_docs_combobox.setModel(DefaultComboBoxModel([])) 1037 | for doc_item in sorted(list(dict(parsable_docs).keys())): 1038 | parsable_docs_combobox.addItem(doc_item) 1039 | 1040 | 1041 | 1042 | 1043 | def tableMenuItemClickedToScanner(self, event): 1044 | global history_table 1045 | global extracted_requests 1046 | 1047 | selected_rows = history_table.getSelectedRows() 1048 | 1049 | for selected_row in selected_rows: 1050 | 1051 | request_item = extracted_requests[selected_row] 1052 | http_service = request_item["http_service"] 1053 | http_request = request_item["http_request"] 1054 | 1055 | is_https = True 1056 | 1057 | if http_service.getProtocol() != "https": 1058 | is_https = False 1059 | 1060 | self._callbacks.doActiveScan(http_service.getHost(), http_service.getPort(), is_https, 1061 | http_request) 1062 | 1063 | def tableMenuItemClickedToIntruder(self, event): 1064 | global history_table 1065 | global extracted_requests 1066 | 1067 | selected_rows = history_table.getSelectedRows() 1068 | 1069 | for selected_row in selected_rows: 1070 | 1071 | request_item = extracted_requests[selected_row] 1072 | http_service = request_item["http_service"] 1073 | http_request = request_item["http_request"] 1074 | 1075 | is_https = True 1076 | 1077 | if http_service.getProtocol() != "https": 1078 | is_https = False 1079 | 1080 | 1081 | self._callbacks.sendToIntruder(http_service.getHost(), http_service.getPort(), is_https, 1082 | http_request, None) 1083 | 1084 | 1085 | def tableMenuItemClickedToRepeater(self, event): 1086 | global history_table 1087 | global extracted_requests 1088 | 1089 | selected_rows = history_table.getSelectedRows() 1090 | 1091 | for selected_row in selected_rows: 1092 | 1093 | request_item = extracted_requests[selected_row] 1094 | http_service = request_item["http_service"] 1095 | http_request = request_item["http_request"] 1096 | 1097 | is_https = True 1098 | 1099 | if http_service.getProtocol() != "https": 1100 | is_https = False 1101 | 1102 | 1103 | title_endpoint = str(request_item["request_url"]).split("?")[0].split("#")[0] 1104 | 1105 | tab_title = request_item["request_method"] + " " + title_endpoint 1106 | 1107 | self._callbacks.sendToRepeater(http_service.getHost(), http_service.getPort(), is_https, 1108 | http_request, tab_title) 1109 | 1110 | 1111 | def runParser(self, traffic, main_url): 1112 | global header_text_editor 1113 | global output_text_editor 1114 | global output_scroll_pane 1115 | 1116 | request_info = self._helpers.analyzeRequest(traffic) 1117 | request_headers = request_info.getHeaders() 1118 | request_body = traffic.getRequest()[request_info.getBodyOffset():] 1119 | 1120 | response = traffic.getResponse() 1121 | response_info = self._helpers.analyzeResponse(response) 1122 | 1123 | response_headers = response_info.getHeaders() 1124 | response_body_bytes = response[response_info.getBodyOffset():] 1125 | 1126 | http_version = str(request_headers[0]).split(" ")[-1] 1127 | basic_headers = request_headers[1:] 1128 | 1129 | response_body_str = self.bytesToString(response_body_bytes) 1130 | 1131 | swagger_parser = SwaggerParser(swagger_url=main_url, headers={}) 1132 | 1133 | parsed_swagger = swagger_parser.parseResponse(main_url, response_body_str) 1134 | 1135 | endpoints = parsed_swagger.get("endpoints") 1136 | 1137 | self.resetTable() 1138 | 1139 | for endpoint in endpoints: 1140 | 1141 | temp_query_string_dict = endpoint.get("query_string") 1142 | 1143 | temp_path = parsed_swagger.get("base_path") 1144 | temp_path += endpoint.get("path") 1145 | 1146 | temp_headers_dict = {} 1147 | 1148 | 1149 | if temp_query_string_dict != None: 1150 | temp_path += "?" + swagger_parser.generateQueryString(temp_query_string_dict) 1151 | 1152 | temp_first_header = endpoint.get("method").upper() + " " + temp_path + " " + http_version 1153 | temp_headers = [temp_first_header] 1154 | 1155 | 1156 | #temp_headers.extend(basic_headers) 1157 | 1158 | for basic_header in basic_headers: 1159 | if ":" in basic_header: 1160 | basic_header_key_value = basic_header.split(":") 1161 | temp_headers_dict[basic_header_key_value[0]] = ":".join(basic_header_key_value[1:]) 1162 | 1163 | temp_custom_headers_dict = endpoint.get("header") # from swagger 1164 | 1165 | if temp_custom_headers_dict != None: 1166 | 1167 | temp_custom_headers_dict = dict(endpoint.get("header")) 1168 | 1169 | for _key in temp_custom_headers_dict.keys(): 1170 | temp_headers_dict[_key] = temp_custom_headers_dict.get(_key) 1171 | 1172 | custom_headers_from_ui = str(header_text_editor.getText()).split("\n") # from ui 1173 | 1174 | for custom_header_from_ui in custom_headers_from_ui: 1175 | clean_custom_header_from_ui = custom_header_from_ui.strip() # todo 1176 | if clean_custom_header_from_ui != "" and ":" in clean_custom_header_from_ui: 1177 | clean_custom_header_from_ui_key_value = clean_custom_header_from_ui.split(":") 1178 | temp_headers_dict[clean_custom_header_from_ui_key_value[0]] = ":".join(clean_custom_header_from_ui_key_value[1:]) 1179 | 1180 | for header_key in temp_headers_dict.keys(): 1181 | temp_headers.append(header_key + ":" + temp_headers_dict.get(header_key)) 1182 | 1183 | temp_body = endpoint.get("body") 1184 | 1185 | if temp_body != None: 1186 | temp_body = json.dumps(temp_body) 1187 | else: 1188 | temp_body = endpoint.get("formData") 1189 | 1190 | if temp_body != None: 1191 | temp_body = swagger_parser.generateQueryString(temp_body) 1192 | else: 1193 | temp_body = "" 1194 | 1195 | get_swagger_request = self._helpers.buildHttpMessage(temp_headers, temp_body) 1196 | 1197 | threads = [] 1198 | 1199 | temp_thread = SequentialThread(self.makeHttpRequest, (traffic.getHttpService(), get_swagger_request, 1200 | "/".join(main_url.split("/")[:3]) + temp_path, 1201 | endpoint.get("method").upper())) 1202 | 1203 | threads.append(temp_thread) 1204 | 1205 | for thread in threads: 1206 | thread.start() 1207 | 1208 | for thread in threads: 1209 | thread.join() 1210 | 1211 | markup_endpoints = parsed_swagger.get("markup_endpoints") 1212 | 1213 | output_markup_endpoints = main_url + "\n\n" 1214 | 1215 | for markup_endpoint in markup_endpoints: 1216 | output_markup_endpoints += markup_endpoint + "\n" 1217 | 1218 | output_markup_endpoints += "\nTotal: " + str(len(markup_endpoints)) 1219 | 1220 | output_markup_endpoints += "\n\n" 1221 | 1222 | output_text_editor.setText(output_markup_endpoints) 1223 | 1224 | output_scroll_pane.revalidate() 1225 | output_scroll_pane.repaint() 1226 | 1227 | def resetTable(self): 1228 | global history_table 1229 | global extracted_requests 1230 | 1231 | table_model = history_table.getModel() 1232 | while table_model.getRowCount() > 0: 1233 | table_model.removeRow(0) 1234 | 1235 | extracted_requests = [] 1236 | 1237 | def loadingOutputEditor(self): 1238 | global output_text_editor 1239 | 1240 | output_text_editor.setText("Loading...") 1241 | 1242 | def loadingTable(self): 1243 | global history_table 1244 | 1245 | table_model = history_table.getModel() 1246 | table_model.addRow(["Loading...", "Loading...", "Loading...", "Loading..."]) 1247 | 1248 | def startParseFromUI(self, traffic): 1249 | 1250 | if traffic.getUrl() != None: 1251 | main_url = str(traffic.getUrl().toString()) 1252 | 1253 | if isValidSwaggerDoc(main_url): 1254 | self.resetTable() 1255 | self.loadingTable() 1256 | self.loadingOutputEditor() 1257 | 1258 | threading.Thread(target=self.runParser, 1259 | args=(traffic, main_url)).start() 1260 | 1261 | def menuItemClicked(self, event): 1262 | global header_text_editor 1263 | global output_text_editor 1264 | global output_scroll_pane 1265 | global history_table 1266 | 1267 | 1268 | httpTraffic = self.invocation.getSelectedMessages() 1269 | 1270 | for traffic in httpTraffic: 1271 | 1272 | self.startParseFromUI(traffic) 1273 | 1274 | 1275 | def makeHttpRequest(self, target_service, post_request, request_url, request_method): 1276 | global history_table 1277 | global extracted_requests 1278 | 1279 | if str(request_url).strip() == "" or str(request_method).strip() == "": 1280 | return 1281 | 1282 | try: 1283 | response = self._callbacks.makeHttpRequest(target_service, post_request) 1284 | 1285 | if response: 1286 | response_info = self._helpers.analyzeResponse(response.getResponse()) 1287 | response_headers = response_info.getHeaders() 1288 | response_body = response.getResponse()[response_info.getBodyOffset():] 1289 | 1290 | response_body_str = self.bytesToString(response_body) 1291 | 1292 | table_model = history_table.getModel() 1293 | row_data = [request_method, request_url, response_info.getStatusCode(), len(response_body_str)] 1294 | table_model.addRow(row_data) 1295 | 1296 | extracted_requests.append( 1297 | {"http_service": target_service, "http_request": post_request, "request_method": request_method, "request_url": request_url}) 1298 | 1299 | 1300 | except Exception as e: 1301 | print("table_model add row err") 1302 | print(e) 1303 | --------------------------------------------------------------------------------