diff --git a/authorization/authorization.py b/authorization/authorization.py index c519abe..f1a26c8 100644 --- a/authorization/authorization.py +++ b/authorization/authorization.py @@ -16,6 +16,8 @@ from java.net import URL import re +from thread import start_new_thread + def tool_needs_to_be_ignored(self, toolFlag): for i in range(0, self.IFList.getModel().getSize()): if self.IFList.getModel().getElementAt(i).split(":")[0] == "Ignore spider requests": @@ -41,7 +43,6 @@ def capture_last_authorization_header(self, messageInfo): self.lastAuthorizationHeader = authorization self.fetchAuthorizationHeaderButton.setEnabled(True) - def valid_tool(self, toolFlag): return (toolFlag == self._callbacks.TOOL_PROXY or (toolFlag == self._callbacks.TOOL_REPEATER and @@ -190,25 +191,179 @@ def handle_message(self, toolFlag, messageIsRequest, messageInfo): return if no_filters_defined(self): - checkAuthorization(self, messageInfo, - self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders(), - self.doUnauthorizedRequest.isSelected()) + # checkAuthorization(self, messageInfo, + # self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders(), + # self.doUnauthorizedRequest.isSelected()) + checkAuthorizationAllUsers(self, messageInfo, self.doUnauthorizedRequest.isSelected()) else: if message_passed_interception_filters(self, messageInfo): - checkAuthorization(self, messageInfo,self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) + # checkAuthorization(self, messageInfo,self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) + checkAuthorizationAllUsers(self, messageInfo, self.doUnauthorizedRequest.isSelected()) + +def checkAuthorizationAllUsers(self, messageInfo, checkUnauthorized=True): + originalHeaders = self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders() + + requestResponseUnauthorized = None + impressionUnauthorized = "Disabled" + + if checkUnauthorized: + messageUnauthorized = makeMessage(self, messageInfo, True, False) + requestResponseUnauthorized = makeRequest(self, messageInfo, messageUnauthorized) + if requestResponseUnauthorized and requestResponseUnauthorized.getResponse(): + unauthorizedResponse = requestResponseUnauthorized.getResponse() + analyzedResponseUnauthorized = self._helpers.analyzeResponse(unauthorizedResponse) + statusCodeUnauthorized = analyzedResponseUnauthorized.getHeaders()[0] + contentUnauthorized = getResponseBody(self, requestResponseUnauthorized) + + message = makeMessage(self, messageInfo, True, True) + requestResponse = makeRequest(self, messageInfo, message) + newResponse = requestResponse.getResponse() + analyzedResponse = self._helpers.analyzeResponse(newResponse) + + oldStatusCode = originalHeaders[0] + newStatusCode = analyzedResponse.getHeaders()[0] + oldContent = getResponseBody(self, messageInfo) + newContent = getResponseBody(self, requestResponse) + + EDFiltersUnauth = self.EDModelUnauth.toArray() + impressionUnauthorized = checkBypass(self, oldStatusCode, statusCodeUnauthorized, + oldContent, contentUnauthorized, + EDFiltersUnauth, requestResponseUnauthorized, + self.AndOrTypeUnauth.getSelectedItem()) + + self._lock.acquire() + + row = self._log.size() + method = self._helpers.analyzeRequest(messageInfo.getRequest()).getMethod() + original_url = self._helpers.analyzeRequest(messageInfo).getUrl() + + logEntry = LogEntry(self.currentRequestNumber, + method, + original_url, + messageInfo, + requestResponseUnauthorized if checkUnauthorized else None, + impressionUnauthorized) + + for user_id, user_data in self.userTab.user_tabs.items(): + user_name = user_data['user_name'] + ed_instance = user_data['ed_instance'] + mr_instance = user_data['mr_instance'] + + message = makeUserMessage(self, messageInfo, True, True, mr_instance) + requestResponse = makeRequest(self, messageInfo, message) + + if requestResponse and requestResponse.getResponse(): + newResponse = requestResponse.getResponse() + analyzedResponse = self._helpers.analyzeResponse(newResponse) + newStatusCode = analyzedResponse.getHeaders()[0] + oldContent = getResponseBody(self, messageInfo) + newContent = getResponseBody(self, requestResponse) + + EDFilters = ed_instance.EDModel.toArray() + impression = checkBypass(self, originalHeaders[0], newStatusCode, oldContent, newContent, + EDFilters, requestResponse, ed_instance.AndOrType.getSelectedItem()) + + savedRequestResponse = self._callbacks.saveBuffersToTempFiles(requestResponse) + logEntry.add_user_enforcement(user_id, savedRequestResponse, impression) + + self._log.add(logEntry) + SwingUtilities.invokeLater(UpdateTableEDT(self,"insert",row,row)) + self.currentRequestNumber = self.currentRequestNumber + 1 + self._lock.release() + +def makeUserMessage(self, messageInfo, removeOrNot, authorizeOrNot, mr_instance): + requestInfo = self._helpers.analyzeRequest(messageInfo) + headers = list(requestInfo.getHeaders()) + + if removeOrNot: + if hasattr(self, 'replaceString') and self.replaceString.getText(): + removeHeaders = self.replaceString.getText().split('\n') + removeHeaders = [header.split(':')[0].strip() + ':' for header in removeHeaders if ':' in header] + + headers_to_remove = [] + for header in headers[1:]: + for removeHeader in removeHeaders: + if header.lower().startswith(removeHeader.lower()): + headers_to_remove.append(header) + + for header in headers_to_remove: + if header in headers: + headers.remove(header) + + if authorizeOrNot: + for i in range(mr_instance.MRModel.getSize()): + rule_key = mr_instance.MRModel.getElementAt(i) + rule_data = mr_instance.badProgrammerMRModel.get(rule_key) + + if rule_data: + rule_type = rule_data['type'] + match_pattern = rule_data['match'] + replace_pattern = rule_data['replace'] + regex_match = rule_data.get('regexMatch') + + if rule_type == "Headers (simple string):": + modifiedHeaders = [h.replace(match_pattern, replace_pattern) for h in headers[1:]] + headers = [headers[0]] + modifiedHeaders + elif rule_type == "Headers (regex):": + if regex_match: + modifiedHeaders = [regex_match.sub(replace_pattern, h) for h in headers[1:]] + headers = [headers[0]] + modifiedHeaders + + if hasattr(self, 'replaceString') and self.replaceString.getText(): + replaceStringLines = self.replaceString.getText().split("\n") + for h in replaceStringLines: + if h.strip() and ':' in h: + headers.append(h.strip()) + + msgBody = messageInfo.getRequest()[requestInfo.getBodyOffset():] + + if authorizeOrNot and msgBody is not None: + msgBody_str = self._helpers.bytesToString(msgBody) + + for i in range(mr_instance.MRModel.getSize()): + rule_key = mr_instance.MRModel.getElementAt(i) + rule_data = mr_instance.badProgrammerMRModel.get(rule_key) + + if rule_data: + rule_type = rule_data['type'] + match_pattern = rule_data['match'] + replace_pattern = rule_data['replace'] + regex_match = rule_data.get('regexMatch') + + if rule_type == "Path (simple string):": + uriPath = headers[0].split(" ")[1] + if match_pattern in uriPath: + headers[0] = headers[0].replace(match_pattern, replace_pattern) + elif rule_type == "Path (regex):": + if regex_match: + uriPath = headers[0].split(" ")[1] + if regex_match.search(uriPath): + headers[0] = regex_match.sub(replace_pattern, headers[0]) + + elif rule_type == "Body (simple string):": + msgBody_str = msgBody_str.replace(match_pattern, replace_pattern) + elif rule_type == "Body (regex):": + if regex_match: + msgBody_str = regex_match.sub(replace_pattern, msgBody_str) + + msgBody = self._helpers.stringToBytes(msgBody_str) + + return self._helpers.buildHttpMessage(headers, msgBody) def send_request_to_autorize(self, messageInfo): if messageInfo.getResponse() is None: message = makeMessage(self, messageInfo,False,False) requestResponse = makeRequest(self, messageInfo, message) - checkAuthorization(self, requestResponse,self._helpers.analyzeResponse(requestResponse.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) + # checkAuthorization(self, requestResponse,self._helpers.analyzeResponse(requestResponse.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) + checkAuthorizationAllUsers(self, requestResponse, self.doUnauthorizedRequest.isSelected()) else: request = messageInfo.getRequest() response = messageInfo.getResponse() httpService = messageInfo.getHttpService() newHttpRequestResponse = IHttpRequestResponseImplementation(httpService,request,response) newHttpRequestResponsePersisted = self._callbacks.saveBuffersToTempFiles(newHttpRequestResponse) - checkAuthorization(self, newHttpRequestResponsePersisted,self._helpers.analyzeResponse(messageInfo.getResponse()).getHeaders(),self.doUnauthorizedRequest.isSelected()) + + checkAuthorizationAllUsers(self, newHttpRequestResponsePersisted, self.doUnauthorizedRequest.isSelected()) def auth_enforced_via_enforcement_detectors(self, filters, requestResponse, andOrEnforcement): response = requestResponse.getResponse() @@ -332,4 +487,4 @@ def retestAllRequests(self): self.logTable.setAutoCreateRowSorter(True) for i in range(self.tableModel.getRowCount()): logEntry = self._log.get(self.logTable.convertRowIndexToModel(i)) - handle_message(self, "AUTORIZE", False, logEntry._originalrequestResponse) + start_new_thread(handle_message, (self, "AUTORIZE", False, logEntry._originalrequestResponse)) \ No newline at end of file diff --git a/gui/configuration_tab.py b/gui/configuration_tab.py index 295d292..521536d 100644 --- a/gui/configuration_tab.py +++ b/gui/configuration_tab.py @@ -90,10 +90,8 @@ def draw(self): self._extender.filtersTabs = JTabbedPane() self._extender.filtersTabs = self._extender.filtersTabs - self._extender.filtersTabs.addTab("Enforcement Detector", self._extender.EDPnl) self._extender.filtersTabs.addTab("Unauthentication Detector ", self._extender.EDPnlUnauth) self._extender.filtersTabs.addTab("Interception Filters", self._extender.filtersPnl) - self._extender.filtersTabs.addTab("Match/Replace", self._extender.MRPnl) self._extender.filtersTabs.addTab("Table Filter", self._extender.filterPnl) self._extender.filtersTabs.addTab("Save/Restore", self._extender.exportPnl) @@ -386,5 +384,4 @@ def __init__(self, extender): def actionPerformed(self, e): selectedTitle = self._extender.savedHeadersTitlesCombo.getSelectedItem() headers = [x for x in self._extender.savedHeaders if x['title'] == selectedTitle] - self._extender.replaceString.setText(headers[0]['headers']) - + self._extender.replaceString.setText(headers[0]['headers']) \ No newline at end of file diff --git a/gui/export.py b/gui/export.py index 7a7f50b..e02ef5f 100644 --- a/gui/export.py +++ b/gui/export.py @@ -232,8 +232,6 @@ def draw(self): ) ) ) - - def export(self, event): if self.exportType.getSelectedItem() == "HTML": @@ -247,6 +245,44 @@ def saveStateAction(self, event): def restoreStateAction(self, event): self.save_restore.restoreState() + def shouldIncludeRow(self, logEntry, enforcementStatusFilter): + should_include = False + + if enforcementStatusFilter == "All Statuses": + should_include = True + elif enforcementStatusFilter == "As table filter": + if hasattr(self._extender, 'showBypassed') and hasattr(self._extender, 'showIsEnforced') and hasattr(self._extender, 'showEnforced'): + # Check unauthenticated status + unauth_status = logEntry._enfocementStatusUnauthorized + if ((self._extender.showBypassed.isSelected() and self.BYPASSSED_STR == unauth_status) or + (self._extender.showIsEnforced.isSelected() and self.IS_ENFORCED_STR == unauth_status) or + (self._extender.showEnforced.isSelected() and self.ENFORCED_STR == unauth_status) or + ("Disabled" == unauth_status)): + should_include = True + + for user_id in logEntry.get_all_users(): + user_data = logEntry.get_user_enforcement(user_id) + if user_data: + user_status = user_data['enforcementStatus'] + if ((self._extender.showBypassed.isSelected() and self.BYPASSSED_STR == user_status) or + (self._extender.showIsEnforced.isSelected() and self.IS_ENFORCED_STR == user_status) or + (self._extender.showEnforced.isSelected() and self.ENFORCED_STR == user_status)): + should_include = True + break + else: + should_include = True + else: + if enforcementStatusFilter == logEntry._enfocementStatusUnauthorized: + should_include = True + else: + for user_id in logEntry.get_all_users(): + user_data = logEntry.get_user_enforcement(user_id) + if user_data and enforcementStatusFilter == user_data['enforcementStatus']: + should_include = True + break + + return should_include + def exportToHTML(self): parentFrame = JFrame() fileChooser = JFileChooser() @@ -257,6 +293,16 @@ def exportToHTML(self): fileToSave = fileChooser.getSelectedFile() enforcementStatusFilter = self.exportES.getSelectedItem() + + header_html = "IDMethodURLOriginal lengthUnauth lengthUnauth Status" + + if hasattr(self._extender, 'userTab') and self._extender.userTab: + for user_id in sorted(self._extender.userTab.user_tabs.keys()): + user_name = self._extender.userTab.user_tabs[user_id]['user_name'] + header_html += "{} Len{} Status".format(user_name, user_name) + + header_html += "" + htmlContent = """Autorize Report by Barak Tawily

Autorize Report

-
- +
IDMethodURLOriginal lengthModified lengthUnauthorized lengthAuthorization Enforcement StatusAuthorization Unauthenticated Status
""" + header_html + """ """ - unique_HTML_lines = set() # set to keep track of unique values - for i in range(0,self._log.size()): + + unique_HTML_lines = set() + for i in range(0, self._log.size()): + logEntry = self._log.get(i) + if self.removeDuplicates.isSelected(): - # line data only looks for method, url, and authorized status. Does not factor in size of request during comparision - lineData = "\t%s\t%s\t%s\t%s\n" % (self._log.get(i)._method, self._log.get(i)._url, self._log.get(i)._enfocementStatus,self._log.get(i)._enfocementStatusUnauthorized) - if lineData in unique_HTML_lines: # Skip if line is already in set + user_statuses = [] + for user_id in sorted(logEntry.get_all_users()): + user_data = logEntry.get_user_enforcement(user_id) + if user_data: + user_statuses.append(user_data['enforcementStatus']) + + lineData = "\t%s\t%s\t%s\t%s" % (logEntry._method, logEntry._url, + logEntry._enfocementStatusUnauthorized, + "\t".join(user_statuses)) + if lineData in unique_HTML_lines: continue - else: # Add line to set and continue with execution + else: unique_HTML_lines.add(lineData) - color_modified = "" - if self._log.get(i)._enfocementStatus == self.BYPASSSED_STR: - color_modified = "red" - elif self._log.get(i)._enfocementStatus == self.IS_ENFORCED_STR: - color_modified = "yellow" - elif self._log.get(i)._enfocementStatus == self.ENFORCED_STR: - color_modified = "LawnGreen" - - color_unauthorized = "" - if self._log.get(i)._enfocementStatusUnauthorized == self.BYPASSSED_STR: - color_unauthorized = "red" - elif self._log.get(i)._enfocementStatusUnauthorized == self.IS_ENFORCED_STR: - color_unauthorized = "yellow" - elif self._log.get(i)._enfocementStatusUnauthorized == self.ENFORCED_STR: - color_unauthorized = "LawnGreen" - - if enforcementStatusFilter == "All Statuses": - htmlContent += "" % (self._log.get(i)._id, self._log.get(i)._method, self._log.get(i)._url, self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse is not None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse is not None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse is not None else 0, color_modified, self._log.get(i)._enfocementStatus, color_unauthorized, self._log.get(i)._enfocementStatusUnauthorized) - elif enforcementStatusFilter == "As table filter": - if ((self._extender.showAuthBypassModified.isSelected() and self.BYPASSSED_STR == self._log.get(i)._enfocementStatus) or - (self._extender.showAuthPotentiallyEnforcedModified.isSelected() and "Is enforced???" == self._log.get(i)._enfocementStatus) or - (self._extender.showAuthEnforcedModified.isSelected() and self.ENFORCED_STR == self._log.get(i)._enfocementStatus) or - (self._extender.showAuthBypassUnauthenticated.isSelected() and self.BYPASSSED_STR == self._log.get(i)._enfocementStatusUnauthorized) or - (self._extender.showAuthPotentiallyEnforcedUnauthenticated.isSelected() and "Is enforced???" == self._log.get(i)._enfocementStatusUnauthorized) or - (self._extender.showAuthEnforcedUnauthenticated.isSelected() and self.ENFORCED_STR == self._log.get(i)._enfocementStatusUnauthorized) or - (self._extender.showDisabledUnauthenticated.isSelected() and "Disabled" == self._log.get(i)._enfocementStatusUnauthorized)): - htmlContent += "" % (self._log.get(i)._id, self._log.get(i)._method, self._log.get(i)._url, self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse is not None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse is not None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse is not None else 0, color_modified, self._log.get(i)._enfocementStatus, color_unauthorized, self._log.get(i)._enfocementStatusUnauthorized) - else: - if (enforcementStatusFilter == self._log.get(i)._enfocementStatus) or (enforcementStatusFilter == self._log.get(i)._enfocementStatusUnauthorized): - htmlContent += "" % (self._log.get(i)._id, self._log.get(i)._method, self._log.get(i)._url, self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse is not None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse is not None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse is not None else 0, color_modified, self._log.get(i)._enfocementStatus, color_unauthorized, self._log.get(i)._enfocementStatusUnauthorized) + + if not self.shouldIncludeRow(logEntry, enforcementStatusFilter): + continue + + row_html = "" % ( + logEntry._id, logEntry._method, logEntry._url, logEntry._url) + + orig_len = len(logEntry._originalrequestResponse.getResponse()) if logEntry._originalrequestResponse else 0 + row_html += "" % orig_len + + unauth_len = 0 + if logEntry._unauthorizedRequestResponse: + unauth_len = len(logEntry._unauthorizedRequestResponse.getResponse()) + + unauth_color = "" + if logEntry._enfocementStatusUnauthorized == self.BYPASSSED_STR: + unauth_color = "red" + elif logEntry._enfocementStatusUnauthorized == self.IS_ENFORCED_STR: + unauth_color = "yellow" + elif logEntry._enfocementStatusUnauthorized == self.ENFORCED_STR: + unauth_color = "LawnGreen" + + row_html += "" % (unauth_len, unauth_color, logEntry._enfocementStatusUnauthorized) + + # User data + if hasattr(self._extender, 'userTab') and self._extender.userTab: + for user_id in sorted(self._extender.userTab.user_tabs.keys()): + user_data = logEntry.get_user_enforcement(user_id) + if user_data and user_data['requestResponse']: + user_len = len(user_data['requestResponse'].getResponse()) + user_status = user_data['enforcementStatus'] + + user_color = "" + if user_status == self.BYPASSSED_STR: + user_color = "red" + elif user_status == self.IS_ENFORCED_STR: + user_color = "yellow" + elif user_status == self.ENFORCED_STR: + user_color = "LawnGreen" + + row_html += "" % (user_len, user_color, user_status) + else: + row_html += "" + + row_html += "" + htmlContent += row_html htmlContent += "
%d%s%s%d%d%d%s%s
%d%s%s%d%d%d%s%s
%d%s%s%d%d%d%s%s
%d%s%s%d%d%s%d%s0N/A
" f = open(fileToSave.getAbsolutePath(), 'w') f.writelines(htmlContent) f.close() - + def exportToCSV(self): parentFrame = JFrame() fileChooser = JFileChooser() @@ -341,33 +412,60 @@ def exportToCSV(self): fileToSave = fileChooser.getSelectedFile() enforcementStatusFilter = self.exportES.getSelectedItem() - csvContent = "id\tMethod\tURL\tOriginal length\tModified length\tUnauthorized length\tAuthorization Enforcement Status\tAuthorization Unauthenticated Status\n" + + csvContent = "ID,Method,URL,Original Length,Unauth Length,Unauth Status" + + if hasattr(self._extender, 'userTab') and self._extender.userTab: + for user_id in sorted(self._extender.userTab.user_tabs.keys()): + user_name = self._extender.userTab.user_tabs[user_id]['user_name'] + csvContent += ",{} Length,{} Status".format(user_name, user_name) + + csvContent += "\n" - unique_CVS_lines = set() + unique_CSV_lines = set() for i in range(0, self._log.size()): + logEntry = self._log.get(i) + if self.removeDuplicates.isSelected(): - # line data only looks for method, url, and authorized status. Does not factor in size of request during comparision - lineData = "\t%s\t%s\t%s\t%s\n" % (self._log.get(i)._method, self._log.get(i)._url, self._log.get(i)._enfocementStatus,self._log.get(i)._enfocementStatusUnauthorized) - if lineData in unique_CVS_lines: # Skip if line is already in set + user_statuses = [] + for user_id in sorted(logEntry.get_all_users()): + user_data = logEntry.get_user_enforcement(user_id) + if user_data: + user_statuses.append(user_data['enforcementStatus']) + + lineData = ",{},{},{},{}".format(logEntry._method, logEntry._url, + logEntry._enfocementStatusUnauthorized, + ",".join(user_statuses)) + if lineData in unique_CSV_lines: continue - else: # Add line to set and continue with execution - unique_CVS_lines.add(lineData) - if enforcementStatusFilter == "All Statuses": - csvContent += "%d\t%s\t%s\t%d\t%d\t%d\t%s\t%s\n" % (self._log.get(i)._id, self._log.get(i)._method, self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse is not None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse is not None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse is not None else 0, self._log.get(i)._enfocementStatus, self._log.get(i)._enfocementStatusUnauthorized) - elif enforcementStatusFilter == "As table filter": - if ((self._extender.showAuthBypassModified.isSelected() and self.BYPASSSED_STR == self._log.get(i)._enfocementStatus) or - (self._extender.showAuthPotentiallyEnforcedModified.isSelected() and "Is enforced???" == self._log.get(i)._enfocementStatus) or - (self._extender.showAuthEnforcedModified.isSelected() and self.ENFORCED_STR == self._log.get(i)._enfocementStatus) or - (self._extender.showAuthBypassUnauthenticated.isSelected() and self.BYPASSSED_STR == self._log.get(i)._enfocementStatusUnauthorized) or - (self._extender.showAuthPotentiallyEnforcedUnauthenticated.isSelected() and "Is enforced???" == self._log.get(i)._enfocementStatusUnauthorized) or - (self._extender.showAuthEnforcedUnauthenticated.isSelected() and self.ENFORCED_STR == self._log.get(i)._enfocementStatusUnauthorized) or - (self._extender.showDisabledUnauthenticated.isSelected() and "Disabled" == self._log.get(i)._enfocementStatusUnauthorized)): - csvContent += "%d\t%s\t%s\t%d\t%d\t%d\t%s\t%s\n" % (self._log.get(i)._id, self._log.get(i)._method, self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse is not None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse is not None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse is not None else 0, self._log.get(i)._enfocementStatus, self._log.get(i)._enfocementStatusUnauthorized) - else: - if (enforcementStatusFilter == self._log.get(i)._enfocementStatus) or (enforcementStatusFilter == self._log.get(i)._enfocementStatusUnauthorized): - csvContent += "%d\t%s\t%s\t%d\t%d\t%d\t%s\t%s\n" % (self._log.get(i)._id, self._log.get(i)._method, self._log.get(i)._url, len(self._log.get(i)._originalrequestResponse.getResponse()) if self._log.get(i)._originalrequestResponse is not None else 0, len(self._log.get(i)._requestResponse.getResponse()) if self._log.get(i)._requestResponse is not None else 0, len(self._log.get(i)._unauthorizedRequestResponse.getResponse()) if self._log.get(i)._unauthorizedRequestResponse is not None else 0, self._log.get(i)._enfocementStatus, self._log.get(i)._enfocementStatusUnauthorized) - + else: + unique_CSV_lines.add(lineData) + + if not self.shouldIncludeRow(logEntry, enforcementStatusFilter): + continue + + orig_len = len(logEntry._originalrequestResponse.getResponse()) if logEntry._originalrequestResponse else 0 + unauth_len = len(logEntry._unauthorizedRequestResponse.getResponse()) if logEntry._unauthorizedRequestResponse else 0 + + url_safe = '"{}"'.format(str(logEntry._url).replace('"', '""')) + + csv_row = '{},{},{},{},{},"{}"'.format( + logEntry._id, logEntry._method, url_safe, orig_len, unauth_len, logEntry._enfocementStatusUnauthorized) + + # User data + if hasattr(self._extender, 'userTab') and self._extender.userTab: + for user_id in sorted(self._extender.userTab.user_tabs.keys()): + user_data = logEntry.get_user_enforcement(user_id) + if user_data and user_data['requestResponse']: + user_len = len(user_data['requestResponse'].getResponse()) + user_status = user_data['enforcementStatus'] + csv_row += ',{},"{}"'.format(user_len, user_status) + else: + csv_row += ',0,"N/A"' + + csv_row += "\n" + csvContent += csv_row f = open(fileToSave.getAbsolutePath(), 'w') f.writelines(csvContent) - f.close() + f.close() \ No newline at end of file diff --git a/gui/save_restore.py b/gui/save_restore.py index 55d2999..8c98cdb 100644 --- a/gui/save_restore.py +++ b/gui/save_restore.py @@ -32,13 +32,9 @@ def __init__(self, extender): "interceptRequestsfromRepeater", "doUnauthorizedRequest", "replaceQueryParam", - "showAuthBypassModified", - "showAuthPotentiallyEnforcedModified", - "showAuthEnforcedModified", - "showAuthBypassUnauthenticated", - "showAuthPotentiallyEnforcedUnauthenticated", - "showAuthEnforcedUnauthenticated", - "showDisabledUnauthenticated" + "showBypassed", + "showIsEnforced", + "showEnforced" ] def saveState(self): @@ -55,57 +51,56 @@ def saveState(self): # Configuration tempRow = ["ReplaceString", base64.b64encode(self._extender.replaceString.getText())] csvwriter.writerow(tempRow) - - for EDFilter in self._extender.EDModel.toArray(): - tempRow = ["EDFilter", base64.b64encode(EDFilter)] - csvwriter.writerow(tempRow) - - for EDFilterUnauth in self._extender.EDModelUnauth.toArray(): - tempRow = ["EDFilterUnauth", base64.b64encode(EDFilterUnauth)] - csvwriter.writerow(tempRow) - - for IFFilter in self._extender.IFModel.toArray(): - tempRow = ["IFFilter", base64.b64encode(IFFilter)] + + user_configs = [] + + for user_id, user_data in self._extender.userTab.user_tabs.items(): + user_config = { + 'user_id': user_id, + 'user_name': user_data['user_name'], + 'ed_filters': list(user_data['ed_instance'].EDModel.toArray()), + 'ed_type': user_data['ed_instance'].EDType.getSelectedIndex(), + 'ed_text': user_data['ed_instance'].EDText.getText(), + 'andor_type': user_data['ed_instance'].AndOrType.getSelectedIndex(), + 'mr_rules': dict(user_data['mr_instance'].badProgrammerMRModel) + } + + user_configs.append(user_config) + + tempRow = ["UserConfigs", base64.b64encode(json.dumps(user_configs))] csvwriter.writerow(tempRow) - for t in ["AndOrType", "AndOrTypeUnauth"]: - tempRow = [t, getattr(self._extender, t).getSelectedItem()] - csvwriter.writerow(tempRow) + if hasattr(self._extender, 'EDModelUnauth'): + for EDFilterUnauth in self._extender.EDModelUnauth.toArray(): + tempRow = ["EDFilterUnauth", base64.b64encode(EDFilterUnauth)] + csvwriter.writerow(tempRow) - for key in self._extender.badProgrammerMRModel: - d = dict(self._extender.badProgrammerMRModel[key]) - d["regexMatch"] = d["regexMatch"] is not None - tempRow = ["MatchReplace", base64.b64encode(json.dumps(d))] - csvwriter.writerow(tempRow) + if hasattr(self._extender, 'IFModel'): + for IFFilter in self._extender.IFModel.toArray(): + tempRow = ["IFFilter", base64.b64encode(IFFilter)] + csvwriter.writerow(tempRow) d = dict((c, getattr(self._extender, c).isSelected()) for c in self._checkBoxes) - tempRow = ["CheckBoxes", json.dumps(d)] - csvwriter.writerow(tempRow) - isSelected = self._extender.exportPnl.getComponents()[-1].isSelected() - tempRow = ["RemoveDuplicates", json.dumps(isSelected)] + tempRow = ["CheckBoxes", json.dumps(d)] csvwriter.writerow(tempRow) # Request/response list - for i in range(0,self._extender._log.size()): - tempRequestResponseHost = self._extender._log.get(i)._requestResponse.getHttpService().getHost() - tempRequestResponsePort = self._extender._log.get(i)._requestResponse.getHttpService().getPort() - tempRequestResponseProtocol = self._extender._log.get(i)._requestResponse.getHttpService().getProtocol() - tempRequestResponseRequest = base64.b64encode(self._extender._log.get(i)._requestResponse.getRequest()) - tempRequestResponseResponse = base64.b64encode(self._extender._log.get(i)._requestResponse.getResponse()) - - tempOriginalRequestResponseHost = self._extender._log.get(i)._originalrequestResponse.getHttpService().getHost() - tempOriginalRequestResponsePort = self._extender._log.get(i)._originalrequestResponse.getHttpService().getPort() - tempOriginalRequestResponseProtocol = self._extender._log.get(i)._originalrequestResponse.getHttpService().getProtocol() - tempOriginalRequestResponseRequest = base64.b64encode(self._extender._log.get(i)._originalrequestResponse.getRequest()) - tempOriginalRequestResponseResponse = base64.b64encode(self._extender._log.get(i)._originalrequestResponse.getResponse()) - - if self._extender._log.get(i)._unauthorizedRequestResponse is not None: - tempUnauthorizedRequestResponseHost = self._extender._log.get(i)._unauthorizedRequestResponse.getHttpService().getHost() - tempUnauthorizedRequestResponsePort = self._extender._log.get(i)._unauthorizedRequestResponse.getHttpService().getPort() - tempUnauthorizedRequestResponseProtocol = self._extender._log.get(i)._unauthorizedRequestResponse.getHttpService().getProtocol() - tempUnauthorizedRequestResponseRequest = base64.b64encode(self._extender._log.get(i)._unauthorizedRequestResponse.getRequest()) - tempUnauthorizedRequestResponseResponse = base64.b64encode(self._extender._log.get(i)._unauthorizedRequestResponse.getResponse()) + for i in range(0, self._extender._log.size()): + logEntry = self._extender._log.get(i) + + tempOriginalRequestResponseHost = logEntry._originalrequestResponse.getHttpService().getHost() + tempOriginalRequestResponsePort = logEntry._originalrequestResponse.getHttpService().getPort() + tempOriginalRequestResponseProtocol = logEntry._originalrequestResponse.getHttpService().getProtocol() + tempOriginalRequestResponseRequest = base64.b64encode(logEntry._originalrequestResponse.getRequest()) + tempOriginalRequestResponseResponse = base64.b64encode(logEntry._originalrequestResponse.getResponse()) + + if logEntry._unauthorizedRequestResponse is not None: + tempUnauthorizedRequestResponseHost = logEntry._unauthorizedRequestResponse.getHttpService().getHost() + tempUnauthorizedRequestResponsePort = logEntry._unauthorizedRequestResponse.getHttpService().getPort() + tempUnauthorizedRequestResponseProtocol = logEntry._unauthorizedRequestResponse.getHttpService().getProtocol() + tempUnauthorizedRequestResponseRequest = base64.b64encode(logEntry._unauthorizedRequestResponse.getRequest()) + tempUnauthorizedRequestResponseResponse = base64.b64encode(logEntry._unauthorizedRequestResponse.getResponse()) else: tempUnauthorizedRequestResponseHost = None tempUnauthorizedRequestResponsePort = None @@ -113,13 +108,30 @@ def saveState(self): tempUnauthorizedRequestResponseRequest = None tempUnauthorizedRequestResponseResponse = None - tempEnforcementStatus = self._extender._log.get(i)._enfocementStatus - tempEnforcementStatusUnauthorized = self._extender._log.get(i)._enfocementStatusUnauthorized - - tempRow = [tempRequestResponseHost,tempRequestResponsePort,tempRequestResponseProtocol,tempRequestResponseRequest,tempRequestResponseResponse] - tempRow.extend([tempOriginalRequestResponseHost,tempOriginalRequestResponsePort,tempOriginalRequestResponseProtocol,tempOriginalRequestResponseRequest,tempOriginalRequestResponseResponse]) - tempRow.extend([tempUnauthorizedRequestResponseHost,tempUnauthorizedRequestResponsePort,tempUnauthorizedRequestResponseProtocol,tempUnauthorizedRequestResponseRequest,tempUnauthorizedRequestResponseResponse]) - tempRow.extend([tempEnforcementStatus,tempEnforcementStatusUnauthorized]) + tempEnforcementStatusUnauthorized = logEntry._enfocementStatusUnauthorized + + user_data_json = {} + + for user_id in logEntry.get_all_users(): + user_enforcement = logEntry.get_user_enforcement(user_id) + if user_enforcement and user_enforcement['requestResponse']: + user_data_json[str(user_id)] = { + 'host': user_enforcement['requestResponse'].getHttpService().getHost(), + 'port': user_enforcement['requestResponse'].getHttpService().getPort(), + 'protocol': user_enforcement['requestResponse'].getHttpService().getProtocol(), + 'request': base64.b64encode(user_enforcement['requestResponse'].getRequest()), + 'response': base64.b64encode(user_enforcement['requestResponse'].getResponse()), + 'status': user_enforcement['enforcementStatus'] + } + + tempRow = [ + tempOriginalRequestResponseHost, tempOriginalRequestResponsePort, tempOriginalRequestResponseProtocol, + tempOriginalRequestResponseRequest, tempOriginalRequestResponseResponse, + tempUnauthorizedRequestResponseHost, tempUnauthorizedRequestResponsePort, tempUnauthorizedRequestResponseProtocol, + tempUnauthorizedRequestResponseRequest, tempUnauthorizedRequestResponseResponse, + tempEnforcementStatusUnauthorized, + base64.b64encode(json.dumps(user_data_json)) + ] csvwriter.writerow(tempRow) @@ -128,51 +140,69 @@ def restoreState(self): fileChooser = JFileChooser() fileChooser.setDialogTitle("State import file") userSelection = fileChooser.showDialog(parentFrame, "Restore") - modelMap = { - "IFFilter": self._extender.IFModel, - "EDFilter": self._extender.EDModel, - "EDFilterUnauth": self._extender.EDModelUnauth - } if userSelection == JFileChooser.APPROVE_OPTION: importFile = fileChooser.getSelectedFile() + + self._extender._log.clear() + self._extender.tableModel.fireTableDataChanged() + + if hasattr(self._extender, 'EDModelUnauth'): + self._extender.EDModelUnauth.clear() + if hasattr(self._extender, 'IFModel'): + self._extender.IFModel.clear() with open(importFile.getAbsolutePath(), 'r') as csvfile: csvreader = csv.reader(csvfile, delimiter='\t', quotechar='|') + user_configs = None + for row in csvreader: # Configuration if row[0] == "ReplaceString": self._extender.replaceString.setText(base64.b64decode(row[1])) continue - if row[0] in modelMap: - f = base64.b64decode(row[1]) - if f not in modelMap[row[0]].toArray(): - modelMap[row[0]].addElement(f) + if row[0] == "UserConfigs": + user_configs = json.loads(base64.b64decode(row[1])) + + # Restore user configurations + if hasattr(self._extender, 'userTab') and self._extender.userTab: + self._extender.userTab.reset_user() + + for i, config in enumerate(user_configs): + if i == 0: + user_data = list(self._extender.userTab.user_tabs.values())[0] + else: + self._extender.userTab.add_user() + user_data = list(self._extender.userTab.user_tabs.values())[-1] + + user_data['ed_instance'].EDModel.clear() + for filter in config['ed_filters']: + user_data['ed_instance'].EDModel.addElement(filter) + + user_data['ed_instance'].EDType.setSelectedIndex(config['ed_type']) + user_data['ed_instance'].EDText.setText(config['ed_text']) + user_data['ed_instance'].AndOrType.setSelectedIndex(config['andor_type']) + + user_data['mr_instance'].badProgrammerMRModel.clear() + user_data['mr_instance'].MRModel.clear() + for key, value in config['mr_rules'].items(): + user_data['mr_instance'].badProgrammerMRModel[key] = value + user_data['mr_instance'].MRModel.addElement(key) continue - if row[0] in {"AndOrType", "AndOrTypeUnauth"}: - getattr(self._extender, row[0]).setSelectedItem(row[1]) + if row[0] == "EDFilterUnauth": + if hasattr(self._extender, 'EDModelUnauth'): + self._extender.EDModelUnauth.addElement(base64.b64decode(row[1])) continue - if row[0] == "MatchReplace": - d = json.loads(base64.b64decode(row[1])) - key = d["type"] + " " + d["match"] + "->" + d["replace"] - if key in self._extender.badProgrammerMRModel: - continue - regexMatch = None - if d["regexMatch"]: - try: - d["regexMatch"] = re.compile(d["match"]) - except re.error: - print("ERROR: Regex to restore is invalid:", d["match"]) - continue - self._extender.badProgrammerMRModel[key] = d - self._extender.MRModel.addElement(key) + if row[0] == "IFFilter": + if hasattr(self._extender, 'IFModel'): + self._extender.IFModel.addElement(base64.b64decode(row[1])) continue - + if row[0] == "CheckBoxes": d = json.loads(row[1]) for k in d: @@ -188,74 +218,56 @@ def restoreState(self): continue # Request/response list - tempRequestResponseHost = row[0] - tempRequestResponsePort = row[1] - tempRequestResponseProtocol = row[2] - tempRequestResponseRequest = base64.b64decode(row[3]) - tempRequestResponseResponse = base64.b64decode(row[4]) - - tempRequestResponseHttpService = self._extender._helpers.buildHttpService(tempRequestResponseHost,int(tempRequestResponsePort),tempRequestResponseProtocol) - tempRequestResponse = IHttpRequestResponseImplementation(tempRequestResponseHttpService,tempRequestResponseRequest,tempRequestResponseResponse) - - tempOriginalRequestResponseHost = row[5] - tempOriginalRequestResponsePort = row[6] - tempOriginalRequestResponseProtocol = row[7] - tempOriginalRequestResponseRequest = base64.b64decode(row[8]) - tempOriginalRequestResponseResponse = base64.b64decode(row[9]) - - tempOriginalRequestResponseHttpService = self._extender._helpers.buildHttpService(tempOriginalRequestResponseHost,int(tempOriginalRequestResponsePort),tempOriginalRequestResponseProtocol) - tempOriginalRequestResponse = IHttpRequestResponseImplementation(tempOriginalRequestResponseHttpService,tempOriginalRequestResponseRequest,tempOriginalRequestResponseResponse) - - checkAuthentication = True - if row[10] != '': - tempUnauthorizedRequestResponseHost = row[10] - tempUnauthorizedRequestResponsePort = row[11] - tempUnauthorizedRequestResponseProtocol = row[12] - tempUnauthorizedRequestResponseRequest = base64.b64decode(row[13]) - tempUnauthorizedRequestResponseResponse = base64.b64decode(row[14]) - tempUnauthorizedRequestResponseHttpService = self._extender._helpers.buildHttpService(tempUnauthorizedRequestResponseHost,int(tempUnauthorizedRequestResponsePort),tempUnauthorizedRequestResponseProtocol) - tempUnauthorizedRequestResponse = IHttpRequestResponseImplementation(tempUnauthorizedRequestResponseHttpService,tempUnauthorizedRequestResponseRequest,tempUnauthorizedRequestResponseResponse) - else: - checkAuthentication = False - tempUnauthorizedRequestResponse = None - - tempEnforcementStatus = row[15] - tempEnforcementStatusUnauthorized = row[16] - - self._extender._lock.acquire() - - row = self._extender._log.size() - - if checkAuthentication: - self._extender._log.add( - LogEntry(self._extender.currentRequestNumber, - self._extender._callbacks.saveBuffersToTempFiles(tempRequestResponse), - self._extender._helpers.analyzeRequest(tempRequestResponse).getMethod(), - self._extender._helpers.analyzeRequest(tempRequestResponse).getUrl(), - self._extender._callbacks.saveBuffersToTempFiles(tempOriginalRequestResponse), - tempEnforcementStatus, - self._extender._callbacks.saveBuffersToTempFiles(tempUnauthorizedRequestResponse), - tempEnforcementStatusUnauthorized)) - else: - self._extender._log.add( - LogEntry(self._extender.currentRequestNumber, - self._extender._callbacks.saveBuffersToTempFiles(tempRequestResponse), - self._extender._helpers.analyzeRequest(tempRequestResponse).getMethod(), - self._extender._helpers.analyzeRequest(tempRequestResponse).getUrl(), - self._extender._callbacks.saveBuffersToTempFiles(tempOriginalRequestResponse), - tempEnforcementStatus,None,tempEnforcementStatusUnauthorized)) - - SwingUtilities.invokeLater(UpdateTableEDT(self._extender,"insert",row,row)) - self._extender.currentRequestNumber = self._extender.currentRequestNumber + 1 - self._extender._lock.release() - - lastRow = self._extender._log.size() - if lastRow > 0: - cookiesHeader = get_cookie_header_from_message(self._extender, self._extender._log.get(lastRow - 1)._requestResponse) - if cookiesHeader: - self._extender.lastCookiesHeader = cookiesHeader - self._extender.fetchCookiesHeaderButton.setEnabled(True) - authorizationHeader = get_authorization_header_from_message(self._extender, self._extender._log.get(lastRow - 1)._requestResponse) - if authorizationHeader: - self._extender.lastAuthorizationHeader = authorizationHeader - self._extender.fetchAuthorizationHeaderButton.setEnabled(True) + + if len(row) >= 12: + tempOriginalRequestResponseHost = row[0] + tempOriginalRequestResponsePort = row[1] + tempOriginalRequestResponseProtocol = row[2] + tempOriginalRequestResponseRequest = base64.b64decode(row[3]) + tempOriginalRequestResponseResponse = base64.b64decode(row[4]) + + tempOriginalRequestResponseHttpService = self._extender._helpers.buildHttpService( + tempOriginalRequestResponseHost, int(tempOriginalRequestResponsePort), tempOriginalRequestResponseProtocol) + tempOriginalRequestResponse = IHttpRequestResponseImplementation( + tempOriginalRequestResponseHttpService, tempOriginalRequestResponseRequest, tempOriginalRequestResponseResponse) + + checkAuthentication = True + if row[5] != '': + tempUnauthorizedRequestResponseHost = row[5] + tempUnauthorizedRequestResponsePort = row[6] + tempUnauthorizedRequestResponseProtocol = row[7] + tempUnauthorizedRequestResponseRequest = base64.b64decode(row[8]) + tempUnauthorizedRequestResponseResponse = base64.b64decode(row[9]) + tempUnauthorizedRequestResponseHttpService = self._extender._helpers.buildHttpService( + tempUnauthorizedRequestResponseHost, int(tempUnauthorizedRequestResponsePort), tempUnauthorizedRequestResponseProtocol) + tempUnauthorizedRequestResponse = IHttpRequestResponseImplementation( + tempUnauthorizedRequestResponseHttpService, tempUnauthorizedRequestResponseRequest, tempUnauthorizedRequestResponseResponse) + else: + checkAuthentication = False + tempUnauthorizedRequestResponse = None + + tempEnforcementStatusUnauthorized = row[10] + + method = self._extender._helpers.analyzeRequest(tempOriginalRequestResponse).getMethod() + original_url = self._extender._helpers.analyzeRequest(tempOriginalRequestResponse).getUrl() + + logEntry = LogEntry(self._extender.currentRequestNumber, method, original_url, + self._extender._callbacks.saveBuffersToTempFiles(tempOriginalRequestResponse), + self._extender._callbacks.saveBuffersToTempFiles(tempUnauthorizedRequestResponse) if checkAuthentication else None, + tempEnforcementStatusUnauthorized) + + if len(row) > 11 and row[11]: + user_data_json = json.loads(base64.b64decode(row[11])) + for user_id_str, user_data in user_data_json.items(): + user_id = int(user_id_str) + userHttpService = self._extender._helpers.buildHttpService( + user_data['host'], int(user_data['port']), user_data['protocol']) + userRequestResponse = IHttpRequestResponseImplementation( + userHttpService, base64.b64decode(user_data['request']), base64.b64decode(user_data['response'])) + savedUserRequestResponse = self._extender._callbacks.saveBuffersToTempFiles(userRequestResponse) + logEntry.add_user_enforcement(user_id, savedUserRequestResponse, user_data['status']) + + self._extender._log.add(logEntry) + self._extender.currentRequestNumber = self._extender.currentRequestNumber + 1 + + self._extender.tableModel.fireTableDataChanged() \ No newline at end of file diff --git a/gui/table.py b/gui/table.py index a7ac4c2..9384e85 100644 --- a/gui/table.py +++ b/gui/table.py @@ -6,14 +6,13 @@ from java.lang import Integer from java.lang import Runnable from javax.swing import JTable -from javax.swing import JLabel from javax.swing import JPanel from javax.swing import RowFilter from javax.swing import JCheckBox -from javax.swing import GroupLayout from javax.swing import ListSelectionModel from java.awt.event import MouseAdapter from java.awt.event import ItemListener +from java.awt import FlowLayout from javax.swing.table import AbstractTableModel from javax.swing.event import ListSelectionListener @@ -27,187 +26,34 @@ def draw(self): """ init show tab """ + self._extender.filterPnl = JPanel(FlowLayout(FlowLayout.LEFT)) - filterLModified = JLabel("Modified:") - filterLModified.setBounds(10, 10, 100, 30) - - filterLUnauthenticated = JLabel("Unauthenticated:") - filterLUnauthenticated.setBounds(250, 10, 100, 30) - - self._extender.showAuthBypassModified = JCheckBox(self._extender.BYPASSSED_STR) - self._extender.showAuthBypassModified.setBounds(10, 35, 200, 30) - self._extender.showAuthBypassModified.setSelected(True) - self._extender.showAuthBypassModified.addItemListener(TabTableFilter(self._extender)) - - self._extender.showAuthPotentiallyEnforcedModified = JCheckBox("Is enforced???") - self._extender.showAuthPotentiallyEnforcedModified.setBounds(10, 60, 200, 30) - self._extender.showAuthPotentiallyEnforcedModified.setSelected(True) - self._extender.showAuthPotentiallyEnforcedModified.addItemListener(TabTableFilter(self._extender)) - - self._extender.showAuthEnforcedModified = JCheckBox(self._extender.ENFORCED_STR) - self._extender.showAuthEnforcedModified.setBounds(10, 85, 200, 30) - self._extender.showAuthEnforcedModified.setSelected(True) - self._extender.showAuthEnforcedModified.addItemListener(TabTableFilter(self._extender)) - - self._extender.showAuthBypassUnauthenticated = JCheckBox(self._extender.BYPASSSED_STR) - self._extender.showAuthBypassUnauthenticated.setBounds(250, 35, 200, 30) - self._extender.showAuthBypassUnauthenticated.setSelected(True) - self._extender.showAuthBypassUnauthenticated.addItemListener(TabTableFilter(self._extender)) - - self._extender.showAuthPotentiallyEnforcedUnauthenticated = JCheckBox("Is enforced???") - self._extender.showAuthPotentiallyEnforcedUnauthenticated.setBounds(250, 60, 200, 30) - self._extender.showAuthPotentiallyEnforcedUnauthenticated.setSelected(True) - self._extender.showAuthPotentiallyEnforcedUnauthenticated.addItemListener(TabTableFilter(self._extender)) - - self._extender.showAuthEnforcedUnauthenticated = JCheckBox(self._extender.ENFORCED_STR) - self._extender.showAuthEnforcedUnauthenticated.setBounds(250, 85, 200, 30) - self._extender.showAuthEnforcedUnauthenticated.setSelected(True) - self._extender.showAuthEnforcedUnauthenticated.addItemListener(TabTableFilter(self._extender)) - - self._extender.showDisabledUnauthenticated = JCheckBox("Disabled") - self._extender.showDisabledUnauthenticated.setBounds(250, 110, 200, 30) - self._extender.showDisabledUnauthenticated.setSelected(True) - self._extender.showDisabledUnauthenticated.addItemListener(TabTableFilter(self._extender)) - - self._extender.filterPnl = JPanel() - layout = GroupLayout(self._extender.filterPnl) - self._extender.filterPnl.setLayout(layout) - layout.setAutoCreateGaps(True) - layout.setAutoCreateContainerGaps(True) - - layout.setHorizontalGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup() - .addComponent( - filterLModified, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthBypassModified, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthPotentiallyEnforcedModified, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthEnforcedModified, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - ) - .addGroup(layout.createParallelGroup() - .addComponent( - filterLUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthBypassUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthPotentiallyEnforcedUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthEnforcedUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showDisabledUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - ) - ) - - - layout.setVerticalGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent( - filterLModified, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - filterLUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - ) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addGroup(layout.createSequentialGroup() - .addComponent( - self._extender.showAuthBypassModified, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthPotentiallyEnforcedModified, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthEnforcedModified, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - ) - .addGroup(layout.createSequentialGroup() - .addComponent( - self._extender.showAuthBypassUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthPotentiallyEnforcedUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showAuthEnforcedUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - .addComponent( - self._extender.showDisabledUnauthenticated, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - GroupLayout.PREFERRED_SIZE, - ) - ) - ) - ) + self._extender.showBypassed = JCheckBox(self._extender.BYPASSSED_STR) + self._extender.showBypassed.setSelected(True) + self._extender.showBypassed.addItemListener(TabTableFilter(self._extender)) + + self._extender.showIsEnforced = JCheckBox("Is enforced???") + self._extender.showIsEnforced.setSelected(True) + self._extender.showIsEnforced.addItemListener(TabTableFilter(self._extender)) + + self._extender.showEnforced = JCheckBox(self._extender.ENFORCED_STR) + self._extender.showEnforced.setSelected(True) + self._extender.showEnforced.addItemListener(TabTableFilter(self._extender)) + + self._extender.filterPnl.add(self._extender.showBypassed) + self._extender.filterPnl.add(self._extender.showIsEnforced) + self._extender.filterPnl.add(self._extender.showEnforced) class TabTableFilter(ItemListener): def __init__(self, extender): self._extender = extender def itemStateChanged(self, e): - self._extender.tableSorter.sort() + if hasattr(self._extender, 'tableSorter'): + self._extender.tableSorter.sort() + + if hasattr(self._extender, 'logTable'): + self._extender.logTable.repaint() class TableModel(AbstractTableModel): def __init__(self, extender): @@ -226,48 +72,104 @@ def getRowCount(self): return 0 def getColumnCount(self): - return 8 + base_columns = 6 + user_count = len(self._extender.userTab.user_tabs) if hasattr(self._extender, 'userTab') and self._extender.userTab else 0 + return base_columns + (user_count * 2) def getColumnName(self, columnIndex): - data = ['ID','Method', 'URL', 'Orig. Len', 'Modif. Len', "Unauth. Len", - "Authz. Status", "Unauth. Status"] + base_columns = ['ID', 'Method', 'URL', 'Orig. Len', 'Unauth.len', 'Unauth. Status'] + + if columnIndex < len(base_columns): + return base_columns[columnIndex] + try: - return data[columnIndex] - except IndexError: + if hasattr(self._extender, 'userTab') and self._extender.userTab: + user_index = (columnIndex - len(base_columns)) // 2 + col_type = (columnIndex - len(base_columns)) % 2 + + user_ids = sorted(self._extender.userTab.user_tabs.keys()) + + if user_index < len(user_ids): + user_id = user_ids[user_index] + user_name = self._extender.userTab.user_tabs[user_id]['user_name'] + + col_names = [ + "{} Modif. Len".format(user_name), + "{} Authz. Status".format(user_name) + ] + + return col_names[col_type] + + return "" + + except (IndexError, KeyError): return "" def getColumnClass(self, columnIndex): - data = [Integer, String, String, Integer, Integer, Integer, String, String] + base_classes = [Integer, String, String, Integer, Integer, String] + + if columnIndex < len(base_classes): + return base_classes[columnIndex] + try: - return data[columnIndex] + col_type = (columnIndex - len(base_classes)) % 2 + user_col_classes = [Integer, String] + return user_col_classes[col_type] + except IndexError: return "" def getValueAt(self, rowIndex, columnIndex): logEntry = self._extender._log.get(rowIndex) - if columnIndex == 0: + + if columnIndex == 0: # ID return logEntry._id - if columnIndex == 1: + if columnIndex == 1: # METHOD return logEntry._method - if columnIndex == 2: + if columnIndex == 2: # URL return logEntry._url.toString() - if columnIndex == 3: + if columnIndex == 3: # Original Request Length response = logEntry._originalrequestResponse.getResponse() return len(logEntry._originalrequestResponse.getResponse()) - self._extender._helpers.analyzeResponse(response).getBodyOffset() - if columnIndex == 4: - response = logEntry._requestResponse.getResponse() - return len(logEntry._requestResponse.getResponse()) - self._extender._helpers.analyzeResponse(response).getBodyOffset() - if columnIndex == 5: + if columnIndex == 4: # Unauthorized Request Length if logEntry._unauthorizedRequestResponse is not None: response = logEntry._unauthorizedRequestResponse.getResponse() return len(logEntry._unauthorizedRequestResponse.getResponse()) - self._extender._helpers.analyzeResponse(response).getBodyOffset() else: return 0 - if columnIndex == 6: - return logEntry._enfocementStatus - if columnIndex == 7: - return logEntry._enfocementStatusUnauthorized + if columnIndex == 5: + return logEntry._enfocementStatusUnauthorized + + # User columns + if hasattr(self._extender, 'userTab') and self._extender.userTab and columnIndex >= 6: + user_index = (columnIndex - 6) // 2 + col_type = (columnIndex - 6) % 2 + + user_ids = sorted(self._extender.userTab.user_tabs.keys()) + if user_index < len(user_ids): + user_id = user_ids[user_index] + user_name = self._extender.userTab.user_tabs[user_id]['user_name'] + + user_data = logEntry.get_user_enforcement(user_id) + if user_data: + if col_type == 0: # Modified Length + if user_data['requestResponse'] and user_data['requestResponse'].getResponse(): + response = user_data['requestResponse'].getResponse() + return len(response) - self._extender._helpers.analyzeResponse(response).getBodyOffset() + return 0 + elif col_type == 1: # Authorization Status + return user_data['enforcementStatus'] + return "" + +class ColorConstants: + BLACK = Color.BLACK + WHITE = Color.WHITE + BYPASSED_BG = Color(255, 153, 153) # Light red + IS_ENFORCED_BG = Color(255, 204, 153) # Light orange + ENFORCED_BG = Color(204, 255, 153) # Light green + DISABLED_BG = Color(211, 211, 211) # Light gray + SELECTED_BG = Color(201, 215, 255) class TableSelectionListener(ListSelectionListener): """Class Responsible for the multi-row deletion""" @@ -284,40 +186,129 @@ def __init__(self, extender): self._extender.tableModel = TableModel(extender) self.setModel(self._extender.tableModel) self.addMouseListener(Mouseclick(self._extender)) - self.getColumnModel().getColumn(0).setPreferredWidth(450) self.setRowSelectionAllowed(True) + # Enables multi-row selection self.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + self.updateColumnWidths() + + def updateColumnWidths(self): + if self.getColumnCount() > 0: + column_model = self.getColumnModel() + + widths = [50, 80, 300, 80, 80, 120] + for i, width in enumerate(widths): + column_model.getColumn(i).setPreferredWidth(width) + + if hasattr(self._extender, 'userTab') and self._extender.userTab: + user_count = len(self._extender.userTab.user_tabs) + for i in range(6, 6 + (user_count << 1)): # Bit shift for multiply by 2 + column_model.getColumn(i).setPreferredWidth(100) def prepareRenderer(self, renderer, row, col): comp = JTable.prepareRenderer(self, renderer, row, col) - value = self._extender.tableModel.getValueAt(self._extender.logTable.convertRowIndexToModel(row), col) - if col == 6 or col == 7: - if value == self._extender.BYPASSSED_STR: - comp.setBackground(Color(255, 153, 153)) - comp.setForeground(Color.BLACK) - elif value == self._extender.IS_ENFORCED_STR: - comp.setBackground(Color(255, 204, 153)) - comp.setForeground(Color.BLACK) - elif value == self._extender.ENFORCED_STR: - comp.setBackground(Color(204, 255, 153)) - comp.setForeground(Color.BLACK) - else: - comp.setForeground(Color.BLACK) - comp.setBackground(Color.WHITE) + + if col < 4: + comp.setForeground(ColorConstants.BLACK) + comp.setBackground(ColorConstants.WHITE) + + selected_rows = self._extender.logTable.getSelectedRows() + if row in selected_rows: + comp.setBackground(ColorConstants.SELECTED_BG) + + return comp + + model_row = self._extender.logTable.convertRowIndexToModel(row) + value = self._extender.tableModel.getValueAt(model_row, col) + + comp.setForeground(Color.BLACK) + comp.setBackground(Color.WHITE) - selectedRows = self._extender.logTable.getSelectedRows() - if row in selectedRows: - comp.setBackground(Color(201, 215, 255)) - comp.setForeground(Color.BLACK) + should_mask = False + + if col == 4: # Unauthenticated length + status_value = self._extender.tableModel.getValueAt(model_row, 5) + should_mask = not self.shouldShowStatus(status_value) + elif col == 5: # Unauthenticated status + should_mask = not self.shouldShowStatus(value) + elif col >= 6: # User columns + if col & 1: + should_mask = not self.shouldShowStatus(value) + else: + status_col = col + 1 + if status_col < self._extender.tableModel.getColumnCount(): + status_value = self._extender.tableModel.getValueAt(model_row, status_col) + should_mask = not self.shouldShowStatus(status_value) + + if should_mask: + comp.setText("") + comp.setBackground(Color.WHITE) + comp.setForeground(Color.WHITE) + elif col >= 4: + if col == 5 or (col >= 6 and col & 1): + if value == self._extender.BYPASSSED_STR: + comp.setBackground(ColorConstants.BYPASSED_BG) + elif value == self._extender.IS_ENFORCED_STR: + comp.setBackground(ColorConstants.IS_ENFORCED_BG) + elif value == self._extender.ENFORCED_STR: + comp.setBackground(ColorConstants.ENFORCED_BG) + elif value == "Disabled": + comp.setBackground(ColorConstants.DISABLED_BG) + + comp.setForeground(ColorConstants.BLACK) + + selected_rows = self._extender.logTable.getSelectedRows() + + if row in selected_rows and not should_mask: + comp.setBackground(ColorConstants.SELECTED_BG) + comp.setForeground(ColorConstants.BLACK) return comp + def shouldShowStatus(self, status): + if not hasattr(self._extender, 'showBypassed'): + return True + + if (self._extender.showBypassed.isSelected() and + self._extender.showIsEnforced.isSelected() and + self._extender.showEnforced.isSelected()): + return True + + if status == "Disabled": + return True + elif self._extender.showBypassed.isSelected() and self._extender.BYPASSSED_STR == status: + return True + elif self._extender.showIsEnforced.isSelected() and self._extender.IS_ENFORCED_STR == status: + return True + elif self._extender.showEnforced.isSelected() and self._extender.ENFORCED_STR == status: + return True + + return False + def changeSelection(self, row, col, toggle, extend): - # show the log entry for the selected row logEntry = self._extender._log.get(self._extender.logTable.convertRowIndexToModel(row)) - self._extender._requestViewer.setMessage(logEntry._requestResponse.getRequest(), True) - self._extender._responseViewer.setMessage(logEntry._requestResponse.getResponse(), False) + + current_user_name = "Original" + current_request_response = logEntry._originalrequestResponse + + if col >= 6 and hasattr(self._extender, 'userTab') and self._extender.userTab: + user_index = (col - 6) >> 1 + user_ids = sorted(self._extender.userTab.user_tabs.keys()) + if user_index < len(user_ids): + user_id = user_ids[user_index] + user_name = self._extender.userTab.user_tabs[user_id]['user_name'] + user_data = logEntry.get_user_enforcement(user_id) + if user_data and user_data['requestResponse']: + current_user_name = user_name + current_request_response = user_data['requestResponse'] + + if col >= 4 and col < 6: # Unauthenticated columns + current_request_response = logEntry._unauthorizedRequestResponse + + current_user_name = "Unauthenticated" + + self._extender._requestViewer.setMessage(current_request_response.getRequest(), True) + self._extender._responseViewer.setMessage(current_request_response.getResponse(), False) self._extender._originalrequestViewer.setMessage(logEntry._originalrequestResponse.getRequest(), True) self._extender._originalresponseViewer.setMessage(logEntry._originalrequestResponse.getResponse(), False) @@ -330,37 +321,154 @@ def changeSelection(self, row, col, toggle, extend): self._extender._currentlyDisplayedItem = logEntry - if col == 3: + self.updateTabTitles(current_user_name) + + if col == 2: # URL column collapse(self._extender, self._extender.modified_requests_tabs) collapse(self._extender, self._extender.unauthenticated_requests_tabs) expand(self._extender, self._extender.original_requests_tabs) - elif col == 4 or col == 6: + elif col >= 6: # User columns collapse(self._extender, self._extender.original_requests_tabs) collapse(self._extender, self._extender.unauthenticated_requests_tabs) expand(self._extender, self._extender.modified_requests_tabs) - elif col == 5 or col == 7: + elif col == 4 or col == 5: # Unauth Status collapse(self._extender, self._extender.original_requests_tabs) collapse(self._extender, self._extender.modified_requests_tabs) expand(self._extender, self._extender.unauthenticated_requests_tabs) - else: - collapse(self._extender, self._extender.original_requests_tabs) - collapse(self._extender, self._extender.modified_requests_tabs) - collapse(self._extender, self._extender.unauthenticated_requests_tabs) + + self.updateContextMenuText(col) JTable.changeSelection(self, row, col, toggle, extend) return + def updateContextMenuText(self, col): + modified_text = "Send Modified Request to Repeater" + comparer_text = "Send Responses to Comparer" + + if col >= 6 and hasattr(self._extender, 'userTab') and self._extender.userTab: + user_index = (col - 6) // 2 + user_ids = sorted(self._extender.userTab.user_tabs.keys()) + if user_index < len(user_ids): + user_id = user_ids[user_index] + user_name = self._extender.userTab.user_tabs[user_id]['user_name'] + modified_text = "Send {} Request to Repeater".format(user_name) + comparer_text = "Send {} Responses to Comparer".format(user_name) + elif col == 4 or col == 5: + modified_text = "Send Unauthenticated Request to Repeater" + comparer_text = "Send Unauthenticated Responses to Comparer" + + if hasattr(self._extender, 'sendRequestMenu2'): + self._extender.sendRequestMenu2.setText(modified_text) + if hasattr(self._extender, 'sendResponseMenu'): + self._extender.sendResponseMenu.setText(comparer_text) + + def updateTabTitles(self, user_name): + if hasattr(self._extender, 'modified_requests_tabs'): + self._extender.modified_requests_tabs.setTitleAt(0, "{} Modified Request".format(user_name)) + self._extender.modified_requests_tabs.setTitleAt(1, "{} Modified Response".format(user_name)) + +class TableExtension: + def prepareRenderer(self, renderer, row, col): + comp = JTable.prepareRenderer(self, renderer, row, col) + + if col < 5: + comp.setForeground(ColorConstants.BLACK) + comp.setBackground(ColorConstants.WHITE) + + selected_rows = self._extender.logTable.getSelectedRows() + if row in selected_rows: + comp.setBackground(ColorConstants.SELECTED_BG) + comp.setForeground(ColorConstants.BLACK) + + return comp + + model_row = self._extender.logTable.convertRowIndexToModel(row) + value = self._extender.tableModel.getValueAt(model_row, col) + + comp.setForeground(ColorConstants.BLACK) + comp.setBackground(ColorConstants.WHITE) + should_mask = False + + if col == 5: + should_mask = not self.shouldShowStatus(value) + elif col >= 6: + if col & 1: # Odd columns are status + should_mask = not self.shouldShowStatus(value) + else: # Even columns are length + status_col = col + 1 + if status_col < self._extender.tableModel.getColumnCount(): + status_value = self._extender.tableModel.getValueAt(model_row, status_col) + should_mask = not self.shouldShowStatus(status_value) + + if should_mask: + comp.setText("") + comp.setBackground(ColorConstants.WHITE) + comp.setForeground(ColorConstants.WHITE) + else: + if col & 1 or col == 5: # Status columns + if value == self._extender.BYPASSSED_STR: + comp.setBackground(ColorConstants.BYPASSED_BG) + elif value == self._extender.IS_ENFORCED_STR: + comp.setBackground(ColorConstants.IS_ENFORCED_BG) + elif value == self._extender.ENFORCED_STR: + comp.setBackground(ColorConstants.ENFORCED_BG) + elif value == "Disabled": + comp.setBackground(ColorConstants.DISABLED_BG) + + comp.setForeground(ColorConstants.BLACK) + + selected_rows = self._extender.logTable.getSelectedRows() + if row in selected_rows and (not should_mask or col < 5): + comp.setBackground(ColorConstants.SELECTED_BG) + comp.setForeground(ColorConstants.BLACK) + + return comp + + def shouldShowStatus(self, status): + if not hasattr(self._extender, 'showBypassed'): + return True + + if (self._extender.showBypassed.isSelected() and + self._extender.showIsEnforced.isSelected() and + self._extender.showEnforced.isSelected()): + return True + + if status == "Disabled": + return True + elif self._extender.showBypassed.isSelected() and self._extender.BYPASSSED_STR == status: + return True + elif self._extender.showIsEnforced.isSelected() and self._extender.IS_ENFORCED_STR == status: + return True + elif self._extender.showEnforced.isSelected() and self._extender.ENFORCED_STR == status: + return True + + return False + class LogEntry: - def __init__(self, id, requestResponse, method, url, originalrequestResponse, enforcementStatus, unauthorizedRequestResponse, enforcementStatusUnauthorized): + def __init__(self, id, method, url, originalrequestResponse, unauthorizedRequestResponse, enforcementStatusUnauthorized): self._id = id - self._requestResponse = requestResponse - self._originalrequestResponse = originalrequestResponse self._method = method self._url = url - self._enfocementStatus = enforcementStatus + self._originalrequestResponse = originalrequestResponse self._unauthorizedRequestResponse = unauthorizedRequestResponse - self._enfocementStatusUnauthorized = enforcementStatusUnauthorized - return + self._enfocementStatusUnauthorized = enforcementStatusUnauthorized + + self._userEnforcements = {} + + def add_user_enforcement(self, user_id, requestResponse, enforcementStatus): + self._userEnforcements[user_id] = { + 'requestResponse': requestResponse, + 'enforcementStatus': enforcementStatus + } + + def get_user_enforcement(self, user_id): + return self._userEnforcements.get(user_id, None) + + def get_all_users(self): + return list(self._userEnforcements.keys()) + + def has_user_data(self, user_id): + return user_id in self._userEnforcements class Mouseclick(MouseAdapter): def __init__(self, extender): @@ -375,23 +483,45 @@ def __init__(self, extender): self._extender = extender def include(self, entry): - if self._extender.showAuthBypassModified.isSelected() and self._extender.BYPASSSED_STR == entry.getValue(6): + if (hasattr(self._extender, 'showBypassed') and + self._extender.showBypassed.isSelected() and + self._extender.showIsEnforced.isSelected() and + self._extender.showEnforced.isSelected()): return True - elif self._extender.showAuthPotentiallyEnforcedModified.isSelected() and self._extender.IS_ENFORCED_STR == entry.getValue(6): + + unauth_status = entry.getValue(5) if entry.getValueCount() > 5 else "" + + if self.statusMatchesFilter(unauth_status): return True - elif self._extender.showAuthEnforcedModified.isSelected() and self._extender.ENFORCED_STR == entry.getValue(6): + + if hasattr(self._extender, 'userTab') and self._extender.userTab: + user_count = len(self._extender.userTab.user_tabs) + + for i in range(user_count): + status_col = 7 + (i << 1) + + if status_col < entry.getValueCount(): + user_status = entry.getValue(status_col) + if self.statusMatchesFilter(user_status): + return True + + return False + + def statusMatchesFilter(self, status): + if not hasattr(self._extender, 'showBypassed'): return True - elif self._extender.showAuthBypassUnauthenticated.isSelected() and self._extender.BYPASSSED_STR == entry.getValue(7): + + if self._extender.showBypassed.isSelected() and self._extender.BYPASSSED_STR == status: return True - elif self._extender.showAuthPotentiallyEnforcedUnauthenticated.isSelected() and self._extender.IS_ENFORCED_STR == entry.getValue(7): + elif self._extender.showIsEnforced.isSelected() and self._extender.IS_ENFORCED_STR == status: return True - elif self._extender.showAuthEnforcedUnauthenticated.isSelected() and self._extender.ENFORCED_STR == entry.getValue(7): + elif self._extender.showEnforced.isSelected() and self._extender.ENFORCED_STR == status: return True - elif self._extender.showDisabledUnauthenticated.isSelected() and "Disabled" == entry.getValue(7): + elif status == "Disabled": return True - else: - return False - + + return False + class UpdateTableEDT(Runnable): def __init__(self,extender,action,firstRow,lastRow): self._extender=extender @@ -407,5 +537,4 @@ def run(self): elif self._action == "delete": self._extender.tableModel.fireTableRowsDeleted(self._firstRow, self._lastRow) else: - print("Invalid action in UpdateTableEDT") - + print("Invalid action in UpdateTableEDT") \ No newline at end of file diff --git a/gui/tabs.py b/gui/tabs.py index 2094497..8262943 100644 --- a/gui/tabs.py +++ b/gui/tabs.py @@ -52,16 +52,8 @@ def draw(self): """ self._extender.logTable = Table(self._extender) - - tableWidth = self._extender.logTable.getPreferredSize().width - self._extender.logTable.getColumn("ID").setPreferredWidth(Math.round(tableWidth / 50 * 2)) - self._extender.logTable.getColumn("Method").setPreferredWidth(Math.round(tableWidth / 50 * 3)) - self._extender.logTable.getColumn("URL").setPreferredWidth(Math.round(tableWidth / 50 * 25)) - self._extender.logTable.getColumn("Orig. Len").setPreferredWidth(Math.round(tableWidth / 50 * 4)) - self._extender.logTable.getColumn("Modif. Len").setPreferredWidth(Math.round(tableWidth / 50 * 4)) - self._extender.logTable.getColumn("Unauth. Len").setPreferredWidth(Math.round(tableWidth / 50 * 4)) - self._extender.logTable.getColumn("Authz. Status").setPreferredWidth(Math.round(tableWidth / 50 * 4)) - self._extender.logTable.getColumn("Unauth. Status").setPreferredWidth(Math.round(tableWidth / 50 * 4)) + + self.setupDynamicColumns() self._extender.tableSorter = TableRowSorter(self._extender.tableModel) rowFilter = TableRowFilter(self._extender) @@ -81,8 +73,8 @@ def draw(self): sendRequestMenu = JMenuItem("Send Original Request to Repeater") sendRequestMenu.addActionListener(SendRequestRepeater(self._extender, self._extender._callbacks, True)) - sendRequestMenu2 = JMenuItem("Send Modified Request to Repeater") - sendRequestMenu2.addActionListener(SendRequestRepeater(self._extender, self._extender._callbacks, False)) + self._extender.sendRequestMenu2 = JMenuItem("Send Modified Request to Repeater") + self._extender.sendRequestMenu2.addActionListener(SendRequestRepeater(self._extender, self._extender._callbacks, False)) # Define the key combination for the shortcut try: @@ -109,8 +101,8 @@ def draw(self): actionMap.put("copyToClipBoard", CopySelectedURLToClipBoard(self._extender, self._extender._callbacks)) - sendResponseMenu = JMenuItem("Send Responses to Comparer") - sendResponseMenu.addActionListener(SendResponseComparer(self._extender, self._extender._callbacks)) + self._extender.sendResponseMenu = JMenuItem("Send Responses to Comparer") + self._extender.sendResponseMenu.addActionListener(SendResponseComparer(self._extender, self._extender._callbacks)) retestSelecteditem = JMenuItem("Retest selected request") retestSelecteditem.addActionListener(RetestSelectedRequest(self._extender)) @@ -123,8 +115,8 @@ def draw(self): self._extender.menu = JPopupMenu("Popup") self._extender.menu.add(sendRequestMenu) - self._extender.menu.add(sendRequestMenu2) - self._extender.menu.add(sendResponseMenu) + self._extender.menu.add(self._extender.sendRequestMenu2) + self._extender.menu.add(self._extender.sendResponseMenu) self._extender.menu.add(copyURLitem) self._extender.menu.add(retestSelecteditem) self._extender.menu.add(retestAllitem) @@ -132,6 +124,7 @@ def draw(self): message_editor = MessageEditor(self._extender) self._extender.tabs = JTabbedPane() + self._extender._requestViewer = self._extender._callbacks.createMessageEditor(message_editor, False) self._extender._responseViewer = self._extender._callbacks.createMessageEditor(message_editor, False) @@ -147,7 +140,7 @@ def draw(self): self._extender.original_requests_tabs.addTab("Original Response", self._extender._originalresponseViewer.getComponent()) self._extender.original_requests_tabs.addTab("Expand", None) self._extender.original_requests_tabs.setSelectedIndex(0) - + self._extender.unauthenticated_requests_tabs = JTabbedPane() self._extender.unauthenticated_requests_tabs.addMouseListener(Mouseclick(self._extender)) self._extender.unauthenticated_requests_tabs.addTab("Unauthenticated Request", self._extender._unauthorizedrequestViewer.getComponent()) @@ -174,6 +167,18 @@ def draw(self): self._extender.tabs.setMinimumSize(Dimension(1,1)) self._extender._splitpane.setRightComponent(self._extender.tabs) + self._extender.tabs.addTab("User", self._extender.userPanel) + + def setupDynamicColumns(self): + if hasattr(self._extender, 'tableModel'): + self._extender.tableModel.fireTableStructureChanged() + if hasattr(self._extender, 'logTable'): + self._extender.logTable.updateColumnWidths() + + def refreshTable(self): + if hasattr(self._extender, 'tableModel'): + self._extender.tableModel.fireTableStructureChanged() + self.setupDynamicColumns() class SendRequestRepeater(ActionListener): def __init__(self, extender, callbacks, original): @@ -199,13 +204,36 @@ def __init__(self, extender, callbacks): self._callbacks = callbacks def actionPerformed(self, e): + if not hasattr(self._extender, '_currentlyDisplayedItem') or not self._extender._currentlyDisplayedItem: + return + originalResponse = self._extender._currentlyDisplayedItem._originalrequestResponse - modifiedResponse = self._extender._currentlyDisplayedItem._requestResponse unauthorizedResponse = self._extender._currentlyDisplayedItem._unauthorizedRequestResponse + selected_col = self._extender.logTable.getSelectedColumn() + + if selected_col >= 6 and hasattr(self._extender, 'userTab') and self._extender.userTab: + user_index = (selected_col - 6) >> 1 + user_ids = sorted(self._extender.userTab.user_tabs.keys()) + if user_index < len(user_ids): + user_id = user_ids[user_index] + user_data = self._extender._currentlyDisplayedItem.get_user_enforcement(user_id) + if user_data and user_data['requestResponse']: + modifiedResponse = user_data['requestResponse'] + else: + modifiedResponse = originalResponse + else: + modifiedResponse = originalResponse + elif selected_col == 4 or selected_col == 5: + modifiedResponse = unauthorizedResponse or originalResponse + else: + modifiedResponse = originalResponse + self._callbacks.sendToComparer(originalResponse.getResponse()) - self._callbacks.sendToComparer(modifiedResponse.getResponse()) - self._callbacks.sendToComparer(unauthorizedResponse.getResponse()) + if modifiedResponse: + self._callbacks.sendToComparer(modifiedResponse.getResponse()) + if unauthorizedResponse: + self._callbacks.sendToComparer(unauthorizedResponse.getResponse()) class RetestSelectedRequest(ActionListener): @@ -228,7 +256,6 @@ def __init__(self, extender): self._extender = extender def actionPerformed(self, e): - # Its ready to delete multiple rows at a time once we can figure out how to select multiple row. rows = self._extender.logTable.getSelectedRows() if len(rows) != 0: rows = [self._extender.logTable.convertRowIndexToModel(row) for row in rows] @@ -239,9 +266,10 @@ def __init__(self, extender): self._extender = extender def actionPerformed(self, e): - stringSelection = StringSelection(str(self._extender._helpers.analyzeRequest(self._extender._currentlyDisplayedItem._requestResponse).getUrl())) - clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard() - clpbrd.setContents(stringSelection, None) + if hasattr(self._extender, '_currentlyDisplayedItem') and self._extender._currentlyDisplayedItem: + stringSelection = StringSelection(str(self._extender._helpers.analyzeRequest(self._extender._currentlyDisplayedItem._originalrequestResponse).getUrl())) + clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard() + clpbrd.setContents(stringSelection, None) class AutoScrollListener(AdjustmentListener): def __init__(self, extender): @@ -256,13 +284,21 @@ def __init__(self, extender): self._extender = extender def getHttpService(self): - return self._extender._currentlyDisplayedItem.getHttpService() + return self._extender._currentlyDisplayedItem._originalrequestResponse.getHttpService() def getRequest(self): - return self._extender._currentlyDisplayedItem.getRequest() + if hasattr(self._extender, '_currentUserSelection'): + user_data = self._extender._currentlyDisplayedItem.get_user_enforcement(self._extender._currentUserSelection) + if user_data and user_data['requestResponse']: + return user_data['requestResponse'].getRequest() + return self._extender._currentlyDisplayedItem._originalrequestResponse.getRequest() def getResponse(self): - return self._extender._currentlyDisplayedItem.getResponse() + if hasattr(self._extender, '_currentUserSelection'): + user_data = self._extender._currentlyDisplayedItem.get_user_enforcement(self._extender._currentUserSelection) + if user_data and user_data['requestResponse']: + return user_data['requestResponse'].getResponse() + return self._extender._currentlyDisplayedItem._originalrequestResponse.getResponse() class Mouseclick(MouseAdapter): def __init__(self, extender): @@ -275,21 +311,75 @@ def mouseReleased(self, evt): else: collapse(self._extender, evt.getComponent()) +class SendRequestRepeater(ActionListener): + def __init__(self, extender, callbacks, original): + self._extender = extender + self._callbacks = callbacks + self.original = original + + def actionPerformed(self, e): + if not hasattr(self._extender, '_currentlyDisplayedItem') or not self._extender._currentlyDisplayedItem: + return + + if self.original: + request = self._extender._currentlyDisplayedItem._originalrequestResponse + else: + selected_row = self._extender.logTable.getSelectedRow() + selected_col = self._extender.logTable.getSelectedColumn() + + if selected_col >= 6 and hasattr(self._extender, 'userTab') and self._extender.userTab: + user_index = (selected_col - 6) >> 1 + user_ids = sorted(self._extender.userTab.user_tabs.keys()) + if user_index < len(user_ids): + user_id = user_ids[user_index] + user_data = self._extender._currentlyDisplayedItem.get_user_enforcement(user_id) + if user_data and user_data['requestResponse']: + request = user_data['requestResponse'] + else: + request = self._extender._currentlyDisplayedItem._originalrequestResponse + else: + request = self._extender._currentlyDisplayedItem._originalrequestResponse + elif selected_col == 4 or selected_col == 5: + request = self._extender._currentlyDisplayedItem._unauthorizedRequestResponse or self._extender._currentlyDisplayedItem._originalrequestResponse + else: + request = self._extender._currentlyDisplayedItem._originalrequestResponse + + host = request.getHttpService().getHost() + port = request.getHttpService().getPort() + proto = request.getHttpService().getProtocol() + secure = True if proto == "https" else False + + self._callbacks.sendToRepeater(host, port, secure, request.getRequest(), "Autorize") + class SendRequestToRepeaterAction(AbstractAction): def __init__(self, extender, callbacks): self._extender = extender self._callbacks = callbacks def actionPerformed(self, e): - # Get the selected row of the JTable - row = self._extender.logTable.getSelectedRow() - - # Get the LogEntry object for the selected row - rowModelIndex = self._extender.logTable.convertRowIndexToModel(row) - entry = self._extender.tableModel.getValueAt(rowModelIndex, 0) + if not hasattr(self._extender, '_currentlyDisplayedItem') or not self._extender._currentlyDisplayedItem: + return + + selected_col = self._extender.logTable.getSelectedColumn() + + if selected_col >= 6 and hasattr(self._extender, 'userTab') and self._extender.userTab: + user_index = (selected_col - 6) >> 1 + user_ids = sorted(self._extender.userTab.user_tabs.keys()) + if user_index < len(user_ids): + user_id = user_ids[user_index] + user_data = self._extender._currentlyDisplayedItem.get_user_enforcement(user_id) + if user_data and user_data['requestResponse']: + request = user_data['requestResponse'] + else: + request = self._extender._currentlyDisplayedItem._originalrequestResponse + else: + request = self._extender._currentlyDisplayedItem._originalrequestResponse + elif selected_col == 4 or selected_col == 5: + # Unauthenticated column + request = self._extender._currentlyDisplayedItem._unauthorizedRequestResponse or self._extender._currentlyDisplayedItem._originalrequestResponse + else: + request = self._extender._currentlyDisplayedItem._originalrequestResponse - # Get the modified request - request = self._extender._currentlyDisplayedItem._requestResponse host = request.getHttpService().getHost() port = request.getHttpService().getPort() proto = request.getHttpService().getProtocol() @@ -303,7 +393,7 @@ def __init__(self, extender, callbacks): self._callbacks = callbacks def actionPerformed(self, e): - stringSelection = StringSelection(str(self._extender._helpers.analyzeRequest( - self._extender._currentlyDisplayedItem._requestResponse).getUrl())) - clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard() - clpbrd.setContents(stringSelection, None) \ No newline at end of file + if hasattr(self._extender, '_currentlyDisplayedItem') and self._extender._currentlyDisplayedItem: + stringSelection = StringSelection(str(self._extender._helpers.analyzeRequest(self._extender._currentlyDisplayedItem._originalrequestResponse).getUrl())) + clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard() + clpbrd.setContents(stringSelection, None) \ No newline at end of file diff --git a/gui/user_tab.py b/gui/user_tab.py new file mode 100644 index 0000000..f17e5e9 --- /dev/null +++ b/gui/user_tab.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from javax.swing import JPanel +from javax.swing import JLabel +from javax.swing import JButton +from javax.swing import JTabbedPane +from javax.swing import JOptionPane +from java.awt import BorderLayout +from java.awt import FlowLayout +from java.awt import Font +from java.awt.event import ActionListener + +from gui.enforcement_detector import EnforcementDetectors +from gui.match_replace import MatchReplace + +class UserTab(): + def __init__(self, extender): + self._extender = extender + self.user_count = 0 + self.user_tabs = {} + self.user_names = [] + + def draw(self): + self._extender.userPanel = JPanel(BorderLayout()) + + buttonPanel = JPanel(FlowLayout(FlowLayout.LEFT)) + + self.addUserBtn = JButton("Add User") + self.addUserBtn.addActionListener(AddUserAction(self)) + buttonPanel.add(self.addUserBtn) + + self.removeUserBtn = JButton("Remove User") + self.removeUserBtn.addActionListener(RemoveUserAction(self)) + buttonPanel.add(self.removeUserBtn) + + self.duplicateUserBtn = JButton("Duplicate User") + self.duplicateUserBtn.addActionListener(DuplicateUserAction(self)) + buttonPanel.add(self.duplicateUserBtn) + + self.renameUserBtn = JButton("Rename User") + self.renameUserBtn.addActionListener(RenameUserAction(self)) + buttonPanel.add(self.renameUserBtn) + + self.userTabs = JTabbedPane() + + self.add_user() + + self._extender.userPanel.add(buttonPanel, BorderLayout.NORTH) + self._extender.userPanel.add(self.userTabs, BorderLayout.CENTER) + + def add_user(self): + self.user_count += 1 + user_name = "User {}".format(self.user_count) + unique_user_name = self.get_unique_name(user_name) + + self.user_names.append(unique_user_name) + + userPanel = JPanel(BorderLayout()) + + headerPanel = JPanel(FlowLayout(FlowLayout.LEFT)) + headerLabel = JLabel(unique_user_name) + headerLabel.setFont(Font("Tahoma", Font.BOLD, 12)) + headerPanel.add(headerLabel) + + userSubTabs = JTabbedPane() + + user_ed = UserEnforcementDetector(self.user_count) + user_ed.draw() + + user_mr = UserMatchReplace(self.user_count) + user_mr.draw() + + userSubTabs.addTab("Enforcement Detector", user_ed.EDPnl) + + userSubTabs.addTab("Match/Replace", user_mr.MRPnl) + + userPanel.add(headerPanel, BorderLayout.NORTH) + userPanel.add(userSubTabs, BorderLayout.CENTER) + + self.user_tabs[self.user_count] = { + 'user_id': self.user_count, + 'user_name': unique_user_name, + 'panel': userPanel, + 'subtabs': userSubTabs, + 'ed_instance': user_ed, + 'mr_instance': user_mr, + 'header_label': headerLabel + } + + self.userTabs.addTab(unique_user_name, userPanel) + + self.userTabs.setSelectedIndex(self.userTabs.getTabCount() - 1) + + self.refreshTableStructure() + + def remove_user(self): + if self.userTabs.getTabCount() <= 1: + JOptionPane.showMessageDialog(None, "Cannot remove the last user!", "Warning", JOptionPane.WARNING_MESSAGE) + return + + selected_index = self.userTabs.getSelectedIndex() + + if selected_index >= 0: + selected_panel = self.userTabs.getComponentAt(selected_index) + + user_id_to_remove = None + user_name_to_remove = None + + for user_id, user_data in self.user_tabs.items(): + if user_data['panel'] == selected_panel: + user_id_to_remove = user_id + user_name_to_remove = user_data['user_name'] + break + + if user_id_to_remove and user_name_to_remove: + if user_name_to_remove in self.user_names: + self.user_names.remove(user_name_to_remove) + + del self.user_tabs[user_id_to_remove] + + self.userTabs.removeTabAt(selected_index) + + self.refreshTableStructure() + + def reset_user(self): + self.userTabs.removeAll() + self.user_tabs.clear() + del self.user_names[:] + self.user_count = 0 + self.add_user() + + def duplicate_user(self): + selected_index = self.userTabs.getSelectedIndex() + + if selected_index >= 0: + selected_panel = self.userTabs.getComponentAt(selected_index) + source_user_data = None + + for user_id, user_data in self.user_tabs.items(): + if user_data['panel'] == selected_panel: + source_user_data = user_data + break + + if source_user_data: + self.add_user() + new_user_id = self.user_count + new_user_data = self.user_tabs[new_user_id] + + self.copy_ed_settings(source_user_data['ed_instance'], new_user_data['ed_instance']) + self.copy_mr_settings(source_user_data['mr_instance'], new_user_data['mr_instance']) + + def copy_ed_settings(self, source_ed, target_ed): + target_ed.EDModel.clear() + for i in range(source_ed.EDModel.getSize()): + target_ed.EDModel.addElement(source_ed.EDModel.getElementAt(i)) + + target_ed.EDType.setSelectedIndex(source_ed.EDType.getSelectedIndex()) + target_ed.EDText.setText(source_ed.EDText.getText()) + target_ed.AndOrType.setSelectedIndex(source_ed.AndOrType.getSelectedIndex()) + + def copy_mr_settings(self, source_mr, target_mr): + target_mr.MRModel.clear() + for i in range(source_mr.MRModel.getSize()): + target_mr.MRModel.addElement(source_mr.MRModel.getElementAt(i)) + + target_mr.MRType.setSelectedIndex(source_mr.MRType.getSelectedIndex()) + target_mr.MText.setText(source_mr.MText.getText()) + target_mr.RText.setText(source_mr.RText.getText()) + + target_mr.badProgrammerMRModel.clear() + for key, value in source_mr.badProgrammerMRModel.items(): + if hasattr(value, 'copy'): + target_mr.badProgrammerMRModel[key] = value.copy() + elif isinstance(value, dict): + target_mr.badProgrammerMRModel[key] = dict(value) + else: + target_mr.badProgrammerMRModel[key] = value + + def rename_user(self): + selected_index = self.userTabs.getSelectedIndex() + + if selected_index >= 0: + current_name = self.userTabs.getTitleAt(selected_index) + new_name = JOptionPane.showInputDialog(None, "Enter new name for user:", "Rename User", JOptionPane.QUESTION_MESSAGE, None, None, current_name) + + if new_name and new_name.strip(): + if current_name in self.user_names: + self.user_names.remove(current_name) + + unique_name = self.get_unique_name(new_name.strip()) + self.user_names.append(unique_name) + + self.userTabs.setTitleAt(selected_index, unique_name) + + selected_panel = self.userTabs.getComponentAt(selected_index) + + for user_id, user_data in self.user_tabs.items(): + if user_data['panel'] == selected_panel: + user_data['header_label'].setText(unique_name) + user_data['user_name'] = unique_name + break + + self.refreshTableStructure() + + def get_unique_name(self, name): + if name not in self.user_names: + return name + + counter = 1 + while True: + candidate_name = "{} Copy".format(name) if counter == 1 else "{} Copy {}".format(name, counter) + + if candidate_name not in self.user_names: + return candidate_name + + counter += 1 + + if counter > 100: + return "{} Copy {}".format(name, counter) + + def refreshTableStructure(self): + if hasattr(self._extender, 'tableModel'): + self._extender.tableModel.fireTableStructureChanged() + if hasattr(self._extender, 'logTable'): + self._extender.logTable.updateColumnWidths() + +class UserEnforcementDetector(EnforcementDetectors): + + def __init__(self, user_id): + self.isolated_extender = type('IsolatedExtender', (object,), {})() + self.user_id = user_id + + EnforcementDetectors.__init__(self, self.isolated_extender) + + def draw(self): + EnforcementDetectors.draw(self) + self.EDPnl = self.isolated_extender.EDPnl + self.EDType = self.isolated_extender.EDType + self.EDText = self.isolated_extender.EDText + self.EDModel = self.isolated_extender.EDModel + self.EDList = self.isolated_extender.EDList + self.AndOrType = self.isolated_extender.AndOrType + +class UserMatchReplace(MatchReplace): + def __init__(self, user_id): + self.isolated_extender = type('IsolatedExtender', (object,), {})() + self.user_id = user_id + + MatchReplace.__init__(self, self.isolated_extender) + + def draw(self): + MatchReplace.draw(self) + + self.MRPnl = self.isolated_extender.MRPnl + self.MRType = self.isolated_extender.MRType + self.MText = self.isolated_extender.MText + self.RText = self.isolated_extender.RText + self.MRModel = self.isolated_extender.MRModel + self.MRList = self.isolated_extender.MRList + self.badProgrammerMRModel = self.isolated_extender.badProgrammerMRModel + +class AddUserAction(ActionListener): + def __init__(self, user_tab): + self.user_tab = user_tab + + def actionPerformed(self, event): + self.user_tab.add_user() + +class RemoveUserAction(ActionListener): + def __init__(self, user_tab): + self.user_tab = user_tab + + def actionPerformed(self, event): + self.user_tab.remove_user() + +class DuplicateUserAction(ActionListener): + def __init__(self, user_tab): + self.user_tab = user_tab + + def actionPerformed(self, event): + self.user_tab.duplicate_user() + +class RenameUserAction(ActionListener): + def __init__(self, user_tab): + self.user_tab = user_tab + + def actionPerformed(self, event): + self.user_tab.rename_user() \ No newline at end of file diff --git a/helpers/initiator.py b/helpers/initiator.py index a4d0947..af99e29 100644 --- a/helpers/initiator.py +++ b/helpers/initiator.py @@ -9,6 +9,7 @@ from gui.table import TableFilter from gui.export import Export from gui.menu import MenuImpl +from gui.user_tab import UserTab from java.util import ArrayList from threading import Lock @@ -18,7 +19,7 @@ def __init__(self, extender): self._extender = extender def init_constants(self): - self.contributors = ["Federico Dotta", "mgeeky", "Marcin Woloszyn", "jpginc", "Eric Harris", "Joao Teles"] + self.contributors = ["Federico Dotta", "mgeeky", "Marcin Woloszyn", "jpginc", "Eric Harris", "Joao Teles", "Roy Oswaldha"] self._extender.version = 1.8 self._extender._log = ArrayList() self._extender._lock = Lock() @@ -54,6 +55,11 @@ def draw_all(self): cfg_tab = ConfigurationTab(self._extender) cfg_tab.draw() + user_tab = UserTab(self._extender) + user_tab.draw() + + self._extender.userTab = user_tab + tabs = Tabs(self._extender) tabs.draw()