├── README.md └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # Burp Scope Monitor Extension 2 | A Burp Suite Extension to monitor and keep track of tested endpoints. 3 | 4 | ![](http://g.recordit.co/MwAyLS1VkZ.gif) 5 | 6 | ## Main Features 7 | 8 | - Simple, easy way to keep track of unique endpoints when testing an application 9 | - Mark individual endpoints as analyzed or not 10 | - Instantly understand when a new endpoint, not tested is requested 11 | - Accessible from Proxy tab (right click, mark request as analyzed/not) 12 | - Send to Repeater 13 | - Enforcement of Burp's in scope rules 14 | - Import/Export state file directly to a CSV file for 15 | - Autosave option 16 | 17 | ## Installation 18 | 19 | 1. Make sure you have Jython configured under Extender -> Options -> Python Environment. For further instructions, check PortSwigger official instructions at their [support page](https://support.portswigger.net/customer/portal/articles/1965930-how-to-install-an-extension-in-burp-suite). 20 | 2. `git clone git@github.com:Regala/burp-scope-monitor.git` 21 | 3. Import [main.py](main.py) in Extender - Extender -> Extensions -> Add -> Select Python -> Select [main.py](main.py) 22 | 23 | ## Documentation 24 | 25 | Most of the options available in General or Import tabs are auto-explanatory. 26 | 27 | - *"Repeater request automatically marks as analyzed"* - when issuing a request to an endpoint from repeater, it marks this request as analyzed automatically. 28 | - *"Color request in Proxy tab"* - this essentially applies the behavior of the extension in the Proxy tab, if you combine these options with "Show only highlighted items" in Proxy. However, it's not as pleasant to the eyes as the color pallete is limited. 29 | - *"Autosave periodically"* - backups the state file every 10 minutes. When activating this option, consider disabling *"Autostart Scope Monitor"*. This is in order to maintain a different state file per Burp project. However, you can easily maintain only one, master state file. 30 | - *"Import/Export"* is dedicated to handle the saved state files. It's preferred to open your Burp project file associated with the Scope Monitor. It will still work if the Burp project is different, but when loading the saved entries, you won't be able to send them to Repeater or view the request itself in the Request/Response viewer (this is due to the fact that we are not storing the actually requests - just the endpoint, it's analyzed status and a couple of other more. This makes it a little bit more efficient). 31 | 32 | ## Future Development 33 | 34 | - Keep track of parameters observed in all requests 35 | - Highlight when a new parameter was used in an already observed/analyzed endpoint 36 | - Export to spreadsheet / Google Sheets 37 | - Adding notes to the endpoint 38 | 39 | ## Implementation 40 | 41 | The code is not yet performant, optimized or anything similar. KISS and it works. Performance will be increased depending on demand and how the extension performs when handling large Burp projects. 42 | 43 | To circumvent some of Burp's Extender API limitations, some small hacks were implemented. One of those is automatically setting a comment on the requests that flow in the Proxy tab. You can still add comments on the items, as you'd normally would, but just make sure to keep the placeholder string (`scope-monitor-placeholder`) there. Hopefully in the future each requestResponse from Burp will have a unique identifier, which would make the import state / load from file much cleaner and fast. With large state files, this might hang a bit when loading. 44 | 45 | ## Contributing 46 | 47 | I welcome contributions from the public, from bug reports, feature suggestions and pull requests. 48 | 49 | ### Using the issue tracker 💡 50 | 51 | The issue tracker is the preferred channel for bug reports and features requests. 52 | 53 | ### Issues and labels 🏷 54 | 55 | The bug tracker utilizes several labels to help organize and identify issues. 56 | 57 | ### Guidelines for bug reports 🐛 58 | 59 | Use the GitHub issue search — check if the issue has already been reported. 60 | 61 | ### Known bugs: 62 | 63 | - Sometimes when switching from "Show All" to "Show New Only" Burp hangs/crashes. If you encounter this behavior please let me know how you reproduce it. 64 | - Manually marking requests as analyzed from the main extension UI tab doesn't apply colors in the proxy 65 | - The import/export function often makes Burp freeze (it unfreezes after a while) so this needs a review, probably has something to do with the locks 66 | 67 | ## Special Thanks 68 | 69 | - BlazeIt team 70 | - BBAC 71 | - HackerOne 72 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from burp import IBurpExtender 3 | from burp import ITab 4 | from burp import IHttpListener 5 | from burp import IMessageEditorController 6 | from burp import IContextMenuFactory 7 | from burp import IExtensionStateListener 8 | from java.awt import Component; 9 | from java.io import PrintWriter; 10 | from java.util import ArrayList; 11 | from java.util import LinkedList; 12 | from java.util import List; 13 | from javax.swing import JScrollPane; 14 | from javax.swing import JSplitPane; 15 | from javax.swing import JTabbedPane; 16 | from javax.swing import JTable; 17 | from javax.swing import JFrame; 18 | from javax.swing import JFileChooser; 19 | from javax.swing import SwingUtilities; 20 | from javax.swing.table import AbstractTableModel; 21 | from javax.swing.table import DefaultTableCellRenderer; 22 | from javax.swing.table import DefaultTableModel; 23 | from javax.swing.table import TableColumn; 24 | from javax.swing.table import TableColumnModel; 25 | from threading import Lock 26 | 27 | ### 28 | from java.awt import Color 29 | from java.awt.event import MouseAdapter 30 | from javax.swing import JMenuItem 31 | from javax.swing import JPopupMenu 32 | from javax.swing import ListSelectionModel 33 | from java.awt.event import ActionListener 34 | #from java.awt.event import ListSelectionListener 35 | from java.awt import BorderLayout 36 | from java.awt import GridLayout 37 | from javax.swing import JTextArea 38 | from javax.swing import ButtonGroup 39 | from javax.swing import JRadioButton 40 | from javax.swing import JLabel 41 | from javax.swing import JButton 42 | from javax.swing import JComboBox 43 | from javax.swing import JCheckBox 44 | from javax.swing import JPanel 45 | from javax.swing import SortOrder 46 | from java.lang import Runnable 47 | from javax.swing import RowFilter 48 | from java.awt.event import ItemListener 49 | from javax.swing.table import TableRowSorter 50 | from urlparse import * 51 | import datetime 52 | import time 53 | import sched 54 | 55 | RED_COLOR = Color(255,135,135) 56 | GREEN_COLOR = Color(107,255,127) 57 | 58 | SHOW_ALL_BUTTON_LABEL = "Show All" 59 | SHOW_NEW_BUTTON_LABEL = "Show New Only" 60 | SHOW_TEST_BUTTON_LABEL = "Show Tested Only" 61 | 62 | MONITOR_ON_LABEL = "Monitor is ON" 63 | MONITOR_OFF_LABEL = "Monitor is OFF" 64 | 65 | SCOPE_MONITOR_COMMENT = "scope-monitor-placeholder" 66 | 67 | class BurpExtender(IBurpExtender, ITab, IHttpListener, IMessageEditorController, AbstractTableModel, IContextMenuFactory, IExtensionStateListener): 68 | 69 | # 70 | # implement IBurpExtender 71 | # 72 | 73 | def registerExtenderCallbacks(self, callbacks): 74 | # keep a reference to our callbacks object 75 | self._callbacks = callbacks 76 | 77 | # obtain an extension helpers object 78 | self._helpers = callbacks.getHelpers() 79 | 80 | # set our extension name 81 | callbacks.setExtensionName("Burp Scope Monitor") 82 | 83 | self.GLOBAL_HANDLER_ANALYZED = False 84 | self.GLOBAL_HANDLER = False 85 | self.STATUS = False 86 | self.AUTOSAVE_REQUESTS = 10 87 | self.AUTOSAVE_TIMEOUT = 600 # 10 minutes should be fine 88 | self.CONFIG_INSCOPE = True 89 | 90 | self.BAD_EXTENSIONS_DEFAULT = ['.gif', '.png', '.js', '.woff', '.woff2', '.jpeg', '.jpg', '.css', '.ico', '.m3u8', '.ts'] 91 | self.BAD_MIMES_DEFAULT = ['gif', 'script', 'jpeg', 'jpg', 'png', 'video', 'mp2t'] 92 | 93 | self.BAD_EXTENSIONS = self.BAD_EXTENSIONS_DEFAULT 94 | self.BAD_MIMES = self.BAD_MIMES_DEFAULT 95 | 96 | # create the log and a lock on which to synchronize when adding log entries 97 | 98 | self._currentlyDisplayedItem = None 99 | 100 | self.SELECTED_MODEL_ROW = 0 101 | self.SELECTED_VIEW_ROW = 0 102 | 103 | self._log = ArrayList() 104 | self._fullLog = ArrayList() 105 | self._lock = Lock() 106 | self._lockFile = Lock() 107 | 108 | # main split pane 109 | self._parentPane = JTabbedPane() 110 | 111 | self._splitpane = JSplitPane(JSplitPane.VERTICAL_SPLIT) 112 | 113 | ##### config pane 114 | self._config = JTabbedPane() 115 | 116 | config = JPanel() 117 | iexport = JPanel() 118 | 119 | #config.setLayout(BorderLayout()) 120 | config.setLayout(None) 121 | iexport.setLayout(None) 122 | 123 | # config radio button 124 | X_BASE = 40 125 | Y_OFFSET = 5 126 | Y_OPTION = 200 127 | Y_OPTION_SPACING = 20 128 | Y_CHECKMARK_SPACING = 20 129 | 130 | 131 | self.showAllButton = JRadioButton(SHOW_ALL_BUTTON_LABEL, True) 132 | self.showNewButton = JRadioButton(SHOW_NEW_BUTTON_LABEL, False) 133 | self.showTestedButton = JRadioButton(SHOW_TEST_BUTTON_LABEL, False) 134 | 135 | self.showAllButton.setBounds(40, 60 + Y_OFFSET, 400, 30) 136 | self.showNewButton.setBounds(40, 80 + Y_OFFSET, 400, 30) 137 | self.showTestedButton.setBounds(40, 100 + Y_OFFSET, 400, 30) 138 | #self.showNewButton = JRadioButton(SHOW_NEW_BUTTON_LABEL, False) 139 | #self.showTestedButton = JRadioButton(SHOW_TEST_BUTTON_LABEL, False) 140 | 141 | self.showAllButton.addActionListener(self.handleRadioConfig) 142 | self.showNewButton.addActionListener(self.handleRadioConfig) 143 | self.showTestedButton.addActionListener(self.handleRadioConfig) 144 | 145 | self.clearButton = JButton("Clear") 146 | self.clearButton.addActionListener(self.handleClearButton) 147 | self.clearButton.setBounds(40, 20, 100, 30) 148 | 149 | self.startButton = JButton(MONITOR_ON_LABEL) 150 | self.startButton.addActionListener(self.handleStartButton) 151 | self.startButton.setBounds(150, 20, 200, 30) 152 | 153 | self.badExtensionsLabel = JLabel("Ignore extensions:") 154 | self.badExtensionsLabel.setBounds(X_BASE, 150, 200, 30) 155 | 156 | self.badExtensionsText = JTextArea("") 157 | self.loadBadExtensions() 158 | self.badExtensionsText.setBounds(X_BASE, 175, 310, 30) 159 | 160 | self.badExtensionsButton = JButton("Save") 161 | self.badExtensionsButton.addActionListener(self.handleBadExtensionsButton) 162 | self.badExtensionsButton.setBounds(355, 175, 70, 30) 163 | 164 | self.badExtensionsDefaultButton = JButton("Load Defaults") 165 | self.badExtensionsDefaultButton.addActionListener(self.handleBadExtensionsDefaultButton) 166 | self.badExtensionsDefaultButton.setBounds(430, 175, 120, 30) 167 | 168 | self.badMimesLabel = JLabel("Ignore mime types:") 169 | self.badMimesLabel.setBounds(X_BASE, 220, 200, 30) 170 | 171 | self.badMimesText = JTextArea("") 172 | self.loadBadMimes() 173 | self.badMimesText.setBounds(X_BASE, 245, 310, 30) 174 | 175 | self.badMimesButton = JButton("Save") 176 | self.badMimesButton.addActionListener(self.handleBadMimesButton) 177 | self.badMimesButton.setBounds(355, 245, 70, 30) 178 | 179 | self.badMimesDefaultButton = JButton("Load Defaults") 180 | self.badMimesDefaultButton.addActionListener(self.handleBadMimesDefaultButton) 181 | self.badMimesDefaultButton.setBounds(430, 245, 120, 30) 182 | 183 | 184 | self.otherLabel = JLabel("Other:") 185 | self.otherLabel.setBounds(40, 300, 120, 30) 186 | 187 | self.otherLabel2 = JLabel("Other:") 188 | self.otherLabel2.setBounds(X_BASE, Y_OPTION, 120, 30) 189 | 190 | self.autoSaveOption = JCheckBox("Auto save periodically") 191 | self.autoSaveOption.setSelected(True) 192 | self.autoSaveOption.addActionListener(self.handleAutoSaveOption) 193 | self.autoSaveOption.setBounds(X_BASE, Y_OPTION + Y_CHECKMARK_SPACING, 420, 30) 194 | 195 | self.repeaterOptionButton = JCheckBox("Repeater request automatically marks as analyzed") 196 | self.repeaterOptionButton.setSelected(True) 197 | self.repeaterOptionButton.addActionListener(self.handleRepeaterOptionButton) 198 | self.repeaterOptionButton.setBounds(50, 330, 420, 30) 199 | 200 | self.scopeOptionButton = JCheckBox("Follow Burp Target In Scope rules") 201 | self.scopeOptionButton.setSelected(True) 202 | self.scopeOptionButton.addActionListener(self.handleScopeOptionButton) 203 | self.scopeOptionButton.setBounds(50, 350, 420, 30) 204 | 205 | self.startOptionButton = JCheckBox("Autostart Scope Monitor") 206 | self.startOptionButton.setSelected(True) 207 | self.startOptionButton.addActionListener(self.handleStartOption) 208 | self.startOptionButton.setBounds(50, 350 + Y_OPTION_SPACING, 420, 30) 209 | 210 | self.markTestedRequestsProxy = JCheckBox("Color request in Proxy tab if analyzed") 211 | self.markTestedRequestsProxy.setSelected(True) 212 | self.markTestedRequestsProxy.addActionListener(self.handleTestedRequestsProxy) 213 | self.markTestedRequestsProxy.setBounds(50, 350 + Y_OPTION_SPACING*2, 420, 30) 214 | 215 | self.markNotTestedRequestsProxy = JCheckBox("Color request in Proxy tab if NOT analyzed") 216 | self.markNotTestedRequestsProxy.setSelected(True) 217 | self.markNotTestedRequestsProxy.addActionListener(self.handleNotTestedRequestsProxy) 218 | self.markNotTestedRequestsProxy.setBounds(50, 350 + Y_OPTION_SPACING*3, 420, 30) 219 | 220 | 221 | self.saveButton = JButton("Save now") 222 | self.saveButton.addActionListener(self.handleSaveButton) 223 | self.saveButton.setBounds(X_BASE + 320, 95, 90, 30) 224 | 225 | self.loadButton = JButton("Load now") 226 | self.loadButton.addActionListener(self.handleLoadButton) 227 | self.loadButton.setBounds(X_BASE + 420, 95, 90, 30) 228 | 229 | self.selectPath = JButton("Select path") 230 | self.selectPath.addActionListener(self.selectExportFile) 231 | self.selectPath.setBounds(X_BASE + 530, 60, 120, 30) 232 | 233 | self.selectPathText = JTextArea("") 234 | self.selectPathText.setBounds(X_BASE, 60, 510, 30) 235 | 236 | self.selectPathLabel = JLabel("State file:") 237 | self.selectPathLabel.setBounds(X_BASE, 30, 200, 30) 238 | 239 | bGroup = ButtonGroup() 240 | 241 | bGroup.add(self.showAllButton) 242 | bGroup.add(self.showNewButton) 243 | bGroup.add(self.showTestedButton) 244 | 245 | config.add(self.clearButton) 246 | config.add(self.startButton) 247 | config.add(self.startOptionButton) 248 | config.add(self.showAllButton) 249 | config.add(self.showNewButton) 250 | config.add(self.showTestedButton) 251 | 252 | config.add(self.badExtensionsButton) 253 | config.add(self.badExtensionsText) 254 | config.add(self.badExtensionsLabel) 255 | 256 | config.add(self.badMimesButton) 257 | config.add(self.badMimesText) 258 | config.add(self.badMimesLabel) 259 | 260 | config.add(self.badExtensionsDefaultButton) 261 | config.add(self.badMimesDefaultButton) 262 | 263 | config.add(self.otherLabel) 264 | config.add(self.repeaterOptionButton) 265 | config.add(self.scopeOptionButton) 266 | config.add(self.markTestedRequestsProxy) 267 | config.add(self.markNotTestedRequestsProxy) 268 | 269 | iexport.add(self.saveButton) 270 | iexport.add(self.loadButton) 271 | iexport.add(self.selectPath) 272 | iexport.add(self.selectPathText) 273 | iexport.add(self.selectPathLabel) 274 | iexport.add(self.otherLabel2) 275 | iexport.add(self.autoSaveOption) 276 | 277 | 278 | self._config.addTab("General", config) 279 | self._config.addTab("Import/Export", iexport) 280 | 281 | ##### end config pane 282 | 283 | 284 | self._parentPane.addTab("Monitor", self._splitpane) 285 | self._parentPane.addTab("Config", self._config) 286 | 287 | # table of log entries 288 | self.logTable = Table(self) 289 | 290 | #self.logTable.setDefaultRenderer(self.logTable.getColumnClass(0), ColoredTableCellRenderer(self)) 291 | 292 | self.logTable.setAutoCreateRowSorter(True) 293 | self.logTable.setRowSelectionAllowed(True) 294 | 295 | renderer = ColoredTableCellRenderer(self) 296 | #column = TableColumn(0, 190, renderer, None) 297 | 298 | print 'Initiating... ' 299 | 300 | # this could be improved by fetching initial dimensions 301 | self.logTable.getColumn("URL").setPreferredWidth(720) # noscope 302 | self.logTable.getColumn("URL").setResizable(True) 303 | 304 | self.logTable.getColumn("Checked").setCellRenderer(renderer) 305 | self.logTable.getColumn("Checked").setPreferredWidth(80) 306 | self.logTable.getColumn("Checked").setMaxWidth(80) 307 | 308 | self.logTable.getColumn("Method").setPreferredWidth(120) 309 | #self.logTable.getColumn("Method").setMaxWidth(120) 310 | self.logTable.getColumn("Method").setResizable(True) 311 | 312 | self.logTable.getColumn("Time").setPreferredWidth(120) # noscope 313 | self.logTable.getColumn("Time").setResizable(True) 314 | 315 | 316 | scrollPane = JScrollPane(self.logTable) 317 | self._splitpane.setLeftComponent(scrollPane) 318 | 319 | # tabs with request/response viewers 320 | tabs = JTabbedPane() 321 | self._requestViewer = callbacks.createMessageEditor(self, False) 322 | self._responseViewer = callbacks.createMessageEditor(self, False) 323 | tabs.addTab("Request", self._requestViewer.getComponent()) 324 | tabs.addTab("Response", self._responseViewer.getComponent()) 325 | self._splitpane.setRightComponent(tabs) 326 | 327 | ## Row sorter shit 328 | 329 | #self._tableRowSorterAutoProxyAutoAction = CustomTableRowSorter(self.logTable.getModel()) 330 | #self.logTable.setRowSorter(self._tableRowSorterAutoProxyAutoAction) 331 | 332 | 333 | markAnalyzedButton = JMenuItem("Mark Requests as Analyzed") 334 | markAnalyzedButton.addActionListener(markRequestsHandler(self, True)) 335 | 336 | markNotAnalyzedButton = JMenuItem("Mark Requests as NOT Analyzed") 337 | markNotAnalyzedButton.addActionListener(markRequestsHandler(self, False)) 338 | 339 | sendRequestMenu = JMenuItem("Send Request to Repeater") 340 | sendRequestMenu.addActionListener(sendRequestRepeater(self)) 341 | 342 | deleteRequestMenu = JMenuItem("Delete request") 343 | deleteRequestMenu.addActionListener(deleteRequestHandler(self)) 344 | 345 | self.menu = JPopupMenu("Popup") 346 | self.menu.add(markAnalyzedButton) 347 | self.menu.add(markNotAnalyzedButton) 348 | self.menu.add(sendRequestMenu) 349 | self.menu.add(deleteRequestMenu) 350 | 351 | # customize our UI components 352 | callbacks.customizeUiComponent(self._parentPane) 353 | callbacks.customizeUiComponent(self._splitpane) 354 | callbacks.customizeUiComponent(self._config) 355 | callbacks.customizeUiComponent(config) 356 | callbacks.customizeUiComponent(self.logTable) 357 | callbacks.customizeUiComponent(scrollPane) 358 | callbacks.customizeUiComponent(tabs) 359 | 360 | callbacks.registerContextMenuFactory(self) 361 | callbacks.registerExtensionStateListener(self) 362 | 363 | # add the custom tab to Burp's UI 364 | callbacks.addSuiteTab(self) 365 | 366 | # register ourselves as an HTTP listener 367 | callbacks.registerHttpListener(self) 368 | 369 | self.loadConfigs() 370 | 371 | print "Loaded!" 372 | self.SC = sched.scheduler(time.time, time.sleep) 373 | self.SCC = self.SC.enter(10, 1, self.autoSave, (self.SC,)) 374 | self.SC.run() 375 | 376 | return 377 | 378 | ##### CUSTOM CODE ##### 379 | def loadConfigs(self): 380 | 381 | if self._callbacks.loadExtensionSetting("CONFIG_AUTOSTART") == "True": 382 | self.startOptionButton.setSelected(True) 383 | self.startOrStop(None, True) 384 | else: 385 | self.startOptionButton.setSelected(False) 386 | self.startOrStop(None, False) 387 | 388 | if self._callbacks.loadExtensionSetting("exportFile") != "": 389 | self.selectPathText.setText(self._callbacks.loadExtensionSetting("exportFile")) 390 | 391 | if self._callbacks.loadExtensionSetting("CONFIG_REPEATER") == "True": 392 | self.repeaterOptionButton.setSelected(True) 393 | else: 394 | self.repeaterOptionButton.setSelected(False) 395 | 396 | if self._callbacks.loadExtensionSetting("CONFIG_INSCOPE") == "True": 397 | self.scopeOptionButton.setSelected(True) 398 | else: 399 | self.scopeOptionButton.setSelected(False) 400 | 401 | if self._callbacks.loadExtensionSetting("CONFIG_AUTOSAVE") == "True": 402 | self.autoSaveOption.setSelected(True) 403 | else: 404 | self.autoSaveOption.setSelected(False) 405 | 406 | if self._callbacks.loadExtensionSetting("CONFIG_HIGHLIGHT_TESTED") == "True": 407 | self.markTestedRequestsProxy.setSelected(True) 408 | else: 409 | self.markTestedRequestsProxy.setSelected(False) 410 | 411 | if self._callbacks.loadExtensionSetting("CONFIG_HIGHLIGHT_NOT_TESTED") == "True": 412 | self.markNotTestedRequestsProxy.setSelected(True) 413 | else: 414 | self.markNotTestedRequestsProxy.setSelected(False) 415 | 416 | 417 | 418 | 419 | 420 | return 421 | 422 | def selectExportFile(self, event): 423 | parentFrame = JFrame() 424 | fileChooser = JFileChooser() 425 | fileChooser.setDialogTitle("Specify file to save state") 426 | fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY) 427 | 428 | userSelection = fileChooser.showOpenDialog(parentFrame) 429 | 430 | if (userSelection == JFileChooser.APPROVE_OPTION): 431 | fileLoad = fileChooser.getSelectedFile() 432 | filename = fileLoad.getAbsolutePath() 433 | 434 | self.selectPathText.setText(filename) 435 | print 'Filename selected:' + filename 436 | self._callbacks.saveExtensionSetting("exportFile", filename) 437 | 438 | return 439 | 440 | 441 | def extensionUnloaded(self): 442 | print 'extension unloading.. ' 443 | 444 | print 'canceling scheduler.. ' 445 | map(self.SC.cancel, self.SC.queue) 446 | return 447 | 448 | def loadBadExtensions(self): 449 | bad = self._callbacks.loadExtensionSetting("badExtensions") 450 | if bad: 451 | self.badExtensionsText.setText(bad) 452 | # transform text to array 453 | bad = bad.replace(" ", "") 454 | self.BAD_EXTENSIONS = bad.split(",") 455 | else: 456 | print 'no bad extension saved, reverting' 457 | self.badExtensionsText.setText(", ".join(self.BAD_EXTENSIONS)) 458 | 459 | def loadBadMimes(self): 460 | bad = self._callbacks.loadExtensionSetting("badMimes") 461 | if bad: 462 | self.badMimesText.setText(bad) 463 | 464 | bad = bad.replace(" ", "") 465 | self.BAD_MIMES = bad.split(",") 466 | else: 467 | print 'no bad mimes saved, reverting' 468 | self.badMimesText.setText(", ".join(self.BAD_MIMES)) 469 | 470 | 471 | 472 | 473 | 474 | ## GLOBAL CONTEXT CODE ## 475 | 476 | def createMenuItems(self, invocation): 477 | responses = invocation.getSelectedMessages() 478 | if responses > 0: 479 | ret = LinkedList() 480 | analyzedMenuItem = JMenuItem("Mark as analyzed") 481 | notAnalyzedMenuItem = JMenuItem("Mark as NOT analyzed") 482 | 483 | for response in responses: 484 | analyzedMenuItem.addActionListener(handleMenuItems(self,response, "analyzed")) 485 | notAnalyzedMenuItem.addActionListener(handleMenuItems(self, response, "not")) 486 | ret.add(analyzedMenuItem) 487 | ret.add(notAnalyzedMenuItem) 488 | return ret 489 | 490 | 491 | def getEndpoint(self, requestResponse): 492 | url_ = str(self._helpers.analyzeRequest(requestResponse).getUrl()) 493 | o = urlparse(url_) 494 | 495 | url = o.scheme+"://"+o.netloc+o.path 496 | #print "Url3: " + url 497 | return url 498 | 499 | 500 | def getMethod(self, requestResponse): 501 | return self._helpers.analyzeRequest(requestResponse).getMethod() 502 | 503 | 504 | ##### CUSTOM CODE ##### 505 | def handleTestedRequestsProxy(self, event): 506 | self._callbacks.saveExtensionSetting("CONFIG_HIGHLIGHT_TESTED", str(self.markTestedRequestsProxy.isSelected())) 507 | return 508 | 509 | def handleNotTestedRequestsProxy(self, event): 510 | self._callbacks.saveExtensionSetting("CONFIG_HIGHLIGHT_NOT_TESTED", str(self.markNotTestedRequestsProxy.isSelected())) 511 | return 512 | 513 | 514 | 515 | def handleStartOption(self, event): 516 | self._callbacks.saveExtensionSetting("CONFIG_AUTOSTART", str(self.startOptionButton.isSelected())) 517 | #print 'saving autostart: ' + str(self.startOptionButton.isSelected()) 518 | return 519 | 520 | def startOrStop(self, event, autoStart): 521 | if (self.startButton.getText() == MONITOR_OFF_LABEL) or autoStart: 522 | self.startButton.setText(MONITOR_ON_LABEL) 523 | self.startButton.setBackground(GREEN_COLOR) 524 | self.STATUS = True 525 | else: 526 | self.startButton.setText(MONITOR_OFF_LABEL) 527 | self.startButton.setBackground(RED_COLOR) 528 | self.STATUS = False 529 | 530 | def handleStartButton(self, event): 531 | self.startOrStop(event, False) 532 | 533 | def handleAutoSaveOption(self, event): 534 | self._callbacks.saveExtensionSetting("CONFIG_AUTOSAVE", str(self.autoSaveOption.isSelected())) 535 | return 536 | 537 | def handleSaveButton(self, event): 538 | self.exportState("test") 539 | 540 | def handleLoadButton(self, event): 541 | self.importState("test") 542 | 543 | def handleRepeaterOptionButton(self, event): 544 | self._callbacks.saveExtensionSetting("CONFIG_REPEATER", str(self.repeaterOptionButton.isSelected())) 545 | return 546 | 547 | def handleScopeOptionButton(self, event): 548 | self.CONFIG_INSCOPE = self.scopeOptionButton.isSelected() 549 | self._callbacks.saveExtensionSetting("CONFIG_INSCOPE", str(self.CONFIG_INSCOPE)) 550 | return 551 | 552 | def handleBadExtensionsButton(self, event): 553 | #print "before BAD array: " 554 | print self.BAD_EXTENSIONS 555 | 556 | extensions = self.badExtensionsText.getText() 557 | self._callbacks.saveExtensionSetting("badExtensions", extensions) 558 | print 'New extensions blocked: ' + extensions 559 | bad = extensions.replace(" ", "") 560 | self.BAD_EXTENSIONS = bad.split(",") 561 | #print "BAD array: " 562 | #print self.BAD_EXTENSIONS 563 | 564 | def handleBadExtensionsDefaultButton(self, event): 565 | self.BAD_EXTENSIONS = self.BAD_EXTENSIONS_DEFAULT 566 | self.badExtensionsText.setText(", ".join(self.BAD_EXTENSIONS)) 567 | self._callbacks.saveExtensionSetting("badExtensions", ", ".join(self.BAD_EXTENSIONS)) 568 | return 569 | 570 | def handleBadMimesDefaultButton(self, event): 571 | self.BAD_MIMES = self.BAD_MIMES_DEFAULT 572 | self.badMimesText.setText(", ".join(self.BAD_MIMES)) 573 | self._callbacks.saveExtensionSetting("badExtensions", ", ".join(self.BAD_MIMES)) 574 | return 575 | 576 | def handleBadMimesButton(self, event): 577 | mimes = self.badMimesText.getText() 578 | self._callbacks.saveExtensionSetting("badMimes", mimes) 579 | print 'New mimes blocked: ' + mimes 580 | bad = mimes.replace(" ", "") 581 | self.BAD_MIMES = bad.split(",") 582 | 583 | def handleClearButton(self, event): 584 | print 'Clearing table' 585 | self._lock.acquire() 586 | self._log = ArrayList() 587 | self._fullLog = ArrayList() 588 | self._lock.release() 589 | return 590 | 591 | def handleRadioConfig(self, event): 592 | #print ' radio button clicked ' 593 | #print event.getActionCommand() 594 | self._lock.acquire() 595 | 596 | if event.getActionCommand() == SHOW_ALL_BUTTON_LABEL: 597 | print "Showing all" 598 | self._log = self._fullLog 599 | elif event.getActionCommand() == SHOW_NEW_BUTTON_LABEL: 600 | print "Showing new scope only" 601 | tmpLog = ArrayList() 602 | for item in self._fullLog: 603 | if not(item._analyzed): 604 | tmpLog.add(item) 605 | self._log = tmpLog 606 | elif event.getActionCommand() == SHOW_TEST_BUTTON_LABEL: 607 | print "Showing tested scope only" 608 | tmpLog = ArrayList() 609 | for item in self._fullLog: 610 | if item._analyzed: 611 | tmpLog.add(item) 612 | self._log = tmpLog 613 | else: 614 | print "unrecognized radio label" 615 | 616 | 617 | self.fireTableDataChanged() 618 | #self._tableRowSorterAutoProxyAutoAction.toggleSortOrder(1) 619 | #self.toggleSortOrder(2) 620 | 621 | #self.logTable.toggleSortOrder(2) 622 | 623 | # refresh table? 624 | 625 | self._lock.release() 626 | 627 | 628 | # 629 | # implement ITab 630 | # 631 | 632 | def getTabCaption(self): 633 | return "Scope Monitor" 634 | 635 | def getUiComponent(self): 636 | return self._parentPane 637 | 638 | # 639 | # implement IHttpListener 640 | # 641 | 642 | def markAnalyzed(self, messageIsRequest, state): 643 | #print "markAnalyzed..." 644 | self._lock.acquire() 645 | 646 | url = self.getEndpoint(messageIsRequest) 647 | for item in self._log: 648 | if url == item._url: 649 | item._analyzed = state 650 | self._lock.release() 651 | return 652 | self._lock.release() 653 | return 654 | 655 | def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): 656 | # only process requests 657 | 658 | #print "processing httpMessage.." 659 | #print messageIsRequest 660 | #print "processHttp status: " + str(self.STATUS) 661 | if not(self.STATUS): 662 | return 663 | 664 | #print "global handler status: (true): " + str(self.GLOBAL_HANDLER) 665 | #print "(processHTTP) messageIsRequest" 666 | #print messageIsRequest 667 | if messageIsRequest and not(self.GLOBAL_HANDLER): 668 | return 669 | 670 | if self.scopeOptionButton.isSelected(): 671 | url = self._helpers.analyzeRequest(messageInfo).getUrl() 672 | if not self._callbacks.isInScope(url): 673 | #print 'Url not in scope, skipping.. ' 674 | return 675 | 676 | #print "still processing httpMessage.., request came from: " + self._callbacks.getToolName(toolFlag) 677 | 678 | if ((self._callbacks.getToolName(toolFlag) != "Repeater") and (self._callbacks.getToolName(toolFlag) != "Proxy") and (self._callbacks.getToolName(toolFlag) != "Target")): 679 | #print 'Aborting processHTTP, request came from: ' + str(self._callbacks.getToolName(toolFlag)) 680 | return 681 | 682 | 683 | url = self.getEndpoint(messageInfo) 684 | method = self.getMethod(messageInfo) 685 | 686 | #print "(processHTTP) before extensions check: " + url 687 | 688 | for extension in self.BAD_EXTENSIONS: 689 | if url.endswith(extension): 690 | return 691 | 692 | if messageInfo.getResponse(): 693 | mime = self._helpers.analyzeResponse(messageInfo.getResponse()).getStatedMimeType() 694 | #print 'Declared mime:' + mime 695 | mime = mime.lower() 696 | if mime in self.BAD_MIMES: 697 | #print 'Bad mime:' + mime 698 | return 699 | 700 | #print "[httpMessage] before lock" 701 | # create a new log entry with the message details 702 | self._lock.acquire() 703 | row = self._log.size() 704 | 705 | 706 | for item in self._log: 707 | if url == item._url: 708 | if method == self._helpers.analyzeRequest(item._requestResponse).getMethod(): 709 | #print 'duplicate URL+method, skipping.. ' 710 | self._lock.release() 711 | 712 | # has it been analyzed? 713 | analyzed = False 714 | if self._callbacks.getToolName(toolFlag) == "Repeater": 715 | if self.repeaterOptionButton.isSelected(): 716 | analyzed = True 717 | #print "[httpMessage] setting analyzed as true" 718 | if self.GLOBAL_HANDLER_ANALYZED: 719 | analyzed = True 720 | 721 | item._analyzed = analyzed 722 | self.paintItems(messageInfo, item) 723 | 724 | return 725 | 726 | #print "[httpMessage] before setComment" 727 | messageInfo.setComment(SCOPE_MONITOR_COMMENT) 728 | # reached here, must be new entry 729 | analyzed = False 730 | if self._callbacks.getToolName(toolFlag) == "Repeater": 731 | if self.repeaterOptionButton.isSelected(): 732 | analyzed = True 733 | #print "[httpMessage] setting analyzed as true" 734 | if self.GLOBAL_HANDLER_ANALYZED: 735 | analyzed = True 736 | 737 | #print "[httpMessage] after comment" 738 | #print 'in httpmessage, response:' 739 | #print self._helpers.analyzeResponse(messageInfo.getResponse()) 740 | 741 | date = datetime.datetime.fromtimestamp(time.time()).strftime('%H:%M:%S %d %b %Y') 742 | entry = LogEntry(toolFlag, self._callbacks.saveBuffersToTempFiles(messageInfo), url, analyzed, date, method) 743 | #print "toolFlag: " + str(toolFlag) 744 | 745 | #print "(processHTTP) Adding URL: " + url 746 | self._log.add(entry) 747 | self._fullLog.add(entry) 748 | self.fireTableRowsInserted(row, row) 749 | 750 | self.paintItems(messageInfo, entry) 751 | 752 | self._lock.release() 753 | 754 | #print "columnCoun:" + str(self.logTable.getColumnCount()) 755 | 756 | # 757 | # extend AbstractTableModel 758 | # 759 | 760 | def paintItems(self, messageInfo, item): 761 | ''' 762 | print "in paint Items" 763 | print "mark color is: (true)" + str(self.markTestedRequestsProxy.isSelected()) 764 | print "global handler analyzed: :" + str(self.GLOBAL_HANDLER_ANALYZED) 765 | print "item analyzed should be the same ^^:" + str(item._analyzed) 766 | ''' 767 | if (self.markTestedRequestsProxy.isSelected()) and (item._analyzed and self.GLOBAL_HANDLER_ANALYZED): 768 | messageInfo.setHighlight("green") 769 | return 770 | 771 | if self.markNotTestedRequestsProxy.isSelected() and not(item._analyzed): 772 | messageInfo.setHighlight("red") 773 | 774 | 775 | def getRowCount(self): 776 | try: 777 | return self._log.size() 778 | except: 779 | return 0 780 | 781 | def getColumnCount(self): 782 | return 4 783 | 784 | def getColumnName(self, columnIndex): 785 | if columnIndex == 0: 786 | return "Checked" 787 | if columnIndex == 1: 788 | return "URL" 789 | if columnIndex == 2: 790 | return "Method" 791 | if columnIndex == 3: 792 | return "Time" 793 | 794 | def getValueAt(self, rowIndex, columnIndex): 795 | logEntry = self._log.get(rowIndex) 796 | 797 | #self.setBackground(Color.GREEN) 798 | return self.returnEntry(rowIndex, columnIndex, logEntry) 799 | 800 | if self.showNewButton.isSelected() and not(logEntry._analyzed): 801 | return self.returnEntry(rowIndex, columnIndex, logEntry) 802 | elif self.showTestedButton.isSelected() and logEntry._analyzed: 803 | return self.returnEntry(rowIndex, columnIndex, logEntry) 804 | elif self.showAllButton.isSelected(): 805 | return self.returnEntry(rowIndex, columnIndex, logEntry) 806 | 807 | 808 | def returnEntry(self, rowIndex, columnIndex, entry): 809 | logEntry = self._log.get(rowIndex) 810 | if columnIndex == 0: 811 | if logEntry._analyzed: 812 | return "True" 813 | else: 814 | return "False" 815 | if columnIndex == 1: 816 | return self._helpers.urlDecode(logEntry._url) 817 | if columnIndex == 2: 818 | return logEntry._method 819 | if columnIndex == 3: 820 | return logEntry._date 821 | # return date 822 | return "" 823 | 824 | 825 | # 826 | # implement IMessageEditorController 827 | # this allows our request/response viewers to obtain details about the messages being displayed 828 | # 829 | 830 | 831 | def getHttpService(self): 832 | return self._currentlyDisplayedItem.getHttpService() 833 | 834 | def getRequest(self): 835 | #print 'getRequest called' 836 | return self._currentlyDisplayedItem.getRequest() 837 | 838 | def getResponse(self): 839 | #print 'getResponse called: ' 840 | print self._currentlyDisplayedItem.getResponse() 841 | return self._currentlyDisplayedItem.getResponse() 842 | 843 | 844 | def exportRequest(self, entity, filename): 845 | 846 | line = str(entity._analyzed) + "," 847 | line = line + self._helpers.urlEncode(entity._url).replace(",", "%2c") + "," # URL is encoded so we should be good 848 | line = line + entity._method + "," 849 | line = line + entity._date 850 | line = line + '\n' 851 | 852 | #print 'Exporting: "' + line + '"' 853 | return line 854 | 855 | def exportUrlEncode(self, url): 856 | return self._helpers.urlEncode(url).replace(",", "%2c") 857 | 858 | def exportState(self, filename): 859 | filename = self.selectPathText.getText() 860 | 861 | if filename == "": 862 | filename = self._callbacks.loadExtensionSetting("exportFile") 863 | print 'Empty filename, skipping export' 864 | return 865 | else: 866 | self._callbacks.saveExtensionSetting("exportFile", filename) 867 | 868 | print 'saving state to: ' + filename 869 | 870 | savedUrls = [] 871 | 872 | self._lockFile.acquire() 873 | try: 874 | with open(filename, 'r') as fr: 875 | savedEntries = fr.read().splitlines() 876 | savedUrls = [] 877 | for savedEntry in savedEntries: 878 | savedUrls.append(savedEntry.split(",")[1]) 879 | #print "savedUrls len: " + str(len(savedUrls)) 880 | #print "savedUrls:" 881 | #print savedUrls 882 | fr.close() 883 | except IOError: 884 | print "Autosaving skipped as file doesn't exist yet" 885 | 886 | with open(filename, 'a+') as f: 887 | 888 | for item in self._log: 889 | if self.exportUrlEncode(item._url) not in savedUrls: 890 | line = self.exportRequest(item, "xx") 891 | f.write(line) 892 | f.close() 893 | self._lockFile.release() 894 | 895 | return 896 | 897 | def importState(self, filename): 898 | filename = self.selectPathText.getText() 899 | 900 | if filename == "": 901 | filename = self._callbacks.loadExtensionSetting("exportFile") 902 | print 'Empty filename, skipping import' 903 | return 904 | else: 905 | self._callbacks.saveExtensionSetting("exportFile", filename) 906 | 907 | print 'loading state from: ' + filename 908 | 909 | self.STATUS = False 910 | 911 | self._lockFile.acquire() 912 | with open(filename, 'r') as f: 913 | 914 | proxy = self._callbacks.getProxyHistory() 915 | 916 | proxyItems = [] 917 | for item in proxy: 918 | if item.getComment(): 919 | if SCOPE_MONITOR_COMMENT in item.getComment(): 920 | proxyItems.append(item) 921 | 922 | #print 'proxyItems has: ' + str(len(proxyItems)) 923 | # TODO - if no proxy items, sraight to import 924 | 925 | lines = f.read().splitlines() 926 | for line in lines: 927 | data = line.split(",") 928 | url = data[1] 929 | url = self._helpers.urlDecode(url) 930 | 931 | #print 'Saving: ' + url 932 | 933 | analyzed = False 934 | if data[0] == "True": 935 | analyzed = True 936 | 937 | requestResponse = None 938 | for request in proxyItems: 939 | if url == self.getEndpoint(request): 940 | #print 'Match found when importing for url: ' + url 941 | requestResponse = request 942 | break 943 | 944 | self._log.add(LogEntry("", requestResponse, url, analyzed, data[3], data[2]) ) 945 | 946 | #(self, tool, requestResponse, url, analyzed, date, method): 947 | 948 | self._lockFile.release() 949 | print 'finished loading.. ' 950 | #print 'size: ' + str(self._log.size()) 951 | self.fireTableDataChanged() 952 | 953 | if self.startButton.getText() == MONITOR_ON_LABEL: 954 | self.STATUS = True 955 | 956 | return 957 | 958 | def autoSave(self, sc): 959 | #print 'autosaving.. lol what' 960 | if self.autoSaveOption.isSelected(): 961 | print "[" + self.getTime() + "] autosaving to " + self._callbacks.loadExtensionSetting("exportFile") 962 | self.exportState("") 963 | 964 | self.SC.enter(self.AUTOSAVE_TIMEOUT, 1, self.autoSave, (self.SC,)) 965 | return 966 | 967 | def getTime(self): 968 | date = datetime.datetime.fromtimestamp(time.time()).strftime('%H:%M:%S') 969 | return date 970 | 971 | # 972 | # extend JTable to handle cell selection 973 | # 974 | 975 | #def getRequest(self,): 976 | 977 | class Table(JTable): 978 | def __init__(self, extender): 979 | self._extender = extender 980 | self.setModel(extender) 981 | self.addMouseListener(mouseclick(self._extender)) 982 | self.setRowSelectionAllowed(True) 983 | self.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) 984 | 985 | def changeSelection(self, row, col, toggle, extend): 986 | 987 | # shows "entries" matching 988 | 989 | # show the log entry for the selected row 990 | #print 'Selecting entry ' + str(row) + ' in changeSelection: ' 991 | 992 | JTable.changeSelection(self, row, col, toggle, extend) 993 | 994 | modelRow = self.convertRowIndexToModel(row) 995 | #print 'converted: ' + str() 996 | 997 | logEntry = self._extender._log.get(modelRow) 998 | 999 | #print str(self._extender._helpers.analyzeRequest(logEntry._requestResponse).getUrl()) 1000 | 1001 | self._extender.SELECTED_MODEL_ROW = modelRow 1002 | self._extender.SELECTED_VIEW_ROW = row 1003 | 1004 | self._extender._currentlyDisplayedItem = logEntry._requestResponse 1005 | self._extender._requestViewer.setMessage(logEntry._requestResponse.getRequest(), True) 1006 | self._extender._responseViewer.setMessage(logEntry._requestResponse.getResponse(), False) 1007 | 1008 | 1009 | #JTable.changeSelection(self, row, col, toggle, extend) 1010 | return 1011 | 1012 | 1013 | class mouseclick(MouseAdapter): 1014 | def __init__(self, extender): 1015 | self._extender = extender 1016 | 1017 | def mouseReleased(self, evt): 1018 | if evt.button == 3: 1019 | self._extender.menu.show(evt.getComponent(), evt.getX(), evt.getY()) 1020 | 1021 | 1022 | class ColoredTableCellRenderer(DefaultTableCellRenderer): 1023 | def __init__(self, extender): 1024 | self._extender = extender 1025 | 1026 | def setValue(self, value): 1027 | if value == "False": 1028 | self.setBackground(Color(255,135,135)) 1029 | #self.setForeground(Color.RED) 1030 | elif value == "True": 1031 | self.setBackground(Color(107,255,127)) 1032 | #self.setForeground(Color.GREEN) 1033 | #return value 1034 | self.super__setValue(value) 1035 | 1036 | 1037 | # 1038 | # class to hold details of each log entry 1039 | # 1040 | 1041 | class LogEntry: 1042 | def __init__(self, tool, requestResponse, url, analyzed, date, method): 1043 | self._tool = tool 1044 | self._requestResponse = requestResponse 1045 | self._url = url 1046 | self._displayed = False 1047 | self._analyzed = analyzed 1048 | self._date = date 1049 | self._method = method 1050 | 1051 | class CustomTableRowSorter(TableRowSorter): 1052 | 1053 | # override toggleSortOrder method 1054 | def toggleSortOrder(self, column): 1055 | 1056 | # check if valid column 1057 | if column >= 0: 1058 | 1059 | # get the sort keys 1060 | keys = self.getSortKeys() 1061 | 1062 | # check if the sort keys are not empty 1063 | if keys.isEmpty() == False: 1064 | 1065 | # get the sort key 1066 | sortKey = keys.get(0) 1067 | 1068 | # check if the column clicked is sorted in descending order 1069 | if sortKey.getColumn() == column and sortKey.getSortOrder() == SortOrder.DESCENDING: 1070 | 1071 | # clear sorting 1072 | self.setSortKeys(None) 1073 | 1074 | # do not continue 1075 | return 1076 | 1077 | # try to toggle default toggleSortOrder 1078 | try: 1079 | # toggle default toggleSortOrder 1080 | TableRowSorter.toggleSortOrder(self, column) 1081 | 1082 | # catch if table is being sorted by processProxyMessage and user 1083 | except: 1084 | pass 1085 | 1086 | class deleteRequestHandler(ActionListener): 1087 | def __init__(self, extender): 1088 | self._extender = extender 1089 | 1090 | def actionPerformed(self, e): 1091 | #print "COPY SELECTED URL HANDLER ******" 1092 | 1093 | rows = self._extender.logTable.getSelectedRows() 1094 | to_delete = [] 1095 | 1096 | for row in rows: 1097 | 1098 | model_row = self._extender.logTable.convertRowIndexToModel(row) 1099 | 1100 | self._extender._log.remove(self._extender._log.get(model_row)) 1101 | 1102 | self._extender.fireTableDataChanged() 1103 | #print 'refreshing view ..... *****' 1104 | 1105 | return 1106 | 1107 | class sendRequestRepeater(ActionListener): 1108 | def __init__(self, extender): 1109 | self._extender = extender 1110 | 1111 | def actionPerformed(self, e): 1112 | #print "COPY SELECTED URL HANDLER ******" 1113 | 1114 | rows = self._extender.logTable.getSelectedRows() 1115 | for row in rows: 1116 | 1117 | model_row = self._extender.logTable.convertRowIndexToModel(row) 1118 | 1119 | request = self._extender._log.get(model_row)._requestResponse 1120 | url = self._extender._log.get(model_row)._url 1121 | 1122 | host = request.getHttpService().getHost() 1123 | port = request.getHttpService().getPort() 1124 | proto = request.getHttpService().getProtocol() 1125 | 1126 | secure = True if proto == 'https' else False 1127 | 1128 | self._extender._callbacks.sendToRepeater(host, port, secure, request.getRequest(), None); 1129 | 1130 | return 1131 | 1132 | ### LOCAL CONTEXT 1133 | class markRequestsHandler(ActionListener): 1134 | def __init__(self, extender, state): 1135 | self._extender = extender 1136 | self._state = state 1137 | 1138 | def actionPerformed(self, e): 1139 | #print "COPY SELECTED URL HANDLER ******" 1140 | #print "Status is: " + str(self._state) 1141 | 1142 | rows = self._extender.logTable.getSelectedRows() 1143 | for row in rows: 1144 | 1145 | model_row = self._extender.logTable.convertRowIndexToModel(row) 1146 | url = self._extender._log.get(model_row)._url 1147 | 1148 | #print "Changing url: " + url 1149 | 1150 | ### TODO REPLACE FOR MARK_AS_ANALYZED 1151 | self._extender._lock.acquire() 1152 | 1153 | for item in self._extender._log: 1154 | if url == item._url: 1155 | item._analyzed = self._state 1156 | self._extender.paintItems(item._requestResponse, item) 1157 | #break 1158 | self._extender._lock.release() 1159 | 1160 | 1161 | self._extender.fireTableDataChanged() 1162 | #print 'refreshing view ..... *****' 1163 | 1164 | #self._extender.changeSelection(self._extender.SELECTED_VIEW_ROW, 1, True, True) 1165 | #self._extender.changeSelection(self._extender.SELECTED_VIEW_ROW, 1, False, False) 1166 | #self._extender.changeSelection(self._extender.SELECTED_VIEW_ROW, 1, True, True) 1167 | 1168 | return 1169 | 1170 | 1171 | ### GLOBAL CONTEXT #### 1172 | class handleMenuItems(ActionListener): 1173 | def __init__(self, extender, messageInfo, menuName): 1174 | self._extender = extender 1175 | self._menuName = menuName 1176 | self._messageInfo = messageInfo 1177 | 1178 | def actionPerformed(self, e): 1179 | self._extender.GLOBAL_HANDLER = True 1180 | 1181 | if self._menuName == "analyzed": 1182 | self._extender.GLOBAL_HANDLER_ANALYZED = True 1183 | self._extender.processHttpMessage(4, self._messageInfo, self._messageInfo) 1184 | self._extender.markAnalyzed(self._messageInfo, True) 1185 | #start_new_thread(self._extender.sendRequestToAutorizeWork,(self._messageInfo,)) 1186 | 1187 | if self._menuName == "not": 1188 | self._extender.GLOBAL_HANDLER_ANALYZED = False 1189 | self._extender.processHttpMessage(4, self._messageInfo, self._messageInfo) 1190 | self._extender.markAnalyzed(self._messageInfo, False) 1191 | 1192 | self._extender.GLOBAL_HANDLER_ANALYZED = False 1193 | self._extender.GLOBAL_HANDLER = False 1194 | #self._extender.replaceString.setText(self._extender.getCookieFromMessage(self._messageInfo)) 1195 | --------------------------------------------------------------------------------