def new_eng_for_app(request, pid, cicd=False): jform = None prod = Product.objects.get(id=pid) if request.method == 'POST': form = EngForm(request.POST, cicd=cicd) if form.is_valid(): new_eng = form.save(commit=False) if not new_eng.name: new_eng.name = str(new_eng.target_start) new_eng.threat_model = False new_eng.api_test = False new_eng.pen_test = False new_eng.check_list = False new_eng.product = prod if new_eng.threat_model: new_eng.progress = 'threat_model' else: new_eng.progress = 'other' if cicd: new_eng.engagement_type = 'CI/CD' new_eng.status = "In Progress" new_eng.save() tags = request.POST.getlist('tags') t = ", ".join(tags) new_eng.tags = t if get_system_setting('enable_jira'): # Test to make sure there is a Jira project associated the product try: jform = JIRAFindingForm(request.POST, prefix='jiraform', enabled=JIRA_PKey.objects.get(product=prod).push_all_issues) if jform.is_valid(): add_epic_task.delay(new_eng, jform.cleaned_data.get('push_to_jira')) except JIRA_PKey.DoesNotExist: pass messages.add_message(request, messages.SUCCESS, 'Engagement added successfully.', extra_tags='alert-success') create_notification(event='engagement_added', title=new_eng.name + " for " + prod.name, engagement=new_eng, url=reverse('view_engagement', args=(new_eng.id,)), objowner=new_eng.lead) if "_Add Tests" in request.POST: return HttpResponseRedirect(reverse('add_tests', args=(new_eng.id,))) elif "_Import Scan Results" in request.POST: return HttpResponseRedirect(reverse('import_scan_results', args=(new_eng.id,))) else: return HttpResponseRedirect(reverse('view_engagement', args=(new_eng.id,))) else: form = EngForm(initial={'lead': request.user, 'target_start': timezone.now().date(), 'target_end': timezone.now().date() + timedelta(days=7)}, cicd=cicd, product=prod.id) if(get_system_setting('enable_jira')): if JIRA_PKey.objects.filter(product=prod).count() != 0: jform = JIRAFindingForm(prefix='jiraform', enabled=JIRA_PKey.objects.get(product=prod).push_all_issues) product_tab = Product_Tab(pid, title="New Engagement", tab="engagements") return render(request, 'dojo/new_eng.html', {'form': form, 'pid': pid, 'product_tab': product_tab, 'jform': jform })
def async_pdf_report(self, report=None, template="None", filename='report.pdf', report_title=None, report_subtitle=None, report_info=None, context={}, uri=None): xsl_style_sheet = settings.DOJO_ROOT + "/static/dojo/xsl/pdf_toc.xsl" x = urlencode({ 'title': report_title, 'subtitle': report_subtitle, 'info': report_info }) cover = context['host'] + reverse('report_cover_page') + "?" + x config = pdfkit.configuration(wkhtmltopdf=settings.WKHTMLTOPDF_PATH) try: report.task_id = async_pdf_report.request.id report.status = 'running' report.save() bytes = render_to_string(template, context) itoc = context['include_table_of_contents'] if itoc: toc = {'xsl-style-sheet': xsl_style_sheet} else: toc = None pdf = pdfkit.from_string(bytes, False, configuration=config, cover=cover, toc=toc, cover_first=True) if report.file.name: with open(report.file.path, 'w') as f: f.write(pdf) f.close() else: f = ContentFile(pdf) report.file.save(filename, f) report.status = 'success' report.done_datetime = timezone.now() report.save() create_notification(event='report_created', title='Report created', description='The report "%s" is ready.' % report.name, icon='file-text', url=uri, report=report, objowner=report.requester) except Exception as e: report.status = 'error' report.save() # email_requester(report, uri, error=e) raise e return True
def new_eng_for_app(request, pid, cicd=False): jform = None prod = Product.objects.get(id=pid) if request.method == 'POST': form = EngForm(request.POST, cicd=cicd) if form.is_valid(): new_eng = form.save(commit=False) if not new_eng.name: new_eng.name = str(new_eng.target_start) new_eng.threat_model = False new_eng.api_test = False new_eng.pen_test = False new_eng.check_list = False new_eng.product = prod if new_eng.threat_model: new_eng.progress = 'threat_model' else: new_eng.progress = 'other' if cicd: new_eng.engagement_type = 'CI/CD' new_eng.status = "In Progress" new_eng.save() tags = request.POST.getlist('tags') t = ", ".join(tags) new_eng.tags = t if get_system_setting('enable_jira'): # Test to make sure there is a Jira project associated the product try: jform = JIRAFindingForm(request.POST, prefix='jiraform', enabled=JIRA_PKey.objects.get(product=prod).push_all_issues) if jform.is_valid(): add_epic_task.delay(new_eng, jform.cleaned_data.get('push_to_jira')) except JIRA_PKey.DoesNotExist: pass messages.add_message(request, messages.SUCCESS, 'Engagement added successfully.', extra_tags='alert-success') create_notification(event='engagement_added', title=new_eng.name + " for " + prod.name, engagement=new_eng, url=request.build_absolute_uri(reverse('view_engagement', args=(new_eng.id,))), objowner=new_eng.lead) if "_Add Tests" in request.POST: return HttpResponseRedirect(reverse('add_tests', args=(new_eng.id,))) elif "_Import Scan Results" in request.POST: return HttpResponseRedirect(reverse('import_scan_results', args=(new_eng.id,))) else: return HttpResponseRedirect(reverse('view_engagement', args=(new_eng.id,))) else: form = EngForm(initial={'lead': request.user, 'target_start': timezone.now().date(), 'target_end': timezone.now().date() + timedelta(days=7)}, cicd=cicd, product=prod.id) if(get_system_setting('enable_jira')): if JIRA_PKey.objects.filter(product=prod).count() != 0: jform = JIRAFindingForm(prefix='jiraform', enabled=JIRA_PKey.objects.get(product=prod).push_all_issues) product_tab = Product_Tab(pid, title="New Engagement", tab="engagements") return render(request, 'dojo/new_eng.html', {'form': form, 'pid': pid, 'product_tab': product_tab, 'jform': jform })
def add_alerts(self, runinterval): now = timezone.now() upcoming_engagements = Engagement.objects.filter( target_start__gt=now + timedelta(days=3), target_start__lt=now + timedelta(days=3) + runinterval).order_by('target_start') for engagement in upcoming_engagements: create_notification(event='upcoming_engagement', title='Upcoming engagement: %s' % engagement.name, engagement=engagement, recipients=[engagement.lead], url=request.build_absolute_uri( reverse('view_engagement', args=(engagement.id, )))) stale_engagements = Engagement.objects.filter( target_start__gt=now - runinterval, target_end__lt=now, status='In Progress').order_by('-target_end') for eng in stale_engagements: create_notification( event='stale_engagement', title='Stale Engagement: %s' % eng.name, description='The engagement "%s" is stale. Target end was %s.' % (eng.name, eng.target_end.strftime("%b. %d, %Y")), url=reverse('view_engagement', args=(eng.id, )), recipients=[eng.lead])
def delete_jira(request, tid): jira_instance = get_object_or_404(JIRA_Conf, pk=tid) # eng = test.engagement # TODO Make Form form = DeleteJIRAConfForm(instance=jira_instance) if request.method == 'POST': if 'id' in request.POST and str(jira_instance.id) == request.POST['id']: form = DeleteJIRAConfForm(request.POST, instance=jira_instance) if form.is_valid(): jira_instance.delete() messages.add_message(request, messages.SUCCESS, 'JIRA Conf and relationships removed.', extra_tags='alert-success') create_notification(event='other', title='Deletion of JIRA URL %s' % jira_instance.url, description='JIRA url "%s" was deleted by %s' % (jira_instance.url, request.user), url=request.build_absolute_uri(reverse('jira')), ) return HttpResponseRedirect(reverse('jira')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([jira_instance]) rels = collector.nested() add_breadcrumb(title="Delete", top_level=False, request=request) return render(request, 'dojo/delete_jira.html', {'inst': jira_instance, 'form': form, 'rels': rels, 'deletable_objects': rels, })
def delete_product(request, pid): product = get_object_or_404(Product, pk=pid) form = DeleteProductForm(instance=product) if request.method == 'POST': if 'id' in request.POST and str(product.id) == request.POST['id']: form = DeleteProductForm(request.POST, instance=product) if form.is_valid(): if product.tags: del product.tags product.delete() messages.add_message(request, messages.SUCCESS, 'Product and relationships removed.', extra_tags='alert-success') create_notification(event='other', title='Deletion of %s' % product.name, description='The product "%s" was deleted by %s' % (product.name, request.user), url=request.build_absolute_uri(reverse('product')), icon="exclamation-triangle") return HttpResponseRedirect(reverse('product')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([product]) rels = collector.nested() product_tab = Product_Tab(pid, title="Product", tab="settings") return render(request, 'dojo/delete_product.html', {'product': product, 'form': form, 'product_tab': product_tab, 'rels': rels, })
def new_product(request): jform = None if request.method == 'POST': form = ProductForm(request.POST, instance=Product()) if get_system_setting('enable_jira'): jform = JIRAPKeyForm(request.POST, instance=JIRA_PKey()) else: jform = None if form.is_valid(): product = form.save() tags = request.POST.getlist('tags') t = ", ".join('"{0}"'.format(w) for w in tags) product.tags = t messages.add_message(request, messages.SUCCESS, 'Product added successfully.', extra_tags='alert-success') if get_system_setting('enable_jira'): if jform.is_valid(): jira_pkey = jform.save(commit=False) if jira_pkey.conf is not None: jira_pkey.product = product jira_pkey.save() messages.add_message( request, messages.SUCCESS, 'JIRA information added successfully.', extra_tags='alert-success') # SonarQube API Configuration sonarqube_form = Sonarqube_ProductForm(request.POST) if sonarqube_form.is_valid(): sonarqube_product = sonarqube_form.save(commit=False) sonarqube_product.product = product sonarqube_product.save() create_notification(event='product_added', title=product.name, url=reverse('view_product', args=(product.id, ))) return HttpResponseRedirect( reverse('view_product', args=(product.id, ))) else: form = ProductForm() if get_system_setting('enable_jira'): jform = JIRAPKeyForm() else: jform = None add_breadcrumb(title="New Product", top_level=False, request=request) return render(request, 'dojo/new_product.html', { 'form': form, 'jform': jform, 'sonarqube_form': Sonarqube_ProductForm() })
def add_tests(request, eid): eng = Engagement.objects.get(id=eid) cred_form = CredMappingForm() cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter(engagement=eng).order_by('cred_id') if request.method == 'POST': form = TestForm(request.POST) cred_form = CredMappingForm(request.POST) cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter(engagement=eng).order_by('cred_id') if form.is_valid(): new_test = form.save(commit=False) new_test.engagement = eng try: new_test.lead = User.objects.get(id=form['lead'].value()) except: new_test.lead = None pass new_test.save() tags = request.POST.getlist('tags') t = ", ".join(tags) new_test.tags = t #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 = new_test new_f.cred_id = cred_user.cred_id new_f.save() messages.add_message(request, messages.SUCCESS, 'Test added successfully.', extra_tags='alert-success') create_notification(event='test_added', title='Test added', test=new_test, engagement=eng, url=request.build_absolute_uri(reverse('view_engagement', args=(eng.id,)))) if '_Add Another Test' in request.POST: return HttpResponseRedirect(reverse('add_tests', args=(eng.id,))) elif '_Add Findings' in request.POST: return HttpResponseRedirect(reverse('add_findings', args=(new_test.id,))) elif '_Finished' in request.POST: return HttpResponseRedirect(reverse('view_engagement', args=(eng.id,))) else: form = TestForm() form.initial['target_start'] = eng.target_start form.initial['target_end'] = eng.target_end add_breadcrumb(parent=eng, title="Add Tests", top_level=False, request=request) return render(request, 'dojo/add_tests.html', {'form': form, 'cred_form': cred_form, 'eid': eid })
def request_finding_review(request, fid): finding = get_object_or_404(Finding, id=fid) user = get_object_or_404(Dojo_User, id=request.user.id) # in order to review a finding, we need to capture why a review is needed # we can do this with a Note if request.method == 'POST': form = ReviewFindingForm(request.POST) if form.is_valid(): now = timezone.now() new_note = Notes() new_note.entry = "Review Request: " + form.cleaned_data['entry'] new_note.author = request.user new_note.date = now new_note.save() finding.notes.add(new_note) finding.active = False finding.verified = False finding.under_review = True finding.review_requested_by = user finding.last_reviewed = now finding.last_reviewed_by = request.user users = form.cleaned_data['reviewers'] finding.reviewers = users finding.save() create_notification(event='review_requested', title='Finding review requested', description='User %s has requested that you please review the finding "%s" for accuracy:\n\n%s' \ % (user, finding.title, new_note), icon='check', url=request.build_absolute_uri(reverse("view_finding", args=(finding.id,)))) messages.add_message( request, messages.SUCCESS, 'Finding marked for review and reviewers notified.', extra_tags='alert-success') return HttpResponseRedirect( reverse('view_finding', args=(finding.id, ))) else: form = ReviewFindingForm() add_breadcrumb(parent=finding, title="Review Finding", top_level=False, request=request) return render(request, 'dojo/review_finding.html', { 'finding': finding, 'user': user, 'form': form })
def async_pdf_report(self, report=None, template="None", filename='report.pdf', report_title=None, report_subtitle=None, report_info=None, context={}, uri=None): xsl_style_sheet = settings.DOJO_ROOT + "/static/dojo/xsl/pdf_toc.xsl" x = urlencode({'title': report_title, 'subtitle': report_subtitle, 'info': report_info}) cover = context['host'] + reverse( 'report_cover_page') + "?" + x config = pdfkit.configuration(wkhtmltopdf=settings.WKHTMLTOPDF_PATH) try: report.task_id = async_pdf_report.request.id report.save() bytes = render_to_string(template, context) itoc = context['include_table_of_contents'] if itoc: toc = {'xsl-style-sheet': xsl_style_sheet} else: toc = None pdf = pdfkit.from_string(bytes, False, configuration=config, cover=cover, toc=toc) if report.file.name: with open(report.file.path, 'w') as f: f.write(pdf) f.close() else: f = ContentFile(pdf) report.file.save(filename, f) report.status = 'success' report.done_datetime = timezone.now() report.save() create_notification(event='report_created', title='Report created', description='The report "%s" is ready.' % report.name, url=uri, report=report, objowner=report.requester) except Exception as e: report.status = 'error' report.save() # email_requester(report, uri, error=e) raise e return True
def add_alerts(self, runinterval): now = timezone.now() """ upcoming_engagements = Engagement.objects.filter(target_start__gt=now + timedelta(days=3), target_start__lt=now + timedelta(days=3) + runinterval).order_by('target_start') for engagement in upcoming_engagements: create_notification(event='upcoming_engagement', title='Upcoming engagement: %s' % engagement.name, engagement=engagement, recipients=[engagement.lead], url=reverse('view_engagement', args=(engagement.id,))) stale_engagements = Engagement.objects.filter( target_start__gt=now - runinterval, target_end__lt=now, status='In Progress').order_by('-target_end') for eng in stale_engagements: create_notification(event='stale_engagement', title='Stale Engagement: %s' % eng.name, description='The engagement "%s" is stale. Target end was %s.' % (eng.name, eng.target_end.strftime("%b. %d, %Y")), url=reverse('view_engagement', args=(eng.id,)), recipients=[eng.lead]) """ system_settings = System_Settings.objects.get() if system_settings.engagement_auto_close: # Close Engagements older than user defined days close_days = system_settings.engagement_auto_close_days unclosed_engagements = Engagement.objects.filter( target_end__lte=now - timedelta(days=close_days), status='In Progress').order_by('target_end') for eng in unclosed_engagements: create_notification( event='auto_close_engagement', title=eng.name, description= 'The engagement "%s" has auto-closed. Target end was %s.' % (eng.name, eng.target_end.strftime("%b. %d, %Y")), url=reverse('view_engagement', args=(eng.id, )), recipients=[eng.lead]) unclosed_engagements.update(status="Completed", active=False, updated=timezone.now()) # Calculate grade if system_settings.enable_product_grade: products = Product.objects.all() for product in products: calculate_grade(product)
def new_eng_for_app(request, pid): jform = None prod = Product.objects.get(id=pid) if request.method == 'POST': form = EngForm(request.POST) if form.is_valid(): new_eng = form.save(commit=False) new_eng.product = prod if new_eng.threat_model: new_eng.progress = 'threat_model' else: new_eng.progress = 'other' new_eng.save() if get_system_setting('enable_jira'): #Test to make sure there is a Jira project associated the product try: jform = JIRAFindingForm(request.POST, prefix='jiraform', enabled=JIRA_PKey.objects.get(product=prod).push_all_issues) if jform.is_valid(): add_epic_task.delay(new_eng, jform.cleaned_data.get('push_to_jira')) except JIRA_PKey.DoesNotExist: pass #else: # print >>sys.stderr, 'no prefix is found' messages.add_message(request, messages.SUCCESS, 'Engagement added successfully.', extra_tags='alert-success') create_notification(event='engagement_added', title='Engagement added', engagement=new_eng, url=request.build_absolute_uri(reverse('view_engagement', args=(new_eng.id,))), objowner=new_eng.lead) if "_Add Tests" in request.POST: return HttpResponseRedirect(reverse('add_tests', args=(new_eng.id,))) else: return HttpResponseRedirect(reverse('view_engagement', args=(new_eng.id,))) else: form = EngForm(initial={}) if(get_system_setting('enable_jira')): if JIRA_PKey.objects.filter(product=prod).count() != 0: jform = JIRAFindingForm(prefix='jiraform', enabled=JIRA_PKey.objects.get(product=prod).push_all_issues) add_breadcrumb(parent=prod, title="New Engagement", top_level=False, request=request) return render(request, 'dojo/new_eng.html', {'form': form, 'pid': pid, 'jform': jform })
def request_finding_review(request, fid): finding = get_object_or_404(Finding, id=fid) user = get_object_or_404(Dojo_User, id=request.user.id) # in order to review a finding, we need to capture why a review is needed # we can do this with a Note if request.method == 'POST': form = ReviewFindingForm(request.POST) if form.is_valid(): now = timezone.now() new_note = Notes() new_note.entry = "Review Request: " + form.cleaned_data['entry'] new_note.author = request.user new_note.date = now new_note.save() finding.notes.add(new_note) finding.active = False finding.verified = False finding.under_review = True finding.review_requested_by = user finding.last_reviewed = now finding.last_reviewed_by = request.user users = form.cleaned_data['reviewers'] finding.reviewers = users finding.save() create_notification(event='review_requested', title='Finding review requested', description='User %s has requested that you please review the finding "%s" for accuracy:\n\n%s' \ % (user, finding.title, new_note), icon='check', url=request.build_absolute_uri(reverse("view_finding", args=(finding.id,)))) messages.add_message(request, messages.SUCCESS, 'Finding marked for review and reviewers notified.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_finding', args=(finding.id,))) else: form = ReviewFindingForm() add_breadcrumb(parent=finding, title="Review Finding", top_level=False, request=request) return render(request, 'dojo/review_finding.html', {'finding': finding, 'user': user, 'form': form})
def delete_engagement(request, eid): engagement = get_object_or_404(Engagement, pk=eid) product = engagement.product form = DeleteEngagementForm(instance=engagement) if request.method == 'POST': if 'id' in request.POST and str(engagement.id) == request.POST['id']: form = DeleteEngagementForm(request.POST, instance=engagement) if form.is_valid(): del engagement.tags engagement.delete() messages.add_message(request, messages.SUCCESS, 'Engagement and relationships removed.', extra_tags='alert-success') create_notification( event='other', title='Deletion of %s' % engagement.name, description='The engagement "%s" was deleted by %s' % (engagement.name, request.user), url=request.build_absolute_uri( reverse('view_engagements', args=(product.id, ))), recipients=[engagement.lead], icon="exclamation-triangle") if engagement.engagement_type == 'CI/CD': return HttpResponseRedirect( reverse("view_engagements_cicd", args=(product.id, ))) else: return HttpResponseRedirect( reverse("view_engagements", args=(product.id, ))) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([engagement]) rels = collector.nested() product_tab = Product_Tab(product.id, title="Delete Engagement", tab="engagements") product_tab.setEngagement(engagement) return render( request, 'dojo/delete_engagement.html', { 'product_tab': product_tab, 'engagement': engagement, 'form': form, 'rels': rels, })
def edit_jira(request, jid): jira = JIRA_Conf.objects.get(pk=jid) if request.method == 'POST': jform = JIRAForm(request.POST, instance=jira) if jform.is_valid(): try: jira_server = jform.cleaned_data.get('url').rstrip('/') jira_username = jform.cleaned_data.get('username') jira_password = jform.cleaned_data.get('password') # Instantiate JIRA instance for validating url, username and password JIRA(server=jira_server, basic_auth=(jira_username, jira_password)) new_j = jform.save(commit=False) new_j.url = jira_server new_j.save() messages.add_message( request, messages.SUCCESS, 'JIRA Configuration Successfully Created.', extra_tags='alert-success') create_notification( event='other', title='Edit of JIRA URL %s' % jform.cleaned_data.get('url').rstrip('/'), description='JIRA url "%s" was edited by %s' % (jform.cleaned_data.get('url').rstrip('/'), request.user), url=request.build_absolute_uri(reverse('jira')), ) return HttpResponseRedirect(reverse('jira', )) except Exception: messages.add_message( request, messages.ERROR, 'Unable to authenticate. Please check the URL, username, and password.', extra_tags='alert-danger') else: jform = JIRAForm(instance=jira) add_breadcrumb(title="Edit JIRA Configuration", top_level=False, request=request) return render(request, 'dojo/edit_jira.html', { 'jform': jform, })
def delete_test(request, tid): test = get_object_or_404(Test, pk=tid) eng = test.engagement form = DeleteTestForm(instance=test) if request.method == 'POST': if 'id' in request.POST and str(test.id) == request.POST['id']: form = DeleteTestForm(request.POST, instance=test) if form.is_valid(): del test.tags test.delete() messages.add_message(request, messages.SUCCESS, 'Test and relationships removed.', extra_tags='alert-success') create_notification( event='other', title='Deletion of %s' % test.title, description='The test "%s" was deleted by %s' % (test.title, request.user), url=request.build_absolute_uri( reverse('view_engagement', args=(eng.id, ))), recipients=[test.engagement.lead], icon="exclamation-triangle") return HttpResponseRedirect( reverse('view_engagement', args=(eng.id, ))) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([test]) rels = collector.nested() product_tab = Product_Tab(test.engagement.product.id, title="Delete Test", tab="engagements") product_tab.setEngagement(test.engagement) return render( request, 'dojo/delete_test.html', { 'test': test, 'product_tab': product_tab, 'form': form, 'rels': rels, 'deletable_objects': rels, })
def edit_product_type(request, ptid): pt = get_object_or_404(Product_Type, pk=ptid) pt_form = Product_TypeForm(instance=pt) delete_pt_form = Delete_Product_TypeForm(instance=pt) if request.method == "POST" and request.POST.get('edit_product_type'): pt_form = Product_TypeForm(request.POST, instance=pt) if pt_form.is_valid(): pt = pt_form.save() messages.add_message( request, messages.SUCCESS, 'Product type updated successfully.', extra_tags="alert-success", ) return HttpResponseRedirect(reverse("product_type")) if request.method == "POST" and request.POST.get("delete_product_type"): form2 = Delete_Product_TypeForm(request.POST, instance=pt) if form2.is_valid(): pt.delete() messages.add_message( request, messages.SUCCESS, "Product type Deleted successfully.", extra_tags="alert-success", ) create_notification( event='other', title='Deletion of %s' % pt.name, description='The product type "%s" was deleted by %s' % (pt.name, request.user), url=request.build_absolute_uri(reverse('product_type')), icon="exclamation-triangle") return HttpResponseRedirect(reverse("product_type")) add_breadcrumb(title="Edit Product Type", top_level=False, request=request) return render( request, 'dojo/edit_product_type.html', { 'name': 'Edit Product Type', 'metric': False, 'user': request.user, 'pt_form': pt_form, 'pt': pt })
def reopen_eng(request, eid): eng = Engagement.objects.get(id=eid) reopen_engagement(eng) messages.add_message(request, messages.SUCCESS, 'Engagement reopened successfully.', extra_tags='alert-success') create_notification( event='other', title='Reopening of %s' % eng.name, description='The engagement "%s" was reopened' % (eng.name), url=request.build_absolute_uri( reverse('view_engagements', args=(eng.product.id, ))), ) if eng.engagement_type == 'CI/CD': return HttpResponseRedirect( reverse("view_engagements_cicd", args=(eng.product.id, ))) else: return HttpResponseRedirect( reverse("view_engagements", args=(eng.product.id, )))
def add_alerts(self, runinterval): now = timezone.now() upcoming_engagements = Engagement.objects.filter(target_start__gt=now+timedelta(days=3),target_start__lt=now+timedelta(days=3)+runinterval).order_by('target_start') for engagement in upcoming_engagements: create_notification(event='upcoming_engagement', title='Upcoming engagement: %s' % engagement.name, engagement=engagement, recipients=[engagement.lead], url=request.build_absolute_uri(reverse('view_engagement', args=(engagement.id,)))) stale_engagements = Engagement.objects.filter( target_start__gt=now-runinterval, target_end__lt=now, status='In Progress').order_by('-target_end') for eng in stale_engagements: create_notification(event='stale_engagement', title='Stale Engagement: %s' % eng.name, description='The engagement "%s" is stale. Target end was %s.' % (eng.name, eng.target_end.strftime("%b. %d, %Y")), url=reverse('view_engagement', args=(eng.id,)), recipients=[eng.lead])
def new_product(request): jform = None if request.method == 'POST': form = ProductForm(request.POST, instance=Product()) if get_system_setting('enable_jira'): jform = JIRAPKeyForm(request.POST, instance=JIRA_PKey()) else: jform = None if form.is_valid(): product = form.save() tags = request.POST.getlist('tags') t = ", ".join(tags) product.tags = t messages.add_message(request, messages.SUCCESS, 'Product added successfully.', extra_tags='alert-success') if get_system_setting('enable_jira'): if jform.is_valid(): jira_pkey = jform.save(commit=False) if jira_pkey.conf is not None: jira_pkey.product = product jira_pkey.save() messages.add_message(request, messages.SUCCESS, 'JIRA information added successfully.', extra_tags='alert-success') create_notification(event='product_added', title=product.name, url=request.build_absolute_uri(reverse('view_product', args=(product.id,)))) return HttpResponseRedirect(reverse('view_product', args=(product.id,))) else: form = ProductForm() if get_system_setting('enable_jira'): jform = JIRAPKeyForm() else: jform = None add_breadcrumb(title="New Product", top_level=False, request=request) return render(request, 'dojo/new_product.html', {'form': form, 'jform': jform})
def add_alerts(self, runinterval): now = timezone.now() upcoming_engagements = Engagement.objects.filter(target_start__gt=now + timedelta(days=3), target_start__lt=now + timedelta(days=3) + runinterval).order_by('target_start') for engagement in upcoming_engagements: create_notification(event='upcoming_engagement', title='Upcoming engagement: %s' % engagement.name, engagement=engagement, recipients=[engagement.lead], url=reverse('view_engagement', args=(engagement.id,))) stale_engagements = Engagement.objects.filter( target_start__gt=now - runinterval, target_end__lt=now, status='In Progress').order_by('-target_end') for eng in stale_engagements: create_notification(event='stale_engagement', title='Stale Engagement: %s' % eng.name, description='The engagement "%s" is stale. Target end was %s.' % (eng.name, eng.target_end.strftime("%b. %d, %Y")), url=reverse('view_engagement', args=(eng.id,)), recipients=[eng.lead]) system_settings = System_Settings.objects.get() if system_settings.engagement_auto_close: # Close Engagements older than user defined days close_days = system_settings.engagement_auto_close_days unclosed_engagements = Engagement.objects.filter(target_end__lte=now - timedelta(days=close_days), status='In Progress').order_by('target_end') for eng in unclosed_engagements: create_notification(event='auto_close_engagement', title=eng.name, description='The engagement "%s" has auto-closed. Target end was %s.' % (eng.name, eng.target_end.strftime("%b. %d, %Y")), url=reverse('view_engagement', args=(eng.id,)), recipients=[eng.lead]) unclosed_engagements.update(status="Completed", active=False, updated=timezone.now()) # Calculate grade if system_settings.enable_product_grade: products = Product.objects.all() for product in products: calculate_grade(product)
def express_new_jira(request): if request.method == 'POST': jform = ExpressJIRAForm(request.POST, instance=JIRA_Conf()) if jform.is_valid(): try: jira_server = jform.cleaned_data.get('url').rstrip('/') jira_username = jform.cleaned_data.get('username') jira_password = jform.cleaned_data.get('password') # Instantiate JIRA instance for validating url, username and password try: jira = JIRA(server=jira_server, basic_auth=(jira_username, jira_password)) except Exception: messages.add_message(request, messages.ERROR, 'Unable to authenticate. Please check the URL, username, and password.', extra_tags='alert-danger') return render(request, 'dojo/express_new_jira.html', {'jform': jform}) # authentication successful # Get the open and close keys issue_id = jform.cleaned_data.get('issue_key') key_url = jira_server + '/rest/api/latest/issue/' + issue_id + '/transitions?expand=transitions.fields' data = json.loads(requests.get(key_url, auth=(jira_username, jira_password)).text) for node in data['transitions']: if node['to']['name'] == 'To Do': open_key = int(node['to']['id']) if node['to']['name'] == 'Done': close_key = int(node['to']['id']) # Get the epic id name key_url = jira_server + '/rest/api/2/field' data = json.loads(requests.get(key_url, auth=(jira_username, jira_password)).text) for node in data: if 'Epic Name' in node['clauseNames']: epic_name = int(node['clauseNames'][0][3:-1]) break jira_conf = JIRA_Conf(username=jira_username, password=jira_password, url=jira_server, configuration_name=jform.cleaned_data.get('configuration_name'), info_mapping_severity='Lowest', low_mapping_severity='Low', medium_mapping_severity='Medium', high_mapping_severity='High', critical_mapping_severity='Highest', epic_name_id=epic_name, open_status_key=open_key, close_status_key=close_key, finding_text='', default_issue_type=jform.cleaned_data.get('default_issue_type')) jira_conf.save() messages.add_message(request, messages.SUCCESS, 'JIRA Configuration Successfully Created.', extra_tags='alert-success') create_notification(event='other', title='New addition of JIRA URL %s' % jform.cleaned_data.get('url').rstrip('/'), description='JIRA url "%s" was added by %s' % (jform.cleaned_data.get('url').rstrip('/'), request.user), url=request.build_absolute_uri(reverse('jira')), ) return HttpResponseRedirect(reverse('jira', )) except: messages.add_message(request, messages.ERROR, 'Unable to query other reuierd fields. They must be entered manually.', extra_tags='alert-danger') return HttpResponseRedirect(reverse('add_jira', )) return render(request, 'dojo/express_new_jira.html', {'jform': jform}) else: jform = ExpressJIRAForm() add_breadcrumb(title="New Jira Configuration (Express)", top_level=False, request=request) return render(request, 'dojo/express_new_jira.html', {'jform': jform})
def import_scan_results(request, eid=None, pid=None): engagement = None form = ImportScanForm() cred_form = CredMappingForm() finding_count = 0 enabled = False jform = None if eid: engagement = get_object_or_404(Engagement, id=eid) cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter( engagement=engagement).order_by('cred_id') if get_system_setting( 'enable_jira') and engagement.product.jira_pkey_set.first( ) is not None: enabled = engagement.product.jira_pkey_set.first().push_all_issues jform = JIRAFindingForm(enabled=enabled, prefix='jiraform') elif pid: product = get_object_or_404(Product, id=pid) if get_system_setting( 'enable_jira') and product.jira_pkey_set.first() is not None: enabled = product.jira_pkey_set.first().push_all_issues jform = JIRAFindingForm(enabled=enabled, prefix='jiraform') 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 form.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') 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'] if not any(scan_type in code for code in ImportScanForm.SCAN_TYPE_CHOICES): raise Http404() 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) t.lead = request.user t.full_clean() t.save() tags = request.POST.getlist('tags') ts = ", ".join(tags) t.tags = ts # 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=(eid, ))) try: # Push to Jira? push_to_jira = False if enabled: push_to_jira = True elif 'jiraform-push_to_jira' in request.POST: jform = JIRAFindingForm(request.POST, prefix='jiraform', enabled=enabled) if jform.is_valid(): push_to_jira = 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 if item.date == timezone.now().date(): item.date = t.target_start item.reporter = request.user item.last_reviewed = timezone.now() item.last_reviewed_by = request.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) item.endpoints.add(ep) 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) item.endpoints.add(ep) 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='results_added', initiator=request.user, 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") 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, 'custom_breadcrumb': custom_breadcrumb, 'title': title, 'cred_form': cred_form, 'jform': jform })
def import_object_eng(request, engagement, json_data): create_test_code_review = False create_alert = False #Get the product from the engagement product = engagement.product #Retrieve the files currently set for this product object_queryset = Objects.objects.filter(product=engagement.product.id).order_by('-path') data = json.load(json_data) #Set default review status review_status_id = 1 review_status = Objects_Review.objects.get(pk=review_status_id) for file in data: print file["path"] #Save the file if the object isn't in object table file_type, found_object = find_item(file["path"], object_queryset) if found_object is None or file_type == "path": review_status_id = 1 if file_type == "path": #Copy the review status review_status_id = found_object.review_status.id #Set default review status review_status = Objects_Review.objects.get(pk=review_status_id) #if found_object is None: object = Objects(product=product, path=file["path"], review_status=review_status) object.save() found_object = object if file_type == "path": for tag in found_object.tags.all(): Tag.objects.update_tags(object, tag.name) full_url = None type = None percentUnchanged = None build_id = None if "full_url" in file: full_url = file["full_url"] if "type" in file: type = file["type"] if "percentUnchanged" in file: percentUnchanged = file["percentUnchanged"] if "build_id" in file: build_id = file["build_id"][:12] #Find the status so the appropriate action takes place if found_object.review_status.id == 2: create_alert = True elif found_object.review_status.id == 3: create_test_code_review = True create_alert = True #Save the changed files to the engagement view object_eng = Objects_Engagement(engagement=engagement, object_id=found_object, full_url=full_url, type=type, percentUnchanged=percentUnchanged, build_id=build_id) object_eng.save() #Create the notification if create_alert: create_notification(event='code_review', title='Manual Code Review Requested', description="Manual code review requested as tracked file changes were found in the latest build.", engagement=engagement, url=request.build_absolute_uri(reverse('view_object_eng', args=(engagement.id,)))) #Create the test within the engagement if create_test_code_review: environment, env_created = Development_Environment.objects.get_or_create(name="Development") tt = Test_Type.objects.get(pk=27) #Manual code review if tt: test = Test(engagement=engagement, test_type=tt, target_start=timezone.now(), target_end=timezone.now() + timezone.timedelta(days=1), environment=environment, percent_complete=0) test.save() create_notification(event='test_added', title='Test added for Manual Code Review', test=test, engagement=engagement, url=request.build_absolute_uri(reverse('view_engagement', args=(engagement.id,))))
def save(self): data = self.validated_data close_old_findings = data['close_old_findings'] active = data['active'] verified = data['verified'] test_type, created = Test_Type.objects.get_or_create( name=data.get('test_type', data['scan_type'])) environment, created = Development_Environment.objects.get_or_create( name='Development') test = Test( engagement=data['engagement'], lead=data['lead'], test_type=test_type, target_start=data['scan_date'], target_end=data['scan_date'], environment=environment, percent_complete=100) try: test.full_clean() except ValidationError: pass test.save() if 'tags' in data: test.tags = ' '.join(data['tags']) try: parser = import_parser_factory(data['file'], test, active, verified, data['scan_type'],) except ValueError: raise Exception('FileParser ValueError') skipped_hashcodes = [] try: for item in parser.items: sev = item.severity if sev == 'Information' or sev == 'Informational': sev = 'Info' item.severity = sev if (Finding.SEVERITIES[sev] > Finding.SEVERITIES[data['minimum_severity']]): continue item.test = test item.date = test.target_start item.reporter = self.context['request'].user item.last_reviewed = timezone.now() item.last_reviewed_by = self.context['request'].user item.active = data['active'] item.verified = data['verified'] item.save(dedupe_option=False) if (hasattr(item, 'unsaved_req_resp') and len(item.unsaved_req_resp) > 0): for req_resp in item.unsaved_req_resp: burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=req_resp["req"], burpResponseBase64=req_resp["resp"]) 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=item.unsaved_request, burpResponseBase64=item.unsaved_response) 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=test.engagement.product) item.endpoints.add(ep) if item.unsaved_tags is not None: item.tags = item.unsaved_tags item.save() except SyntaxError: raise Exception('Parser SyntaxError') if close_old_findings: # Close old active findings that are not reported by this scan. new_hash_codes = test.finding_set.values('hash_code') old_findings = None if test.engagement.deduplication_on_engagement: old_findings = Finding.objects.exclude(test=test) \ .exclude(hash_code__in=new_hash_codes) \ .exclude(hash_code__in=skipped_hashcodes) \ .filter(test__engagement=test.engagement, test__test_type=test_type, active=True) else: old_findings = Finding.objects.exclude(test=test) \ .exclude(hash_code__in=new_hash_codes) \ .exclude(hash_code__in=skipped_hashcodes) \ .filter(test__engagement__product=test.engagement.product, test__test_type=test_type, active=True) for old_finding in old_findings: old_finding.active = False old_finding.mitigated = datetime.datetime.combine( test.target_start, timezone.now().time()) if settings.USE_TZ: old_finding.mitigated = timezone.make_aware( old_finding.mitigated, timezone.get_default_timezone()) old_finding.mitigated_by = self.context['request'].user old_finding.notes.create(author=self.context['request'].user, entry="This finding has been automatically closed" " as it is not present anymore in recent scans.") Tag.objects.add_tag(old_finding, 'stale') old_finding.save() title = 'An old finding has been closed for "{}".' \ .format(test.engagement.product.name) description = 'See <a href="{}">{}</a>' \ .format(reverse('view_finding', args=(old_finding.id, )), old_finding.title) create_notification(event='other', title=title, description=description, icon='bullseye', objowner=self.context['request'].user) return test
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 enabled = False # Decide if we need to present the Push to JIRA form if get_system_setting( 'enable_jira') and engagement.product.jira_pkey_set.first( ) is not None: enabled = engagement.product.jira_pkey_set.first().push_all_issues jform = JIRAFindingForm(enabled=enabled, prefix='jiraform') form.initial['tags'] = [tag.name for tag in test.tags] if request.method == "POST": form = ReImportScanForm(request.POST, request.FILES) if form.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['file'] 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 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 # Push to Jira? push_to_jira = False if enabled: push_to_jira = True elif 'jiraform-push_to_jira' in request.POST: jform = JIRAFindingForm(request.POST, prefix='jiraform', enabled=enabled) if jform.is_valid(): push_to_jira = 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 # 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... 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 finding.save() note = Notes( entry="Re-activated by %s re-upload." % scan_type, author=request.user) note.save() finding.notes.add(note) reactivated_count += 1 new_items.append(finding.id) else: item.test = test if item.date == timezone.now().date(): item.date = test.target_start 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) finding.endpoints.add(ep) 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) finding.endpoints.add(ep) 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 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='results_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_findings(request, tid): test = Test.objects.get(id=tid) form_error = False enabled = False jform = None form = AddFindingForm(initial={'date': timezone.now().date()}) if get_system_setting('enable_jira') and JIRA_PKey.objects.filter(product=test.engagement.product).count() != 0: enabled = JIRA_PKey.objects.get(product=test.engagement.product).push_all_issues jform = JIRAFindingForm(enabled=enabled, prefix='jiraform') else: jform = None if request.method == 'POST': form = AddFindingForm(request.POST) if form['active'].value() is False or form['verified'].value() is False and 'jiraform-push_to_jira' in request.POST: error = ValidationError('Findings must be active and verified to be pushed to JIRA', code='not_active_or_verified') if form['active'].value() is False: form.add_error('active', error) if form['verified'].value() is False: form.add_error('verified', error) messages.add_message(request, messages.ERROR, 'Findings must be active and verified to be pushed to JIRA', extra_tags='alert-danger') if form['severity'].value() == 'Info' and 'jiraform-push_to_jira' in request.POST: error = ValidationError('Findings with Informational severity cannot be pushed to JIRA.', code='info-severity-to-jira') if form.is_valid(): 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) if new_finding.false_p or new_finding.active is False: new_finding.mitigated = timezone.now() new_finding.mitigated_by = request.user create_template = new_finding.is_template # always false now since this will be deprecated soon in favor of new Finding_Template model new_finding.is_template = False new_finding.save(dedupe_option=False) new_finding.endpoints.set(form.cleaned_data['endpoints']) new_finding.save(false_history=True) create_notification(event='other', title='Addition of %s' % new_finding.title, 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 'jiraform-push_to_jira' in request.POST: jform = JIRAFindingForm(request.POST, prefix='jiraform', enabled=enabled) if jform.is_valid(): add_issue_task.delay(new_finding, jform.cleaned_data.get('push_to_jira')) messages.add_message(request, messages.SUCCESS, 'Finding 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') if '_Finished' in request.POST: return HttpResponseRedirect(reverse('view_test', args=(test.id,))) else: return HttpResponseRedirect(reverse('add_findings', args=(test.id,))) else: if 'endpoints' in form.cleaned_data: form.fields['endpoints'].queryset = form.cleaned_data['endpoints'] else: form.fields['endpoints'].queryset = Endpoint.objects.none() form_error = True messages.add_message(request, messages.ERROR, 'The form has errors, please correct them below.', extra_tags='alert-danger') 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 if eid: engagement = get_object_or_404(Engagement, id=eid) cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter( engagement=engagement).order_by('cred_id') 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 form.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['file'] 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'] if not any(scan_type in code for code in ImportScanForm.SCAN_TYPE_CHOICES): raise Http404() 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) t.lead = request.user t.full_clean() t.save() tags = request.POST.getlist('tags') ts = ", ".join(tags) t.tags = ts # 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) except ValueError: raise Http404() try: for item in parser.items: 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 if item.date == timezone.now().date(): item.date = t.target_start item.reporter = request.user item.last_reviewed = timezone.now() item.last_reviewed_by = request.user item.active = active item.verified = verified item.save(dedupe_option=False) if hasattr(item, 'unsaved_req_resp') and len( item.unsaved_req_resp) > 0: for req_resp in item.unsaved_req_resp: burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=req_resp["req"], burpResponseBase64=req_resp["resp"], ) 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=item.unsaved_request, burpResponseBase64=item.unsaved_response, ) 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) item.endpoints.add(ep) item.save() if item.unsaved_tags is not None: item.tags = item.unsaved_tags # patched: I have to add the jira creation in line below add_issue(item, True) 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='results_added', title=str(finding_count) + " findings for " + engagement.product.name, finding_count=finding_count, test=t, engagement=engagement, url=request.build_absolute_uri( 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") return render( request, 'dojo/import_scan_results.html', { 'form': form, 'product_tab': product_tab, 'custom_breadcrumb': custom_breadcrumb, 'title': title, 'cred_form': cred_form, })
def import_scan_results(request, eid): engagement = get_object_or_404(Engagement, id=eid) finding_count = 0 form = ImportScanForm() if request.method == "POST": form = ImportScanForm(request.POST, request.FILES) if form.is_valid(): file = request.FILES['file'] scan_date = form.cleaned_data['scan_date'] min_sev = form.cleaned_data['minimum_severity'] scan_type = request.POST['scan_type'] if not any(scan_type in code for code in ImportScanForm.SCAN_TYPE_CHOICES): raise Http404() 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) t.full_clean() t.save() tags = request.POST.getlist('tags') ts = ", ".join(tags) t.tags = ts try: parser = import_parser_factory(file, t) except ValueError: raise Http404() try: for item in parser.items: 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.date = timezone.now() item.reporter = request.user item.last_reviewed = timezone.now() item.last_reviewed_by = request.user item.save() if hasattr(item, 'unsaved_req_resp') and len(item.unsaved_req_resp) > 0: for req_resp in item.unsaved_req_resp: burp_rr = BurpRawRequestResponse(finding=item, burpRequestBase64=req_resp["req"], burpResponseBase64=req_resp["resp"], ) 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=item.unsaved_request, burpResponseBase64=item.unsaved_response, ) 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, fqdn=endpoint.fqdn, port=endpoint.port, path=endpoint.path, query=endpoint.query, fragment=endpoint.fragment, product=t.engagement.product) item.endpoints.add(ep) 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='results_added', title='Results added', finding_count=finding_count, test=t, engagement=engagement, url=request.build_absolute_uri(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') add_breadcrumb(parent=engagement, title="Import Scan Results", top_level=False, request=request) return render(request, 'dojo/import_scan_results.html', {'form': form, 'eid': engagement.id, })
def import_scan_results(request, eid=None, pid=None): engagement = None form = ImportScanForm() cred_form = CredMappingForm() finding_count = 0 if eid: engagement = get_object_or_404(Engagement, id=eid) cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter(engagement=engagement).order_by('cred_id') 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 form.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['file'] 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'] if not any(scan_type in code for code in ImportScanForm.SCAN_TYPE_CHOICES): raise Http404() 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) t.lead = request.user t.full_clean() t.save() tags = request.POST.getlist('tags') ts = ", ".join(tags) t.tags = ts # 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() parser = import_parser_factory(file, t) try: 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 if item.date == timezone.now().date(): item.date = t.target_start item.reporter = request.user item.last_reviewed = timezone.now() item.last_reviewed_by = request.user 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: burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=req_resp["req"], burpResponseBase64=req_resp["resp"], ) 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=item.unsaved_request, burpResponseBase64=item.unsaved_response, ) 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) item.endpoints.add(ep) item.save(false_history=True) 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='results_added', title=str(finding_count) + " findings for " + engagement.product.name, finding_count=finding_count, test=t, engagement=engagement, url=request.build_absolute_uri( 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") return render(request, 'dojo/import_scan_results.html', { 'form': form, 'product_tab': product_tab, 'custom_breadcrumb': custom_breadcrumb, 'title': title, 'cred_form': cred_form, })
def import_object_eng(request, engagement, json_data): create_test_code_review = False create_alert = False #Get the product from the engagement product = engagement.product #Retrieve the files currently set for this product object_queryset = Objects.objects.filter( product=engagement.product.id).order_by('-path') data = json.load(json_data) #Set default review status review_status_id = 1 review_status = Objects_Review.objects.get(pk=review_status_id) for file in data: print file["path"] #Save the file if the object isn't in object table file_type, found_object = find_item(file["path"], object_queryset) if found_object is None or file_type == "path": review_status_id = 1 if file_type == "path": #Copy the review status review_status_id = found_object.review_status.id #Set default review status review_status = Objects_Review.objects.get(pk=review_status_id) #if found_object is None: object = Objects(product=product, path=file["path"], review_status=review_status) object.save() found_object = object if file_type == "path": for tag in found_object.tags.all(): Tag.objects.update_tags(object, tag.name) full_url = None type = None percentUnchanged = None build_id = None if "full_url" in file: full_url = file["full_url"] if "type" in file: type = file["type"] if "percentUnchanged" in file: percentUnchanged = file["percentUnchanged"] if "build_id" in file: build_id = file["build_id"][:12] #Find the status so the appropriate action takes place if found_object.review_status.id == 2: create_alert = True elif found_object.review_status.id == 3: create_test_code_review = True create_alert = True #Save the changed files to the engagement view object_eng = Objects_Engagement(engagement=engagement, object_id=found_object, full_url=full_url, type=type, percentUnchanged=percentUnchanged, build_id=build_id) object_eng.save() #Create the notification if create_alert: create_notification( event='code_review', title='Manual Code Review Requested', description= "Manual code review requested as tracked file changes were found in the latest build.", engagement=engagement, url=request.build_absolute_uri( reverse('view_object_eng', args=(engagement.id, )))) #Create the test within the engagement if create_test_code_review: environment, env_created = Development_Environment.objects.get_or_create( name="Development") tt = Test_Type.objects.get(pk=27) #Manual code review if tt: test = Test(engagement=engagement, test_type=tt, target_start=timezone.now(), target_end=timezone.now() + timezone.timedelta(days=1), environment=environment, percent_complete=0) test.save() create_notification(event='test_added', title='Test added for Manual Code Review', test=test, engagement=engagement, url=request.build_absolute_uri( reverse('view_engagement', args=(engagement.id, ))))
def async_custom_pdf_report(self, report=None, template="None", filename='report.pdf', host=None, user=None, uri=None, finding_notes=False, finding_images=False): config = pdfkit.configuration(wkhtmltopdf=settings.WKHTMLTOPDF_PATH) selected_widgets = report_widget_factory(json_data=report.options, request=None, user=user, finding_notes=finding_notes, finding_images=finding_images, host=host) widgets = selected_widgets.values() temp = None try: report.task_id = async_custom_pdf_report.request.id report.save() toc = None toc_depth = 4 if 'table-of-contents' in selected_widgets: xsl_style_sheet_tempalte = "dojo/pdf_toc.xsl" temp = tempfile.NamedTemporaryFile() toc_settings = selected_widgets['table-of-contents'] toc_depth = toc_settings.depth toc_bytes = render_to_string(xsl_style_sheet_tempalte, {'widgets': widgets, 'depth': toc_depth, 'title': toc_settings.title}) temp.write(toc_bytes) temp.seek(0) toc = {'toc-header-text': toc_settings.title, 'xsl-style-sheet': temp.name} # default the cover to not come first by default cover_first_val = False cover = None if 'cover-page' in selected_widgets: cover_first_val = True cp = selected_widgets['cover-page'] x = urlencode({'title': cp.title, 'subtitle': cp.sub_heading, 'info': cp.meta_info}) cover = host + reverse( 'report_cover_page') + "?" + x bytes = render_to_string(template, {'widgets': widgets, 'toc_depth': toc_depth, 'host': host, 'report_name': report.name}) pdf = pdfkit.from_string(bytes, False, configuration=config, toc=toc, cover=cover, cover_first=cover_first_val) if report.file.name: with open(report.file.path, 'w') as f: f.write(pdf) f.close() else: f = ContentFile(pdf) report.file.save(filename, f) report.status = 'success' report.done_datetime = timezone.now() report.save() create_notification(event='report_created', title='Report created', description='The report "%s" is ready.' % report.name, url=uri, report=report, objowner=report.requester) except Exception as e: report.status = 'error' report.save() # email_requester(report, uri, error=e) # raise e log_generic_alert("PDF Report", "Report Creation Failure", "Make sure WKHTMLTOPDF is installed. " + str(e)) finally: if temp is not None: # deleting temp xsl file temp.close() return True
def async_docx_report(self, report=None, template="None", filename='report.docx', report_title=None, report_subtitle=None, report_info=None, context={}, uri=None): def format_fields(tpl): context['engagement'].executive_summary = Markdown( context['engagement'].executive_summary) context['engagement'].technical_summary = Markdown( context['engagement'].technical_summary) for finding in context['findings']: if finding.formatting == 'Markdown': format_func = Markdown else: format_func = RichText finding.pics = [ InlineImage(tpl, settings.MEDIA_ROOT + pic.image.name, width=Mm(150)) for pic in finding.images.all() ] finding.description = format_func(finding.description) finding.impact = format_func(finding.impact) finding.mitigation = format_func(finding.mitigation) finding.references = format_func(finding.references) import sys reload(sys) sys.setdefaultencoding('utf-8') try: report.task_id = async_docx_report.request.id report.status = 'running' report.save() d = DocxTemplate(template) format_fields(d) d.render(context) if report.file.name: d.save(report.file.path) else: rtmp = StringIO() d.save(rtmp) f = ContentFile(rtmp.getvalue()) report.file.save(filename, f) report.status = 'success' report.done_datetime = timezone.now() report.save() create_notification(event='report_created', title='Report created', description='The report "%s" is ready.' % report.name, icon='file-text', url=uri, report=report, objowner=report.requester) except Exception as e: import traceback report.status = 'error' report.save() exc_type, exc_obj, exc_tb = sys.exc_info() traceback.print_tb(exc_tb) # print(exc_type, exc_tb.tb_lineno) raise e return True
def log_generic_alert(source, title, description): create_notification(event='other', title=title, description=description, icon='bullseye', source=source)
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." t = get_object_or_404(Test, id=tid) scan_type = t.test_type.name engagement = t.engagement form = ReImportScanForm() form.initial['tags'] = [tag.name for tag in t.tags] if request.method == "POST": form = ReImportScanForm(request.POST, request.FILES) if form.is_valid(): scan_date = form.cleaned_data['scan_date'] min_sev = form.cleaned_data['minimum_severity'] file = request.FILES['file'] scan_type = t.test_type.name active = form.cleaned_data['active'] verified = form.cleaned_data['verified'] tags = request.POST.getlist('tags') ts = ", ".join(tags) t.tags = ts try: parser = import_parser_factory(file, t) except ValueError: raise Http404() try: items = parser.items original_items = t.finding_set.all().values_list("id", flat=True) new_items = [] mitigated_count = 0 finding_count = 0 finding_added_count = 0 reactivated_count = 0 for item in items: sev = item.severity if sev == 'Information' or sev == 'Informational': sev = 'Info' if Finding.SEVERITIES[sev] > Finding.SEVERITIES[min_sev]: continue if scan_type == 'Veracode Scan' or scan_type == 'Arachni Scan': find = Finding.objects.filter(title=item.title, test__id=t.id, severity=sev, numerical_severity=Finding.get_numerical_severity(sev), description=item.description ) else: find = Finding.objects.filter(title=item.title, test__id=t.id, severity=sev, numerical_severity=Finding.get_numerical_severity(sev), ) if len(find) == 1: find = find[0] if find.mitigated: # it was once fixed, but now back find.mitigated = None find.mitigated_by = None find.active = True find.verified = verified find.save() note = Notes(entry="Re-activated by %s re-upload." % scan_type, author=request.user) note.save() find.notes.add(note) reactivated_count += 1 new_items.append(find.id) else: item.test = t item.date = t.target_start item.reporter = request.user item.last_reviewed = timezone.now() item.last_reviewed_by = request.user item.verified = verified item.active = active item.save() finding_added_count += 1 new_items.append(item.id) find = item if hasattr(item, 'unsaved_req_resp') and len(item.unsaved_req_resp) > 0: for req_resp in item.unsaved_req_resp: burp_rr = BurpRawRequestResponse(finding=find, burpRequestBase64=req_resp["req"], burpResponseBase64=req_resp["resp"], ) burp_rr.clean() burp_rr.save() if item.unsaved_request is not None and item.unsaved_response is not None: burp_rr = BurpRawRequestResponse(finding=find, burpRequestBase64=item.unsaved_request, burpResponseBase64=item.unsaved_response, ) burp_rr.clean() burp_rr.save() if find: 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=t.engagement.product) find.endpoints.add(ep) if item.unsaved_tags is not None: find.tags = item.unsaved_tags # calculate the difference to_mitigate = set(original_items) - set(new_items) for finding_id in to_mitigate: finding = Finding.objects.get(id=finding_id) finding.mitigated = datetime.combine(scan_date, timezone.now().time()) 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 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='results_added', title='Results added', finding_count=finding_count, test=t, engagement=engagement, url=request.build_absolute_uri(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') add_breadcrumb(parent=t, title="Re-upload a %s" % scan_type, top_level=False, request=request) return render(request, 'dojo/import_scan_results.html', {'form': form, 'eid': engagement.id, 'additional_message': additional_message, })
def async_custom_pdf_report(self, report=None, template="None", filename='report.pdf', host=None, user=None, uri=None, finding_notes=False, finding_images=False): config = pdfkit.configuration(wkhtmltopdf=settings.WKHTMLTOPDF_PATH) selected_widgets = report_widget_factory(json_data=report.options, request=None, user=user, finding_notes=finding_notes, finding_images=finding_images, host=host) widgets = list(selected_widgets.values()) temp = None try: report.task_id = async_custom_pdf_report.request.id report.save() toc = None toc_depth = 4 if 'table-of-contents' in selected_widgets: xsl_style_sheet_tempalte = "dojo/pdf_toc.xsl" temp = tempfile.NamedTemporaryFile() toc_settings = selected_widgets['table-of-contents'] toc_depth = toc_settings.depth toc_bytes = render_to_string(xsl_style_sheet_tempalte, {'widgets': widgets, 'depth': toc_depth, 'title': toc_settings.title}) temp.write(toc_bytes) temp.seek(0) toc = {'toc-header-text': toc_settings.title, 'xsl-style-sheet': temp.name} # default the cover to not come first by default cover_first_val = False cover = None if 'cover-page' in selected_widgets: cover_first_val = True cp = selected_widgets['cover-page'] x = urlencode({'title': cp.title, 'subtitle': cp.sub_heading, 'info': cp.meta_info}) cover = host + reverse( 'report_cover_page') + "?" + x bytes = render_to_string(template, {'widgets': widgets, 'toc_depth': toc_depth, 'host': host, 'report_name': report.name}) pdf = pdfkit.from_string(bytes, False, configuration=config, toc=toc, cover=cover, cover_first=cover_first_val) if report.file.name: with open(report.file.path, 'w') as f: f.write(pdf) f.close() else: f = ContentFile(pdf) report.file.save(filename, f) report.status = 'success' report.done_datetime = timezone.now() report.save() create_notification(event='report_created', title='Report created', description='The report "%s" is ready.' % report.name, url=uri, report=report, objowner=report.requester) except Exception as e: report.status = 'error' report.save() # email_requester(report, uri, error=e) # raise e log_generic_alert("PDF Report", "Report Creation Failure", "Make sure WKHTMLTOPDF is installed. " + str(e)) finally: if temp is not None: # deleting temp xsl file temp.close() return True
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." t = get_object_or_404(Test, id=tid) scan_type = t.test_type.name engagement = t.engagement form = ReImportScanForm() form.initial['tags'] = [tag.name for tag in t.tags] if request.method == "POST": form = ReImportScanForm(request.POST, request.FILES) if form.is_valid(): scan_date = form.cleaned_data['scan_date'] min_sev = form.cleaned_data['minimum_severity'] file = request.FILES['file'] scan_type = t.test_type.name active = form.cleaned_data['active'] verified = form.cleaned_data['verified'] tags = request.POST.getlist('tags') ts = ", ".join(tags) t.tags = ts try: parser = import_parser_factory(file, t, active, verified) except ValueError: raise Http404() try: items = parser.items original_items = t.finding_set.all().values_list("id", flat=True) new_items = [] mitigated_count = 0 finding_count = 0 finding_added_count = 0 reactivated_count = 0 for item in items: sev = item.severity if sev == 'Information' or sev == 'Informational': sev = 'Info' item.severity = sev if Finding.SEVERITIES[sev] > Finding.SEVERITIES[min_sev]: continue if scan_type == 'Veracode Scan' or scan_type == 'Arachni Scan': find = Finding.objects.filter(title=item.title, test__id=t.id, severity=sev, numerical_severity=Finding.get_numerical_severity(sev), description=item.description ) else: find = Finding.objects.filter(title=item.title, test__id=t.id, severity=sev, numerical_severity=Finding.get_numerical_severity(sev), ) if len(find) == 1: find = find[0] if find.mitigated: # it was once fixed, but now back find.mitigated = None find.mitigated_by = None find.active = True find.verified = verified find.save() note = Notes(entry="Re-activated by %s re-upload." % scan_type, author=request.user) note.save() find.notes.add(note) reactivated_count += 1 new_items.append(find.id) else: item.test = t item.date = scan_date item.reporter = request.user item.last_reviewed = timezone.now() item.last_reviewed_by = request.user item.verified = verified item.active = active item.save(dedupe_option=False) finding_added_count += 1 new_items.append(item.id) find = 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=req_resp["req"].encode("utf-8"), burpResponseBase64=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=find, burpRequestBase64=item.unsaved_request.encode("utf-8"), burpResponseBase64=item.unsaved_response.encode("utf-8"), ) burp_rr.clean() burp_rr.save() if find: 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=t.engagement.product) find.endpoints.add(ep) if item.unsaved_tags is not None: find.tags = item.unsaved_tags find.save() # calculate the difference to_mitigate = set(original_items) - set(new_items) for finding_id in to_mitigate: finding = Finding.objects.get(id=finding_id) finding.mitigated = datetime.combine(scan_date, timezone.now().time()) 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 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='results_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') product_tab = Product_Tab(engagement.product.id, title="Re-upload a %s" % scan_type, tab="engagements") product_tab.setEngagement(engagement) return render(request, 'dojo/import_scan_results.html', {'form': form, 'product_tab': product_tab, 'eid': engagement.id, 'additional_message': additional_message, })
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
def save(self): data = self.validated_data skip_duplicates = data['skip_duplicates'] close_old_findings = data['close_old_findings'] test_type, created = Test_Type.objects.get_or_create( name=data.get('test_type', data['scan_type'])) environment, created = Development_Environment.objects.get_or_create( name='Development') test = Test( engagement=data['engagement'], lead=data['lead'], test_type=test_type, target_start=data['scan_date'], target_end=data['scan_date'], environment=environment, percent_complete=100) try: test.full_clean() except ValidationError: pass test.save() test.tags = u' '.join(data['tags']) try: parser = import_parser_factory(data['file'], test, data['scan_type'],) except ValueError: raise Exception('FileParser ValueError') skipped_hashcodes = [] try: for item in parser.items: if skip_duplicates: hash_code = item.compute_hash_code() if Finding.objects.filter(Q(active=True) | Q(false_p=True) | Q(duplicate=True), test__engagement__product=test.engagement.product, hash_code=hash_code).exists(): skipped_hashcodes.append(hash_code) continue sev = item.severity if sev == 'Information' or sev == 'Informational': sev = 'Info' item.severity = sev if (Finding.SEVERITIES[sev] > Finding.SEVERITIES[data['minimum_severity']]): continue item.test = test item.date = test.target_start item.reporter = self.context['request'].user item.last_reviewed = timezone.now() item.last_reviewed_by = self.context['request'].user item.active = data['active'] item.verified = data['verified'] item.save() if (hasattr(item, 'unsaved_req_resp') and len(item.unsaved_req_resp) > 0): for req_resp in item.unsaved_req_resp: burp_rr = BurpRawRequestResponse( finding=item, burpRequestBase64=req_resp["req"], burpResponseBase64=req_resp["resp"]) 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=item.unsaved_request, burpResponseBase64=item.unsaved_response) 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=test.engagement.product) item.endpoints.add(ep) # if item.unsaved_tags is not None: # item.tags = item.unsaved_tags except SyntaxError: raise Exception('Parser SyntaxError') if close_old_findings: # Close old active findings that are not reported by this scan. new_hash_codes = test.finding_set.values('hash_code') for old_finding in Finding.objects.exclude(test=test) \ .exclude(hash_code__in=new_hash_codes) \ .exclude(hash_code__in=skipped_hashcodes) \ .filter(test__engagement__product=test.engagement.product, test__test_type=test_type, active=True): old_finding.active = False old_finding.mitigated = datetime.datetime.combine( test.target_start, timezone.now().time()) old_finding.mitigated_by = self.context['request'].user old_finding.notes.create(author=self.context['request'].user, entry="This finding has been automatically closed" " as it is not present anymore in recent scans.") old_finding.save() title = 'An old finding has been closed for "{}".' \ .format(test.engagement.product.name) description = 'See <a href="{}">{}</a>' \ .format(reverse('view_finding', args=(old_finding.id, )), old_finding.title) create_notification(event='other', title=title, description=description, icon='bullseye', objowner=self.context['request'].user) return test