def update(self, instance, validated_data): # remove tags from validated data and store them seperately to_be_tagged, validated_data = self._pop_tags(validated_data) # pop push_to_jira so it won't get send to the model as a field # TODO: JIRA can we remove this is_push_all_issues, already checked in apiv2 viewset? push_to_jira = validated_data.pop( 'push_to_jira') or jira_helper.is_push_all_issues(instance) instance = super(TaggitSerializer, self).update(instance, validated_data) # If we need to push to JIRA, an extra save call is needed. # Also if we need to update the mitigation date of the finding. # TODO try to combine create and save, but for now I'm just fixing a bug and don't want to change to much if push_to_jira: instance.save(push_to_jira=push_to_jira) # not sure why we are returning a tag_object, but don't want to change too much now as we're just fixing a bug tag_object = self._save_tags(instance, to_be_tagged) return tag_object
def create(self, validated_data): # remove tags from validated data and store them seperately to_be_tagged, validated_data = self._pop_tags(validated_data) # pop push_to_jira so it won't get send to the model as a field push_to_jira = validated_data.pop('push_to_jira') # first save, so we have an instance to get push_all_to_jira from new_finding = super(TaggitSerializer, self).create(validated_data) # TODO: JIRA can we remove this is_push_all_issues, already checked in apiv2 viewset? push_to_jira = push_to_jira or jira_helper.is_push_all_issues( new_finding) # If we need to push to JIRA, an extra save call is needed. # TODO try to combine create and save, but for now I'm just fixing a bug and don't want to change to much if push_to_jira or new_finding: new_finding.save(push_to_jira=push_to_jira) # not sure why we are returning a tag_object, but don't want to change too much now as we're just fixing a bug tag_object = self._save_tags(new_finding, to_be_tagged) return tag_object
def import_scan_results(request, eid=None, pid=None): engagement = None form = ImportScanForm() cred_form = CredMappingForm() finding_count = 0 jform = None user = request.user if eid: engagement = get_object_or_404(Engagement, id=eid) engagement_or_product = engagement cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter(engagement=engagement).order_by('cred_id') elif pid: product = get_object_or_404(Product, id=pid) engagement_or_product = product elif not user.is_staff: raise PermissionDenied user_has_permission_or_403(user, engagement_or_product, Permissions.Import_Scan_Result) push_all_jira_issues = jira_helper.is_push_all_issues(engagement_or_product) if request.method == "POST": form = ImportScanForm(request.POST, request.FILES) cred_form = CredMappingForm(request.POST) cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter( engagement=engagement).order_by('cred_id') if jira_helper.get_jira_project(engagement_or_product): jform = JIRAImportScanForm(request.POST, push_all=push_all_jira_issues, prefix='jiraform') logger.debug('jform valid: %s', jform.is_valid()) logger.debug('jform errors: %s', jform.errors) if form.is_valid() and (jform is None or jform.is_valid()): scan = request.FILES.get('file', None) scan_date = form.cleaned_data['scan_date'] minimum_severity = form.cleaned_data['minimum_severity'] active = form.cleaned_data['active'] verified = form.cleaned_data['verified'] scan_type = request.POST['scan_type'] tags = form.cleaned_data['tags'] version = form.cleaned_data['version'] branch_tag = form.cleaned_data.get('branch_tag', None) build_id = form.cleaned_data.get('build_id', None) commit_hash = form.cleaned_data.get('commit_hash', None) api_scan_configuration = form.cleaned_data.get('api_scan_configuration', None) service = form.cleaned_data.get('service', None) close_old_findings = form.cleaned_data.get('close_old_findings', None) # Will save in the provided environment or in the `Development` one if absent environment_id = request.POST.get('environment', 'Development') environment = Development_Environment.objects.get(id=environment_id) group_by = form.cleaned_data.get('group_by', None) # TODO move to form validation? if scan and is_scan_file_too_large(scan): messages.add_message(request, messages.ERROR, "Report file is too large. Maximum supported size is {} MB".format(settings.SCAN_FILE_MAX_SIZE), extra_tags='alert-danger') return HttpResponseRedirect(reverse('import_scan_results', args=(engagement,))) # Allows for a test to be imported with an engagement created on the fly if engagement is None: engagement = Engagement() engagement.name = "AdHoc Import - " + strftime("%a, %d %b %Y %X", timezone.now().timetuple()) engagement.threat_model = False engagement.api_test = False engagement.pen_test = False engagement.check_list = False engagement.target_start = timezone.now().date() engagement.target_end = timezone.now().date() engagement.product = product engagement.active = True engagement.status = 'In Progress' engagement.version = version engagement.branch_tag = branch_tag engagement.build_id = build_id engagement.commit_hash = commit_hash engagement.save() # can't use helper as when push_all_jira_issues is True, the checkbox gets disabled and is always false # push_to_jira = jira_helper.is_push_to_jira(new_finding, jform.cleaned_data.get('push_to_jira')) push_to_jira = push_all_jira_issues or (jform and jform.cleaned_data.get('push_to_jira')) error = False # Save newly added endpoints added_endpoints = save_endpoints_to_add(form.endpoints_to_add_list, engagement.product) try: importer = Importer() test, finding_count, closed_finding_count = importer.import_scan(scan, scan_type, engagement, user, environment, active=active, verified=verified, tags=tags, minimum_severity=minimum_severity, endpoints_to_add=list(form.cleaned_data['endpoints']) + added_endpoints, scan_date=scan_date, version=version, branch_tag=branch_tag, build_id=build_id, commit_hash=commit_hash, push_to_jira=push_to_jira, close_old_findings=close_old_findings, group_by=group_by, api_scan_configuration=api_scan_configuration, service=service) message = f'{scan_type} processed a total of {finding_count} findings' if close_old_findings: message = message + ' and closed %d findings' % (closed_finding_count) message = message + "." add_success_message_to_response(message) except Exception as e: logger.exception(e) add_error_message_to_response('An exception error occurred during the report import:%s' % str(e)) error = True # Save the credential to the test if cred_form.is_valid(): if cred_form.cleaned_data['cred_user']: # Select the credential mapping object from the selected list and only allow if the credential is associated with the product cred_user = Cred_Mapping.objects.filter( pk=cred_form.cleaned_data['cred_user'].id, engagement=eid).first() new_f = cred_form.save(commit=False) new_f.test = test new_f.cred_id = cred_user.cred_id new_f.save() if not error: return HttpResponseRedirect( reverse('view_test', args=(test.id, ))) prod_id = None custom_breadcrumb = None title = "Import Scan Results" if engagement: prod_id = engagement.product.id product_tab = Product_Tab(prod_id, title=title, tab="engagements") product_tab.setEngagement(engagement) else: prod_id = pid custom_breadcrumb = {"", ""} product_tab = Product_Tab(prod_id, title=title, tab="findings") if jira_helper.get_jira_project(engagement_or_product): jform = JIRAImportScanForm(push_all=push_all_jira_issues, prefix='jiraform') form.fields['endpoints'].queryset = Endpoint.objects.filter(product__id=product_tab.product.id) form.fields['api_scan_configuration'].queryset = Product_API_Scan_Configuration.objects.filter(product__id=product_tab.product.id) return render(request, 'dojo/import_scan_results.html', {'form': form, 'product_tab': product_tab, 'engagement_or_product': engagement_or_product, 'custom_breadcrumb': custom_breadcrumb, 'title': title, 'cred_form': cred_form, 'jform': jform, 'scan_types': get_scan_types_sorted(), })
def re_import_scan_results(request, tid): additional_message = "When re-uploading a scan, any findings not found in original scan will be updated as " \ "mitigated. The process attempts to identify the differences, however manual verification " \ "is highly recommended." test = get_object_or_404(Test, id=tid) # by default we keep a trace of the scan_type used to create the test # if it's not here, we use the "name" of the test type # this feature exists to provide custom label for tests for some parsers if test.scan_type: scan_type = test.scan_type else: scan_type = test.test_type.name engagement = test.engagement form = ReImportScanForm(test=test) jform = None jira_project = jira_helper.get_jira_project(test) push_all_jira_issues = jira_helper.is_push_all_issues(test) # Decide if we need to present the Push to JIRA form if get_system_setting('enable_jira') and jira_project: jform = JIRAImportScanForm(push_all=push_all_jira_issues, prefix='jiraform') if request.method == "POST": form = ReImportScanForm(request.POST, request.FILES, test=test) if jira_project: jform = JIRAImportScanForm(request.POST, push_all=push_all_jira_issues, prefix='jiraform') if form.is_valid() and (jform is None or jform.is_valid()): scan_date = form.cleaned_data['scan_date'] minimum_severity = form.cleaned_data['minimum_severity'] scan = request.FILES.get('file', None) active = form.cleaned_data['active'] verified = form.cleaned_data['verified'] tags = form.cleaned_data['tags'] version = form.cleaned_data.get('version', None) branch_tag = form.cleaned_data.get('branch_tag', None) build_id = form.cleaned_data.get('build_id', None) commit_hash = form.cleaned_data.get('commit_hash', None) api_scan_configuration = form.cleaned_data.get( 'api_scan_configuration', None) service = form.cleaned_data.get('service', None) endpoints_to_add = None # not available on reimport UI close_old_findings = form.cleaned_data.get('close_old_findings', True) group_by = form.cleaned_data.get('group_by', None) # Tags are replaced, same behaviour as with django-tagging test.tags = tags test.version = version if scan and is_scan_file_too_large(scan): messages.add_message( request, messages.ERROR, "Report file is too large. Maximum supported size is {} MB" .format(settings.SCAN_FILE_MAX_SIZE), extra_tags='alert-danger') return HttpResponseRedirect( reverse('re_import_scan_results', args=(test.id, ))) push_to_jira = push_all_jira_issues or ( jform and jform.cleaned_data.get('push_to_jira')) error = False finding_count, new_finding_count, closed_finding_count, reactivated_finding_count, untouched_finding_count = 0, 0, 0, 0, 0 reimporter = ReImporter() try: test, finding_count, new_finding_count, closed_finding_count, reactivated_finding_count, untouched_finding_count, _ = \ reimporter.reimport_scan(scan, scan_type, test, active=active, verified=verified, tags=None, minimum_severity=minimum_severity, endpoints_to_add=endpoints_to_add, scan_date=scan_date, version=version, branch_tag=branch_tag, build_id=build_id, commit_hash=commit_hash, push_to_jira=push_to_jira, close_old_findings=close_old_findings, group_by=group_by, api_scan_configuration=api_scan_configuration, service=service) except Exception as e: logger.exception(e) add_error_message_to_response( 'An exception error occurred during the report import:%s' % str(e)) error = True if not error: message = construct_imported_message( scan_type, finding_count, new_finding_count=new_finding_count, closed_finding_count=closed_finding_count, reactivated_finding_count=reactivated_finding_count, untouched_finding_count=untouched_finding_count) add_success_message_to_response(message) return HttpResponseRedirect(reverse('view_test', args=(test.id, ))) product_tab = Product_Tab(engagement.product.id, title="Re-upload a %s" % scan_type, tab="engagements") product_tab.setEngagement(engagement) form.fields['endpoints'].queryset = Endpoint.objects.filter( product__id=product_tab.product.id) form.initial['api_scan_configuration'] = test.api_scan_configuration form.fields[ 'api_scan_configuration'].queryset = Product_API_Scan_Configuration.objects.filter( product__id=product_tab.product.id) return render( request, 'dojo/import_scan_results.html', { 'form': form, 'product_tab': product_tab, 'eid': engagement.id, 'additional_message': additional_message, 'jform': jform, 'scan_types': get_scan_types_sorted(), })
def add_temp_finding(request, tid, fid): jform = None test = get_object_or_404(Test, id=tid) finding = get_object_or_404(Finding_Template, id=fid) findings = Finding_Template.objects.all() push_all_jira_issues = jira_helper.is_push_all_issues(finding) if request.method == 'POST': form = AddFindingForm(request.POST, req_resp=None, product=test.engagement.product) if jira_helper.get_jira_project(test): jform = JIRAFindingForm( push_all=jira_helper.is_push_all_issues(test), prefix='jiraform', jira_project=jira_helper.get_jira_project(test), finding_form=form) logger.debug('jform valid: %s', jform.is_valid()) if (form['active'].value() is False or form['false_p'].value() ) and form['duplicate'].value() is False: closing_disabled = Note_Type.objects.filter( is_mandatory=True, is_active=True).count() if closing_disabled != 0: error_inactive = ValidationError( 'Can not set a finding as inactive without adding all mandatory notes', code='not_active_or_false_p_true') error_false_p = ValidationError( 'Can not set a finding as false positive without adding all mandatory notes', code='not_active_or_false_p_true') if form['active'].value() is False: form.add_error('active', error_inactive) if form['false_p'].value(): form.add_error('false_p', error_false_p) messages.add_message( request, messages.ERROR, 'Can not set a finding as inactive or false positive without adding all mandatory notes', extra_tags='alert-danger') if form.is_valid(): finding.last_used = timezone.now() finding.save() new_finding = form.save(commit=False) new_finding.test = test new_finding.reporter = request.user new_finding.numerical_severity = Finding.get_numerical_severity( new_finding.severity) new_finding.date = form.cleaned_data['date'] or datetime.today() finding_helper.update_finding_status(new_finding, request.user) new_finding.save(dedupe_option=False, false_history=False) # Save and add new endpoints finding_helper.add_endpoints(new_finding, form) new_finding.save(false_history=True) if 'jiraform-push_to_jira' in request.POST: jform = JIRAFindingForm( request.POST, prefix='jiraform', instance=new_finding, push_all=push_all_jira_issues, jira_project=jira_helper.get_jira_project(test), finding_form=form) if jform.is_valid(): if jform.cleaned_data.get('push_to_jira'): jira_helper.push_to_jira(new_finding) else: add_error_message_to_response( 'jira form validation failed: %s' % jform.errors) messages.add_message(request, messages.SUCCESS, 'Finding from template added successfully.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_test', args=(test.id, ))) else: messages.add_message( request, messages.ERROR, 'The form has errors, please correct them below.', extra_tags='alert-danger') else: form = AddFindingForm(req_resp=None, product=test.engagement.product, initial={ 'active': False, 'date': timezone.now().date(), 'verified': False, 'false_p': False, 'duplicate': False, 'out_of_scope': False, 'title': finding.title, 'description': finding.description, 'cwe': finding.cwe, 'severity': finding.severity, 'mitigation': finding.mitigation, 'impact': finding.impact, 'references': finding.references, 'numerical_severity': finding.numerical_severity }) if jira_helper.get_jira_project(test): jform = JIRAFindingForm( push_all=jira_helper.is_push_all_issues(test), prefix='jiraform', jira_project=jira_helper.get_jira_project(test), finding_form=form) # logger.debug('form valid: %s', form.is_valid()) # logger.debug('jform valid: %s', jform.is_valid()) # logger.debug('form errors: %s', form.errors) # logger.debug('jform errors: %s', jform.errors) # logger.debug('jform errors: %s', vars(jform)) product_tab = Product_Tab(test.engagement.product.id, title="Add Finding", tab="engagements") product_tab.setEngagement(test.engagement) return render( request, 'dojo/add_findings.html', { 'form': form, 'product_tab': product_tab, 'jform': jform, 'findings': findings, 'temp': True, 'fid': finding.id, 'tid': test.id, 'test': test, })
def add_findings(request, tid): test = Test.objects.get(id=tid) form_error = False jform = None form = AddFindingForm(initial={'date': timezone.now().date()}, req_resp=None, product=test.engagement.product) push_all_jira_issues = jira_helper.is_push_all_issues(test) use_jira = jira_helper.get_jira_project(test) is not None if request.method == 'POST': form = AddFindingForm(request.POST, req_resp=None, product=test.engagement.product) if (form['active'].value() is False or form['false_p'].value() ) and form['duplicate'].value() is False: closing_disabled = Note_Type.objects.filter( is_mandatory=True, is_active=True).count() if closing_disabled != 0: error_inactive = ValidationError( 'Can not set a finding as inactive without adding all mandatory notes', code='inactive_without_mandatory_notes') error_false_p = ValidationError( 'Can not set a finding as false positive without adding all mandatory notes', code='false_p_without_mandatory_notes') if form['active'].value() is False: form.add_error('active', error_inactive) if form['false_p'].value(): form.add_error('false_p', error_false_p) messages.add_message( request, messages.ERROR, 'Can not set a finding as inactive or false positive without adding all mandatory notes', extra_tags='alert-danger') if use_jira: jform = JIRAFindingForm( request.POST, prefix='jiraform', push_all=push_all_jira_issues, jira_project=jira_helper.get_jira_project(test), finding_form=form) if form.is_valid() and (jform is None or jform.is_valid()): if jform: logger.debug('jform.jira_issue: %s', jform.cleaned_data.get('jira_issue')) logger.debug('jform.push_to_jira: %s', jform.cleaned_data.get('push_to_jira')) new_finding = form.save(commit=False) new_finding.test = test new_finding.reporter = request.user new_finding.numerical_severity = Finding.get_numerical_severity( new_finding.severity) new_finding.tags = form.cleaned_data['tags'] new_finding.save(dedupe_option=False, push_to_jira=False) # Save and add new endpoints finding_helper.add_endpoints(new_finding, form) # Push to jira? push_to_jira = False jira_message = None if jform and jform.is_valid(): # can't use helper as when push_all_jira_issues is True, the checkbox gets disabled and is always false # push_to_jira = jira_helper.is_push_to_jira(new_finding, jform.cleaned_data.get('push_to_jira')) push_to_jira = push_all_jira_issues or jform.cleaned_data.get( 'push_to_jira') # if the jira issue key was changed, update database new_jira_issue_key = jform.cleaned_data.get('jira_issue') if new_finding.has_jira_issue: jira_issue = new_finding.jira_issue # everything in DD around JIRA integration is based on the internal id of the issue in JIRA # instead of on the public jira issue key. # I have no idea why, but it means we have to retrieve the issue from JIRA to get the internal JIRA id. # we can assume the issue exist, which is already checked in the validation of the jform if not new_jira_issue_key: jira_helper.finding_unlink_jira(request, new_finding) jira_message = 'Link to JIRA issue removed successfully.' elif new_jira_issue_key != new_finding.jira_issue.jira_key: jira_helper.finding_unlink_jira(request, new_finding) jira_helper.finding_link_jira(request, new_finding, new_jira_issue_key) jira_message = 'Changed JIRA link successfully.' else: logger.debug('finding has no jira issue yet') if new_jira_issue_key: logger.debug( 'finding has no jira issue yet, but jira issue specified in request. trying to link.' ) jira_helper.finding_link_jira(request, new_finding, new_jira_issue_key) jira_message = 'Linked a JIRA issue successfully.' new_finding.save(false_history=True, push_to_jira=push_to_jira) create_notification(event='other', title='Addition of %s' % new_finding.title, finding=new_finding, description='Finding "%s" was added by %s' % (new_finding.title, request.user), url=request.build_absolute_uri( reverse('view_finding', args=(new_finding.id, ))), icon="exclamation-triangle") if 'request' in form.cleaned_data or 'response' in form.cleaned_data: burp_rr = BurpRawRequestResponse( finding=new_finding, burpRequestBase64=base64.b64encode( form.cleaned_data['request'].encode()), burpResponseBase64=base64.b64encode( form.cleaned_data['response'].encode()), ) burp_rr.clean() burp_rr.save() if '_Finished' in request.POST: return HttpResponseRedirect( reverse('view_test', args=(test.id, ))) else: return HttpResponseRedirect( reverse('add_findings', args=(test.id, ))) else: form_error = True add_error_message_to_response( 'The form has errors, please correct them below.') add_field_errors_to_response(jform) add_field_errors_to_response(form) else: if use_jira: jform = JIRAFindingForm( push_all=jira_helper.is_push_all_issues(test), prefix='jiraform', jira_project=jira_helper.get_jira_project(test), finding_form=form) product_tab = Product_Tab(test.engagement.product.id, title="Add Finding", tab="engagements") product_tab.setEngagement(test.engagement) return render( request, 'dojo/add_findings.html', { 'form': form, 'product_tab': product_tab, 'test': test, 'temp': False, 'tid': tid, 'form_error': form_error, 'jform': jform, })
def import_scan_results(request, eid=None, pid=None): engagement = None form = ImportScanForm() cred_form = CredMappingForm() finding_count = 0 jform = None user = request.user if eid: engagement = get_object_or_404(Engagement, id=eid) engagement_or_product = engagement cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter( engagement=engagement).order_by('cred_id') elif pid: product = get_object_or_404(Product, id=pid) engagement_or_product = product elif not user.is_staff: raise PermissionDenied if not user_is_authorized(user, 'staff', engagement_or_product): raise PermissionDenied push_all_jira_issues = jira_helper.is_push_all_issues( engagement_or_product) if request.method == "POST": form = ImportScanForm(request.POST, request.FILES) cred_form = CredMappingForm(request.POST) cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter( engagement=engagement).order_by('cred_id') if jira_helper.get_jira_project(engagement_or_product): jform = JIRAImportScanForm(request.POST, push_all=push_all_jira_issues, prefix='jiraform') logger.debug('jform valid: %s', jform.is_valid()) logger.debug('jform errors: %s', jform.errors) if form.is_valid() and (jform is None or jform.is_valid()): # Allows for a test to be imported with an engagement created on the fly if engagement is None: engagement = Engagement() # product = get_object_or_404(Product, id=pid) engagement.name = "AdHoc Import - " + strftime( "%a, %d %b %Y %X", timezone.now().timetuple()) engagement.threat_model = False engagement.api_test = False engagement.pen_test = False engagement.check_list = False engagement.target_start = timezone.now().date() engagement.target_end = timezone.now().date() engagement.product = product engagement.active = True engagement.status = 'In Progress' engagement.save() file = request.FILES.get('file', None) scan_date = form.cleaned_data['scan_date'] min_sev = form.cleaned_data['minimum_severity'] active = form.cleaned_data['active'] verified = form.cleaned_data['verified'] scan_type = request.POST['scan_type'] tags = form.cleaned_data['tags'] if not any(scan_type in code for code in ImportScanForm.SCAN_TYPE_CHOICES): raise Http404() if file and is_scan_file_too_large(file): messages.add_message( request, messages.ERROR, "Report file is too large. Maximum supported size is {} MB" .format(settings.SCAN_FILE_MAX_SIZE), extra_tags='alert-danger') return HttpResponseRedirect( reverse('import_scan_results', args=(engagement, ))) tt, t_created = Test_Type.objects.get_or_create(name=scan_type) # will save in development environment environment, env_created = Development_Environment.objects.get_or_create( name="Development") t = Test(engagement=engagement, test_type=tt, target_start=scan_date, target_end=scan_date, environment=environment, percent_complete=100, tags=tags) t.lead = user t.full_clean() t.save() # Save the credential to the test if cred_form.is_valid(): if cred_form.cleaned_data['cred_user']: # Select the credential mapping object from the selected list and only allow if the credential is associated with the product cred_user = Cred_Mapping.objects.filter( pk=cred_form.cleaned_data['cred_user'].id, engagement=eid).first() new_f = cred_form.save(commit=False) new_f.test = t new_f.cred_id = cred_user.cred_id new_f.save() try: parser = import_parser_factory(file, t, active, verified) except Exception as e: messages.add_message( request, messages.ERROR, "An error has occurred in the parser, please see error " "log for details.", extra_tags='alert-danger') parse_logger.exception(e) parse_logger.error("Error in parser: {}".format(str(e))) return HttpResponseRedirect( reverse('import_scan_results', args=(engagement.id, ))) try: # can't use helper as when push_all_jira_issues is True, the checkbox gets disabled and is always false # push_to_jira = jira_helper.is_push_to_jira(new_finding, jform.cleaned_data.get('push_to_jira')) push_to_jira = push_all_jira_issues or ( jform and jform.cleaned_data.get('push_to_jira')) for item in parser.items: # print("item blowup") # print(item) sev = item.severity if sev == 'Information' or sev == 'Informational': sev = 'Info' item.severity = sev if Finding.SEVERITIES[sev] > Finding.SEVERITIES[min_sev]: continue item.test = t item.reporter = user item.last_reviewed = timezone.now() item.last_reviewed_by = user if not handles_active_verified_statuses( form.get_scan_type()): item.active = active item.verified = verified item.save(dedupe_option=False, false_history=True) if hasattr(item, 'unsaved_req_resp') and len( item.unsaved_req_resp) > 0: for req_resp in item.unsaved_req_resp: if form.get_scan_type() == "Arachni Scan": burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=req_resp["req"], burpResponseBase64=req_resp["resp"], ) else: burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=base64.b64encode( req_resp["req"].encode("utf-8")), burpResponseBase64=base64.b64encode( req_resp["resp"].encode("utf-8")), ) burp_rr.clean() burp_rr.save() if item.unsaved_request is not None and item.unsaved_response is not None: burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=base64.b64encode( item.unsaved_request.encode()), burpResponseBase64=base64.b64encode( item.unsaved_response.encode()), ) burp_rr.clean() burp_rr.save() for endpoint in item.unsaved_endpoints: ep, created = Endpoint.objects.get_or_create( protocol=endpoint.protocol, host=endpoint.host, path=endpoint.path, query=endpoint.query, fragment=endpoint.fragment, product=t.engagement.product) eps, created = Endpoint_Status.objects.get_or_create( finding=item, endpoint=ep) ep.endpoint_status.add(eps) item.endpoints.add(ep) item.endpoint_status.add(eps) for endpoint in form.cleaned_data['endpoints']: ep, created = Endpoint.objects.get_or_create( protocol=endpoint.protocol, host=endpoint.host, path=endpoint.path, query=endpoint.query, fragment=endpoint.fragment, product=t.engagement.product) eps, created = Endpoint_Status.objects.get_or_create( finding=item, endpoint=ep) ep.endpoint_status.add(eps) item.endpoints.add(ep) item.endpoint_status.add(eps) item.save(false_history=True, push_to_jira=push_to_jira) if item.unsaved_tags is not None: item.tags = item.unsaved_tags finding_count += 1 messages.add_message( request, messages.SUCCESS, scan_type + ' processed, a total of ' + message(finding_count, 'finding', 'processed'), extra_tags='alert-success') create_notification(event='scan_added', title=str(finding_count) + " findings for " + engagement.product.name, finding_count=finding_count, test=t, engagement=engagement, url=reverse('view_test', args=(t.id, ))) return HttpResponseRedirect(reverse('view_test', args=(t.id, ))) except SyntaxError: messages.add_message( request, messages.ERROR, 'There appears to be an error in the XML report, please check and try again.', extra_tags='alert-danger') prod_id = None custom_breadcrumb = None title = "Import Scan Results" if engagement: prod_id = engagement.product.id product_tab = Product_Tab(prod_id, title=title, tab="engagements") product_tab.setEngagement(engagement) else: prod_id = pid custom_breadcrumb = {"", ""} product_tab = Product_Tab(prod_id, title=title, tab="findings") if jira_helper.get_jira_project(engagement_or_product): jform = JIRAImportScanForm(push_all=push_all_jira_issues, prefix='jiraform') form.fields['endpoints'].queryset = Endpoint.objects.filter( product__id=product_tab.product.id) return render( request, 'dojo/import_scan_results.html', { 'form': form, 'product_tab': product_tab, 'engagement_or_product': engagement_or_product, 'custom_breadcrumb': custom_breadcrumb, 'title': title, 'cred_form': cred_form, 'jform': jform })
def re_import_scan_results(request, tid): additional_message = "When re-uploading a scan, any findings not found in original scan will be updated as " \ "mitigated. The process attempts to identify the differences, however manual verification " \ "is highly recommended." test = get_object_or_404(Test, id=tid) scan_type = test.test_type.name engagement = test.engagement form = ReImportScanForm() jform = None jira_project = jira_helper.get_jira_project(test) push_all_jira_issues = jira_helper.is_push_all_issues(test) # Decide if we need to present the Push to JIRA form if get_system_setting('enable_jira') and jira_project: jform = JIRAImportScanForm(push_all=push_all_jira_issues, prefix='jiraform') form.initial['tags'] = [tag.name for tag in test.tags] if request.method == "POST": form = ReImportScanForm(request.POST, request.FILES) if jira_project: jform = JIRAImportScanForm(request.POST, push_all=push_all_jira_issues, prefix='jiraform') if form.is_valid() and (jform is None or jform.is_valid()): scan_date = form.cleaned_data['scan_date'] scan_date_time = datetime.combine(scan_date, timezone.now().time()) if settings.USE_TZ: scan_date_time = timezone.make_aware( scan_date_time, timezone.get_default_timezone()) min_sev = form.cleaned_data['minimum_severity'] file = request.FILES.get('file', None) scan_type = test.test_type.name active = form.cleaned_data['active'] verified = form.cleaned_data['verified'] tags = request.POST.getlist('tags') ts = ", ".join(tags) test.tags = ts if file and is_scan_file_too_large(file): messages.add_message( request, messages.ERROR, "Report file is too large. Maximum supported size is {} MB" .format(settings.SCAN_FILE_MAX_SIZE), extra_tags='alert-danger') return HttpResponseRedirect( reverse('re_import_scan_results', args=(test.id, ))) try: parser = import_parser_factory(file, test, active, verified) except ValueError: raise Http404() except Exception as e: messages.add_message( request, messages.ERROR, "An error has occurred in the parser, please see error " "log for details.", extra_tags='alert-danger') parse_logger.exception(e) parse_logger.error("Error in parser: {}".format(str(e))) return HttpResponseRedirect( reverse('re_import_scan_results', args=(test.id, ))) try: items = parser.items original_items = test.finding_set.all().values_list("id", flat=True) new_items = [] mitigated_count = 0 finding_count = 0 finding_added_count = 0 reactivated_count = 0 # can't use helper as when push_all_jira_issues is True, the checkbox gets disabled and is always false # push_to_jira = jira_helper.is_push_to_jira(new_finding, jform.cleaned_data.get('push_to_jira')) push_to_jira = push_all_jira_issues or ( jform and jform.cleaned_data.get('push_to_jira')) for item in items: sev = item.severity if sev == 'Information' or sev == 'Informational': sev = 'Info' item.severity = sev # existing findings may be from before we had component_name/version fields component_name = item.component_name if hasattr( item, 'component_name') else None component_version = item.component_version if hasattr( item, 'component_version') else None # If it doesn't clear minimum severity, move on if Finding.SEVERITIES[sev] > Finding.SEVERITIES[min_sev]: continue # Try to find the existing finding # If it's Veracode or Arachni, then we consider the description for some # reason... from titlecase import titlecase item.title = titlecase(item.title) if scan_type == 'Veracode Scan' or scan_type == 'Arachni Scan': finding = Finding.objects.filter( title=item.title, test__id=test.id, severity=sev, numerical_severity=Finding.get_numerical_severity( sev), description=item.description) else: finding = Finding.objects.filter( title=item.title, test__id=test.id, severity=sev, numerical_severity=Finding.get_numerical_severity( sev)) if len(finding) == 1: finding = finding[0] if finding.mitigated or finding.is_Mitigated: # it was once fixed, but now back finding.mitigated = None finding.is_Mitigated = False finding.mitigated_by = None finding.active = True finding.verified = verified # existing findings may be from before we had component_name/version fields finding.component_name = finding.component_name if finding.component_name else component_name finding.component_version = finding.component_version if finding.component_version else component_version finding.save() note = Notes( entry="Re-activated by %s re-upload." % scan_type, author=request.user) note.save() finding.notes.add(note) endpoint_status = finding.endpoint_status.all() for status in endpoint_status: status.mitigated_by = None status.mitigated_time = None status.mitigated = False status.last_modified = timezone.now() status.save() reactivated_count += 1 else: # existing findings may be from before we had component_name/version fields if not finding.component_name or not finding.component_version: finding.component_name = finding.component_name if finding.component_name else component_name finding.component_version = finding.component_version if finding.component_version else component_version finding.save(dedupe_option=False, push_to_jira=False) new_items.append(finding.id) else: item.test = test item.reporter = request.user item.last_reviewed = timezone.now() item.last_reviewed_by = request.user item.verified = verified item.active = active # Save it item.save(dedupe_option=False) finding_added_count += 1 # Add it to the new items new_items.append(item.id) finding = item if hasattr(item, 'unsaved_req_resp') and len( item.unsaved_req_resp) > 0: for req_resp in item.unsaved_req_resp: if scan_type == "Arachni Scan": burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=req_resp["req"], burpResponseBase64=req_resp["resp"], ) else: burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=base64.b64encode( req_resp["req"].encode("utf-8")), burpResponseBase64=base64.b64encode( req_resp["resp"].encode("utf-8")), ) burp_rr.clean() burp_rr.save() if item.unsaved_request is not None and item.unsaved_response is not None: burp_rr = BurpRawRequestResponse( finding=finding, burpRequestBase64=base64.b64encode( item.unsaved_request.encode()), burpResponseBase64=base64.b64encode( item.unsaved_response.encode()), ) burp_rr.clean() burp_rr.save() if finding: finding_count += 1 for endpoint in item.unsaved_endpoints: ep, created = Endpoint.objects.get_or_create( protocol=endpoint.protocol, host=endpoint.host, path=endpoint.path, query=endpoint.query, fragment=endpoint.fragment, product=test.engagement.product) eps, created = Endpoint_Status.objects.get_or_create( finding=finding, endpoint=ep) ep.endpoint_status.add(eps) finding.endpoints.add(ep) finding.endpoint_status.add(eps) for endpoint in form.cleaned_data['endpoints']: ep, created = Endpoint.objects.get_or_create( protocol=endpoint.protocol, host=endpoint.host, path=endpoint.path, query=endpoint.query, fragment=endpoint.fragment, product=test.engagement.product) eps, created = Endpoint_Status.objects.get_or_create( finding=finding, endpoint=ep) ep.endpoint_status.add(eps) finding.endpoints.add(ep) finding.endpoint_status.add(eps) if item.unsaved_tags is not None: finding.tags = item.unsaved_tags # Save it. This may be the second time we save it in this function. finding.save(push_to_jira=push_to_jira) # calculate the difference to_mitigate = set(original_items) - set(new_items) for finding_id in to_mitigate: finding = Finding.objects.get(id=finding_id) if not finding.mitigated or not finding.is_Mitigated: finding.mitigated = scan_date_time finding.is_Mitigated = True finding.mitigated_by = request.user finding.active = False finding.save() note = Notes(entry="Mitigated by %s re-upload." % scan_type, author=request.user) note.save() finding.notes.add(note) mitigated_count += 1 endpoint_status = finding.endpoint_status.all() for status in endpoint_status: status.mitigated_by = request.user status.mitigated_time = timezone.now() status.mitigated = True status.last_modified = timezone.now() status.save() test.updated = max_safe([scan_date_time, test.updated]) test.engagement.updated = max_safe( [scan_date_time, test.engagement.updated]) test.save() test.engagement.save() messages.add_message( request, messages.SUCCESS, '%s processed, a total of ' % scan_type + message(finding_count, 'finding', 'processed'), extra_tags='alert-success') if finding_added_count > 0: messages.add_message( request, messages.SUCCESS, 'A total of ' + message(finding_added_count, 'finding', 'added') + ', that are new to scan.', extra_tags='alert-success') if reactivated_count > 0: messages.add_message( request, messages.SUCCESS, 'A total of ' + message(reactivated_count, 'finding', 'reactivated') + ', that are back in scan results.', extra_tags='alert-success') if mitigated_count > 0: messages.add_message( request, messages.SUCCESS, 'A total of ' + message(mitigated_count, 'finding', 'mitigated') + '. Please manually verify each one.', extra_tags='alert-success') create_notification(event='scan_added', title=str(finding_count) + " findings for " + test.engagement.product.name, finding_count=finding_count, test=test, engagement=test.engagement, url=reverse('view_test', args=(test.id, ))) return HttpResponseRedirect( reverse('view_test', args=(test.id, ))) except SyntaxError: messages.add_message( request, messages.ERROR, 'There appears to be an error in the XML report, please check and try again.', extra_tags='alert-danger') product_tab = Product_Tab(engagement.product.id, title="Re-upload a %s" % scan_type, tab="engagements") product_tab.setEngagement(engagement) form.fields['endpoints'].queryset = Endpoint.objects.filter( product__id=product_tab.product.id) return render( request, 'dojo/import_scan_results.html', { 'form': form, 'product_tab': product_tab, 'eid': engagement.id, 'additional_message': additional_message, 'jform': jform, })
def add_temp_finding(request, tid, fid): jform = None test = get_object_or_404(Test, id=tid) finding = get_object_or_404(Finding_Template, id=fid) findings = Finding_Template.objects.all() push_all_jira_issues = jira_helper.is_push_all_issues(finding) if jira_helper.get_jira_project(test): jform = JIRAFindingForm( push_all=jira_helper.is_push_all_issues(test), prefix='jiraform', jira_project=jira_helper.get_jira_project(test)) else: jform = None if request.method == 'POST': form = FindingForm(request.POST, template=True, req_resp=None) if (form['active'].value() is False or form['false_p'].value() ) and form['duplicate'].value() is False: closing_disabled = Note_Type.objects.filter( is_mandatory=True, is_active=True).count() if closing_disabled != 0: error_inactive = ValidationError( 'Can not set a finding as inactive without adding all mandatory notes', code='not_active_or_false_p_true') error_false_p = ValidationError( 'Can not set a finding as false positive without adding all mandatory notes', code='not_active_or_false_p_true') if form['active'].value() is False: form.add_error('active', error_inactive) if form['false_p'].value(): form.add_error('false_p', error_false_p) messages.add_message( request, messages.ERROR, 'Can not set a finding as inactive or false positive without adding all mandatory notes', extra_tags='alert-danger') if form.is_valid(): finding.last_used = timezone.now() finding.save() new_finding = form.save(commit=False) new_finding.test = test new_finding.reporter = request.user new_finding.numerical_severity = Finding.get_numerical_severity( new_finding.severity) new_finding.date = datetime.today() if new_finding.false_p or new_finding.active is False: new_finding.mitigated = timezone.now() new_finding.mitigated_by = request.user new_finding.is_Mitigated = True create_template = new_finding.is_template # is template always False now in favor of new model Finding_Template # no further action needed here since this is already adding from template. new_finding.is_template = False new_finding.save(dedupe_option=False, false_history=False) for ep in form.cleaned_data['endpoints']: eps, created = Endpoint_Status.objects.get_or_create( finding=new_finding, endpoint=ep) ep.endpoint_status.add(eps) new_finding.endpoints.add(ep) new_finding.endpoint_status.add(eps) new_finding.save(false_history=True) tags = request.POST.getlist('tags') t = ", ".join('"{0}"'.format(w) for w in tags) new_finding.tags = t if 'jiraform-push_to_jira' in request.POST: jform = JIRAFindingForm( request.POST, prefix='jiraform', push_all=push_all_jira_issues, jira_project=jira_helper.get_jira_project(test)) if jform.is_valid(): if jform.cleaned_data.get('push_to_jira'): jira_helper.push_to_jira(new_finding) messages.add_message(request, messages.SUCCESS, 'Finding from template added successfully.', extra_tags='alert-success') if create_template: templates = Finding_Template.objects.filter( title=new_finding.title) if len(templates) > 0: messages.add_message( request, messages.ERROR, 'A finding template was not created. A template with this title already ' 'exists.', extra_tags='alert-danger') else: template = Finding_Template( title=new_finding.title, cwe=new_finding.cwe, severity=new_finding.severity, description=new_finding.description, mitigation=new_finding.mitigation, impact=new_finding.impact, references=new_finding.references, numerical_severity=new_finding.numerical_severity) template.save() messages.add_message( request, messages.SUCCESS, 'A finding template was also created.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_test', args=(test.id, ))) else: messages.add_message( request, messages.ERROR, 'The form has errors, please correct them below.', extra_tags='alert-danger') else: form = FindingForm(template=True, req_resp=None, initial={ 'active': False, 'date': timezone.now().date(), 'verified': False, 'false_p': False, 'duplicate': False, 'out_of_scope': False, 'title': finding.title, 'description': finding.description, 'cwe': finding.cwe, 'severity': finding.severity, 'mitigation': finding.mitigation, 'impact': finding.impact, 'references': finding.references, 'numerical_severity': finding.numerical_severity, 'tags': [tag.name for tag in finding.tags] }) product_tab = Product_Tab(test.engagement.product.id, title="Add Finding", tab="engagements") product_tab.setEngagement(test.engagement) return render( request, 'dojo/add_findings.html', { 'form': form, 'product_tab': product_tab, 'jform': jform, 'findings': findings, 'temp': True, 'fid': finding.id, 'tid': test.id, 'test': test, })