def new_tool_config(request): if request.method == 'POST': tform = ToolConfigForm(request.POST) if tform.is_valid(): form_copy = tform.save(commit=False) try: tool_type_qs = Tool_Type.objects.filter(name='SonarQube') if form_copy.tool_type in tool_type_qs: sq = SonarQubeAPI(form_copy) project_count = sq.test_connection() # if connection is not successful, this call raise exception messages.add_message(request, messages.SUCCESS, 'SonarQube connection successful. You have access to {} projects'.format(project_count), extra_tags='alert-success') form_copy.save() messages.add_message(request, messages.SUCCESS, 'Tool Configuration Successfully Updated.', extra_tags='alert-success') return HttpResponseRedirect(reverse('tool_config', )) except Exception as e: messages.add_message(request, messages.ERROR, str(e), extra_tags='alert-danger') else: tform = ToolConfigForm() add_breadcrumb(title="New Tool Configuration", top_level=False, request=request) return render(request, 'dojo/new_tool_config.html', {'tform': tform})
def edit_tool_config(request, ttid): tool_config = Tool_Configuration.objects.get(pk=ttid) if request.method == 'POST': tform = ToolConfigForm(request.POST, instance=tool_config) if tform.is_valid(): form_copy = tform.save(commit=False) form_copy.password = dojo_crypto_encrypt( tform.cleaned_data['password']) form_copy.ssh = dojo_crypto_encrypt(tform.cleaned_data['ssh']) try: tool_type_qs_sonarqube = Tool_Type.objects.filter( name='SonarQube') if form_copy.tool_type in tool_type_qs_sonarqube: sq = SonarQubeAPI(form_copy) project_count = sq.test_connection( ) # if connection is not successful, this call raise exception messages.add_message( request, messages.SUCCESS, 'SonarQube connection successful. You have access to {} projects' .format(project_count), extra_tags='alert-success') tool_type_qs_cobaltio = Tool_Type.objects.filter( name='Cobalt.io') if form_copy.tool_type in tool_type_qs_cobaltio: cobalt = CobaltAPI(form_copy) org = cobalt.test_connection( ) # if connection is not successful, this call raise exception messages.add_message( request, messages.SUCCESS, 'Cobalt.io connection successful. You have access to the "{}" org' .format(org["resource"]["name"]), extra_tags='alert-success') form_copy.save() messages.add_message( request, messages.SUCCESS, 'Tool Configuration Successfully Updated.', extra_tags='alert-success') return HttpResponseRedirect(reverse('tool_config', )) except Exception as e: messages.add_message(request, messages.ERROR, str(e), extra_tags='alert-danger') else: tool_config.password = prepare_for_view(tool_config.password) tool_config.ssh = prepare_for_view(tool_config.ssh) tform = ToolConfigForm(instance=tool_config) add_breadcrumb(title="Edit Tool Configuration", top_level=False, request=request) return render(request, 'dojo/edit_tool_config.html', { 'tform': tform, })
def prepare_client(test): product = test.engagement.product if test.api_scan_configuration: config = test.api_scan_configuration # https://github.com/DefectDojo/django-DefectDojo/pull/4676 case no. 7 and 8 # Double check of config if config.product != product: raise ValidationError( 'Product API Scan Configuration and Product do not match.') else: sqqs = product.product_api_scan_configuration_set.filter( product=product, tool_configuration__tool_type__name='SonarQube') if sqqs.count( ) == 1: # https://github.com/DefectDojo/django-DefectDojo/pull/4676 case no. 4 config = sqqs.first() elif sqqs.count( ) > 1: # https://github.com/DefectDojo/django-DefectDojo/pull/4676 case no. 6 raise ValidationError( 'More than one Product API Scan Configuration has been configured, but none of them has been chosen. Please specify which one should be used.' ) else: # https://github.com/DefectDojo/django-DefectDojo/pull/4676 cases no. 1-3 config = None return SonarQubeAPI( tool_config=config.tool_configuration if config else None), config
def update_sonarqube_finding(self, finding): sonarqube_issue = finding.sonarqube_issue if not sonarqube_issue: return logger.debug( "Checking if finding '{}' needs to be updated in SonarQube".format( finding)) product = finding.test.engagement.product config = product.sonarqube_product_set.all().first() client = SonarQubeAPI( tool_config=config.sonarqube_tool_config if config else None) target_status = self.get_sonarqube_status_for(finding) issue = client.get_issue(sonarqube_issue.key) if issue.get('resolution'): current_status = '{} / {}'.format(issue.get('status'), issue.get('resolution')) else: current_status = issue.get('status') logger.debug( "--> SQ Current status: {}. Current target status: {}".format( current_status, target_status)) transitions = self.get_sonarqube_required_transitions_for( current_status, target_status) if transitions: logger.info("Updating finding '{}' in SonarQube".format(finding)) for transition in transitions: client.transition_issue(sonarqube_issue.key, transition) # Track Defect Dojo has updated the SonarQube issue Sonarqube_Issue_Transition.objects.create( sonarqube_issue=finding.sonarqube_issue, # not sure if this is needed, but looks like the original author decided to send display status to sonarcube # we changed Accepted into Risk Accepted, but we change it back to be sure we don't break the integration finding_status=finding.status().replace( 'Risk Accepted', 'Accepted') if finding.status() else finding.status(), sonarqube_status=current_status, transitions=','.join(transitions), )
def update_sonarqube_finding(self, finding): sonarqube_issue = finding.sonarqube_issue if not sonarqube_issue: return logger.debug("Checking if finding '{}' needs to be updated in SonarQube".format(finding)) product = finding.test.engagement.product config = product.sonarqube_product_set.all().first() client = SonarQubeAPI( tool_config=config.sonarqube_tool_config if config else None ) target_status = self.get_sonarqube_status_for(finding) issue = client.get_issue(sonarqube_issue.key) if issue.get('resolution'): current_status = '{} / {}'.format(issue.get('status'), issue.get('resolution')) else: current_status = issue.get('status') logger.debug("--> SQ Current status: {}. Current target status: {}".format(current_status, target_status)) transitions = self.get_sonarqube_required_transitions_for(current_status, target_status) if transitions: logger.info("Updating finding '{}' in SonarQube".format(finding)) for transition in transitions: client.transition_issue(sonarqube_issue.key, transition) # Track Defect Dojo has updated the SonarQube issue Sonarqube_Issue_Transition.objects.create( sonarqube_issue=finding.sonarqube_issue, finding_status=finding.status(), sonarqube_status=current_status, transitions=','.join(transitions), )
def update(self, finding): sonarqube_issue = finding.sonarqube_issue if not sonarqube_issue: return product = finding.test.engagement.product config = product.sonarqube_product_set.all().first() client = SonarQubeAPI( tool_config=config.sonarqube_tool_config if config else None) issue = client.get_issue(sonarqube_issue.key) if issue: # Issue could have disappeared in SQ because a previous scan has resolved the issue as fixed current_status = issue.get('resolution') or issue.get('status') current_finding_status = self.get_sonarqube_status_for(finding) logger.debug( "--> SQ Current status: {}. Finding status: {}".format( current_status, current_finding_status)) if current_status != "OPEN" and current_finding_status != current_status: logger.info( "Original SonarQube issue '{}' has changed. Updating DefectDojo finding '{}'..." .format(sonarqube_issue, finding)) self.update_finding_status(finding, current_status)
def prepare_client(test): product = test.engagement.product if test.sonarqube_config: config = test.sonarqube_config # https://github.com/DefectDojo/django-DefectDojo/pull/4676 case no. 7 and 8 # Double check of config if config.product != product: raise Exception( 'Product SonarQube Configuration and "Product" mismatch') else: sqqs = product.sonarqube_product_set.filter(product=product) if sqqs.count( ) == 1: # https://github.com/DefectDojo/django-DefectDojo/pull/4676 case no. 4 config = sqqs.first() elif sqqs.count( ) > 1: # https://github.com/DefectDojo/django-DefectDojo/pull/4676 case no. 6 raise Exception( 'It has configured more than one Product SonarQube Configuration but non of them has been choosen.\n' 'Please specify at Test which one should be used.') else: # https://github.com/DefectDojo/django-DefectDojo/pull/4676 cases no. 1-3 config = None return SonarQubeAPI(tool_config=config. sonarqube_tool_config if config else None), config
def import_issues(self, test): items = list() try: product = test.engagement.product config = product.sonarqube_product_set.all().first() client = SonarQubeAPI( tool_config=config.sonarqube_tool_config if config else None) if config and config.sonarqube_project_key: component = client.get_project(config.sonarqube_project_key) else: component = client.find_project(product.name) issues = client.find_issues(component['key']) logging.info('Found {} issues for component {}'.format( len(issues), component["key"])) for issue in issues: status = issue['status'] from_hotspot = issue.get('fromHotspot', False) if self.is_closed(status) or from_hotspot: continue type = issue['type'] title = issue['message'] component_key = issue['component'] line = issue.get('line') rule_id = issue['rule'] rule = client.get_rule(rule_id) severity = self.convert_sonar_severity(rule['severity']) description = self.clean_rule_description_html( rule['htmlDesc']) cwe = self.clean_cwe(rule['htmlDesc']) references = self.get_references(rule['htmlDesc']) sonarqube_issue, _ = Sonarqube_Issue.objects.update_or_create( key=issue['key'], defaults={ 'status': status, 'type': type, }) # Only assign the SonarQube_issue to the first finding related to the issue if Finding.objects.filter( sonarqube_issue=sonarqube_issue).exists(): sonarqube_issue = None find = Finding( title=title, cwe=cwe, description=description, test=test, severity=severity, references=references, file_path=component_key, line=line, active=True, verified=self.is_confirmed(status), false_p=False, duplicate=False, out_of_scope=False, mitigated=None, mitigation='No mitigation provided', impact="No impact provided", numerical_severity=Finding.get_numerical_severity( severity), static_finding=True, sonarqube_issue=sonarqube_issue, ) items.append(find) except Exception as e: logger.exception(e) create_notification(event='other', title='SonarQube API import issue', description=e, icon='exclamation-triangle', source='SonarQube API') return items