Exemple #1
0
def update_epic(engagement):
    logger.debug('trying to update jira EPIC for %d:%s', engagement.id,
                 engagement.name)

    if not is_jira_configured_and_enabled(engagement):
        return False

    logger.debug('config found')

    jira_project = get_jira_project(engagement)
    jira_instance = get_jira_instance(engagement)
    if jira_project.enable_engagement_epic_mapping:
        try:
            jira = get_jira_connection(jira_instance)
            j_issue = get_jira_issue(engagement)
            issue = jira.issue(j_issue.jira_id)
            issue.update(summary=engagement.name, description=engagement.name)
            return True
        except JIRAError as e:
            logger.exception(e)
            log_jira_generic_alert('Jira Engagement/Epic Update Error', str(e))
            return False
    else:
        add_error_message_to_response(
            'Push to JIRA for Epic skipped because enable_engagement_epic_mapping is not checked for this engagement'
        )

        return False
Exemple #2
0
def import_endpoint_meta(request, pid):
    product = get_object_or_404(Product, id=pid)
    form = ImportEndpointMetaForm()
    if request.method == 'POST':
        form = ImportEndpointMetaForm(request.POST, request.FILES)
        if form.is_valid():
            file = request.FILES.get('file', None)
            # Make sure size is not too large
            if file and is_scan_file_too_large(file):
                messages.add_message(
                    request,
                    messages.ERROR,
                    "Report file is too large. Maximum supported size is {} MB".format(settings.SCAN_FILE_MAX_SIZE),
                    extra_tags='alert-danger')

            create_endpoints = form.cleaned_data['create_endpoints']
            create_tags = form.cleaned_data['create_tags']
            create_dojo_meta = form.cleaned_data['create_dojo_meta']

            try:
                endpoint_meta_import(file, product, create_endpoints, create_tags, create_dojo_meta, origin='UI', request=request)
            except Exception as e:
                logger.exception(e)
                add_error_message_to_response('An exception error occurred during the report import:%s' % str(e))
            return HttpResponseRedirect(reverse('endpoint') + "?product=" + pid)

    add_breadcrumb(title="Endpoint Meta Importer", top_level=False, request=request)
    product_tab = Product_Tab(product.id, title="Endpoint Meta Importer", tab="endpoints")
    return render(request, 'dojo/endpoint_meta_importer.html', {
        'product_tab': product_tab,
        'form': form,
    })
Exemple #3
0
def get_jira_connection_raw(jira_server, jira_username, jira_password):
    try:
        jira = JIRA(server=jira_server,
                    basic_auth=(jira_username, jira_password),
                    options={"verify": settings.JIRA_SSL_VERIFY},
                    max_retries=0)

        logger.debug('logged in to JIRA ' '%s' ' successfully', jira_server)

        return jira
    except JIRAError as e:
        logger.exception(e)

        if e.status_code in [401, 403]:
            log_jira_generic_alert('JIRA Authentication Error', e)
        else:
            log_jira_generic_alert('Unknown JIRA Connection Error', e)

        add_error_message_to_response(
            'Unable to authenticate to JIRA. Please check the URL, username, password, captcha challenge, Network connection. Details in alert on top right. '
            + str(e))
        raise e

    except requests.exceptions.RequestException as re:
        logger.exception(re)
        log_jira_generic_alert('Unknown JIRA Connection Error', re)

        add_error_message_to_response(
            'Unable to authenticate to JIRA. Please check the URL, username, password, captcha challenge, Network connection. Details in alert on top right. '
            + str(re))

        raise re
Exemple #4
0
def add_epic(engagement):
    logger.debug('trying to create a new jira EPIC for %d:%s', engagement.id,
                 engagement.name)

    if not is_jira_configured_and_enabled(engagement):
        return False

    logger.debug('config found')

    jira_project = get_jira_project(engagement)
    jira_instance = get_jira_instance(engagement)
    if jira_project.enable_engagement_epic_mapping:
        issue_dict = {
            'project': {
                'key': jira_project.project_key
            },
            'summary': engagement.name,
            'description': engagement.name,
            'issuetype': {
                'name': 'Epic'
            },
            get_epic_name_field_name(jira_instance): engagement.name,
        }
        try:
            jira = get_jira_connection(jira_instance)
            logger.debug('add_epic: %s', issue_dict)
            new_issue = jira.create_issue(fields=issue_dict)
            j_issue = JIRA_Issue(jira_id=new_issue.id,
                                 jira_key=new_issue.key,
                                 engagement=engagement,
                                 jira_project=jira_project)
            j_issue.save()
            return True
        except JIRAError as e:
            # should we try to parse the errors as JIRA is very strange in how it responds.
            # for example a non existent project_key leads to "project key is required" which sounds like something is missing
            # but it's just a non-existent project (or maybe a project for which the account has no create permission?)
            #
            # {"errorMessages":[],"errors":{"project":"project is required"}}
            logger.exception(e)
            error = str(e)
            message = ""
            if "customfield" in error:
                message = "The 'Epic name id' in your DefectDojo Jira Configuration does not appear to be correct. Please visit, " + jira_instance.url + \
                    "/rest/api/2/field and search for Epic Name. Copy the number out of cf[number] and place in your DefectDojo settings for Jira and try again. For example, if your results are cf[100001] then copy 100001 and place it in 'Epic name id'. (Your Epic Id will be different.) \n\n"

            log_jira_generic_alert('Jira Engagement/Epic Creation Error',
                                   message + error)
            return False
    else:
        add_error_message_to_response(
            'Push to JIRA for Epic skipped because enable_engagement_epic_mapping is not checked for this engagement'
        )
        return False
Exemple #5
0
def get_jira_meta(jira, jira_project):
    meta = jira.createmeta(
        projectKeys=jira_project.project_key,
        issuetypeNames=jira_project.jira_instance.default_issue_type,
        expand="projects.issuetypes.fields")

    meta_data_error = False
    if len(meta['projects']) == 0:
        # non-existent project, or no permissions
        # [09/Nov/2020 21:04:22] DEBUG [dojo.jira_link.helper:595] get_jira_meta: {
        #     "expand": "projects",
        #     "projects": []
        # }
        meta_data_error = True
        message = 'unable to retrieve metadata from JIRA %s for project %s. Invalid project key or no permissions to this project?' % (
            jira_project.jira_instance, jira_project.project_key)

    elif len(meta['projects'][0]['issuetypes']) == 0:
        # default issue type doesn't exist in project
        # [09/Nov/2020 21:09:03] DEBUG [dojo.jira_link.helper:595] get_jira_meta: {
        #     "expand": "projects",
        #     "projects": [
        #         {
        #             "expand": "issuetypes",
        #             "self": "https://jira-uat.com/rest/api/2/project/1212",
        #             "id": "1212",
        #             "key": "ISO",
        #             "name": "ISO ISMS",
        #             "avatarUrls": {
        #                 "48x48": "https://jira-uat.com/secure/projectavatar?pid=14431&avatarId=17200",
        #                 "24x24": "https://jira-uat.com/secure/projectavatar?size=small&pid=14431&avatarId=17200",
        #                 "16x16": "https://jira-uat.com/secure/projectavatar?size=xsmall&pid=14431&avatarId=17200",
        #                 "32x32": "https://jira-uat.com/secure/projectavatar?size=medium&pid=14431&avatarId=17200"
        #             },
        #             "issuetypes": []
        #         }
        #     ]
        # }
        meta_data_error = True
        message = 'unable to retrieve metadata from JIRA %s for issuetype %s in project %s. Invalid default issue type configured in Defect Dojo?' % (
            jira_project.jira_instance,
            jira_project.jira_instance.default_issue_type,
            jira_project.project_key)

    if meta_data_error:
        logger.warn(message)
        logger.warn("get_jira_meta: %s",
                    json.dumps(meta, indent=4))  # this is None safe

        add_error_message_to_response(message)

        raise JIRAError(text=message)
    else:
        return meta
Exemple #6
0
def close_epic(eng, push_to_jira):
    engagement = eng
    if not is_jira_enabled():
        return False

    if not is_jira_configured_and_enabled(engagement):
        return False

    jira_project = get_jira_project(engagement)
    jira_instance = get_jira_instance(engagement)
    if jira_project.enable_engagement_epic_mapping:
        if push_to_jira:
            try:
                jissue = get_jira_issue(eng)
                if jissue is None:
                    logger.warn("JIRA close epic failed: no issue found")
                    return False

                req_url = jira_instance.url + '/rest/api/latest/issue/' + \
                    jissue.jira_id + '/transitions'
                json_data = {
                    'transition': {
                        'id': jira_instance.close_status_key
                    }
                }
                r = requests.post(url=req_url,
                                  auth=HTTPBasicAuth(jira_instance.username,
                                                     jira_instance.password),
                                  json=json_data)
                if r.status_code != 204:
                    logger.warn("JIRA close epic failed with error: {}".format(
                        r.text))
                    return False
                return True
            except JIRAError as e:
                logger.exception(e)
                log_jira_generic_alert('Jira Engagement/Epic Close Error',
                                       str(e))
                return False
    else:
        add_error_message_to_response(
            'Push to JIRA for Epic skipped because enable_engagement_epic_mapping is not checked for this engagement'
        )
        return False
Exemple #7
0
def delete_jira(request, tid):
    jira_instance = get_object_or_404(JIRA_Instance, pk=tid)
    # eng = test.engagement
    # TODO Make Form
    form = DeleteJIRAInstanceForm(instance=jira_instance)

    if request.method == 'POST':
        if 'id' in request.POST and str(
                jira_instance.id) == request.POST['id']:
            form = DeleteJIRAInstanceForm(request.POST, instance=jira_instance)
            if form.is_valid():
                try:
                    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: %s' %
                        jira_instance.configuration_name,
                        description='JIRA "%s" was deleted by %s' %
                        (jira_instance.configuration_name, request.user),
                        url=request.build_absolute_uri(reverse('jira')),
                    )
                    return HttpResponseRedirect(reverse('jira'))
                except Exception as e:
                    add_error_message_to_response(
                        'Unable to delete JIRA Instance, probably because it is used by JIRA Issues: %s'
                        % str(e))

    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,
        })
Exemple #8
0
def import_scan_results(request, eid=None, pid=None):
    engagement = None
    form = ImportScanForm()
    cred_form = CredMappingForm()
    finding_count = 0
    jform = None
    user = request.user

    if eid:
        engagement = get_object_or_404(Engagement, id=eid)
        engagement_or_product = engagement
        cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter(engagement=engagement).order_by('cred_id')
    elif pid:
        product = get_object_or_404(Product, id=pid)
        engagement_or_product = product
    elif not user.is_staff:
        raise PermissionDenied

    user_has_permission_or_403(user, engagement_or_product, Permissions.Import_Scan_Result)

    push_all_jira_issues = jira_helper.is_push_all_issues(engagement_or_product)

    if request.method == "POST":
        form = ImportScanForm(request.POST, request.FILES)
        cred_form = CredMappingForm(request.POST)
        cred_form.fields["cred_user"].queryset = Cred_Mapping.objects.filter(
            engagement=engagement).order_by('cred_id')

        if jira_helper.get_jira_project(engagement_or_product):
            jform = JIRAImportScanForm(request.POST, push_all=push_all_jira_issues, prefix='jiraform')
            logger.debug('jform valid: %s', jform.is_valid())
            logger.debug('jform errors: %s', jform.errors)

        if form.is_valid() and (jform is None or jform.is_valid()):
            scan = request.FILES.get('file', None)
            scan_date = form.cleaned_data['scan_date']
            minimum_severity = form.cleaned_data['minimum_severity']
            active = form.cleaned_data['active']
            verified = form.cleaned_data['verified']
            scan_type = request.POST['scan_type']
            tags = form.cleaned_data['tags']
            version = form.cleaned_data['version']
            branch_tag = form.cleaned_data.get('branch_tag', None)
            build_id = form.cleaned_data.get('build_id', None)
            commit_hash = form.cleaned_data.get('commit_hash', None)
            api_scan_configuration = form.cleaned_data.get('api_scan_configuration', None)
            service = form.cleaned_data.get('service', None)
            close_old_findings = form.cleaned_data.get('close_old_findings', None)
            # Will save in the provided environment or in the `Development` one if absent
            environment_id = request.POST.get('environment', 'Development')
            environment = Development_Environment.objects.get(id=environment_id)

            group_by = form.cleaned_data.get('group_by', None)

            # TODO move to form validation?
            if scan and is_scan_file_too_large(scan):
                messages.add_message(request,
                                     messages.ERROR,
                                     "Report file is too large. Maximum supported size is {} MB".format(settings.SCAN_FILE_MAX_SIZE),
                                     extra_tags='alert-danger')
                return HttpResponseRedirect(reverse('import_scan_results', args=(engagement,)))

            # Allows for a test to be imported with an engagement created on the fly
            if engagement is None:
                engagement = Engagement()
                engagement.name = "AdHoc Import - " + strftime("%a, %d %b %Y %X", timezone.now().timetuple())
                engagement.threat_model = False
                engagement.api_test = False
                engagement.pen_test = False
                engagement.check_list = False
                engagement.target_start = timezone.now().date()
                engagement.target_end = timezone.now().date()
                engagement.product = product
                engagement.active = True
                engagement.status = 'In Progress'
                engagement.version = version
                engagement.branch_tag = branch_tag
                engagement.build_id = build_id
                engagement.commit_hash = commit_hash
                engagement.save()

            # can't use helper as when push_all_jira_issues is True, the checkbox gets disabled and is always false
            # push_to_jira = jira_helper.is_push_to_jira(new_finding, jform.cleaned_data.get('push_to_jira'))
            push_to_jira = push_all_jira_issues or (jform and jform.cleaned_data.get('push_to_jira'))
            error = False

            # Save newly added endpoints
            added_endpoints = save_endpoints_to_add(form.endpoints_to_add_list, engagement.product)

            try:
                importer = Importer()
                test, finding_count, closed_finding_count = importer.import_scan(scan, scan_type, engagement, user, environment, active=active, verified=verified, tags=tags,
                            minimum_severity=minimum_severity, endpoints_to_add=list(form.cleaned_data['endpoints']) + added_endpoints, scan_date=scan_date,
                            version=version, branch_tag=branch_tag, build_id=build_id, commit_hash=commit_hash, push_to_jira=push_to_jira,
                            close_old_findings=close_old_findings, group_by=group_by, api_scan_configuration=api_scan_configuration, service=service)

                message = f'{scan_type} processed a total of {finding_count} findings'

                if close_old_findings:
                    message = message + ' and closed %d findings' % (closed_finding_count)

                message = message + "."

                add_success_message_to_response(message)

            except Exception as e:
                logger.exception(e)
                add_error_message_to_response('An exception error occurred during the report import:%s' % str(e))
                error = True

            # Save the credential to the test
            if cred_form.is_valid():
                if cred_form.cleaned_data['cred_user']:
                    # Select the credential mapping object from the selected list and only allow if the credential is associated with the product
                    cred_user = Cred_Mapping.objects.filter(
                        pk=cred_form.cleaned_data['cred_user'].id,
                        engagement=eid).first()

                    new_f = cred_form.save(commit=False)
                    new_f.test = test
                    new_f.cred_id = cred_user.cred_id
                    new_f.save()

            if not error:
                return HttpResponseRedirect(
                    reverse('view_test', args=(test.id, )))

    prod_id = None
    custom_breadcrumb = None
    title = "Import Scan Results"
    if engagement:
        prod_id = engagement.product.id
        product_tab = Product_Tab(prod_id, title=title, tab="engagements")
        product_tab.setEngagement(engagement)
    else:
        prod_id = pid
        custom_breadcrumb = {"", ""}
        product_tab = Product_Tab(prod_id, title=title, tab="findings")

    if jira_helper.get_jira_project(engagement_or_product):
        jform = JIRAImportScanForm(push_all=push_all_jira_issues, prefix='jiraform')

    form.fields['endpoints'].queryset = Endpoint.objects.filter(product__id=product_tab.product.id)
    form.fields['api_scan_configuration'].queryset = Product_API_Scan_Configuration.objects.filter(product__id=product_tab.product.id)
    return render(request,
        'dojo/import_scan_results.html',
        {'form': form,
         'product_tab': product_tab,
         'engagement_or_product': engagement_or_product,
         'custom_breadcrumb': custom_breadcrumb,
         'title': title,
         'cred_form': cred_form,
         'jform': jform,
         'scan_types': get_scan_types_sorted(),
         })
Exemple #9
0
def re_import_scan_results(request, tid):
    additional_message = "When re-uploading a scan, any findings not found in original scan will be updated as " \
                         "mitigated.  The process attempts to identify the differences, however manual verification " \
                         "is highly recommended."
    test = get_object_or_404(Test, id=tid)
    # by default we keep a trace of the scan_type used to create the test
    # if it's not here, we use the "name" of the test type
    # this feature exists to provide custom label for tests for some parsers
    if test.scan_type:
        scan_type = test.scan_type
    else:
        scan_type = test.test_type.name
    engagement = test.engagement
    form = ReImportScanForm(test=test)
    jform = None
    jira_project = jira_helper.get_jira_project(test)
    push_all_jira_issues = jira_helper.is_push_all_issues(test)

    # Decide if we need to present the Push to JIRA form
    if get_system_setting('enable_jira') and jira_project:
        jform = JIRAImportScanForm(push_all=push_all_jira_issues,
                                   prefix='jiraform')

    if request.method == "POST":
        form = ReImportScanForm(request.POST, request.FILES, test=test)
        if jira_project:
            jform = JIRAImportScanForm(request.POST,
                                       push_all=push_all_jira_issues,
                                       prefix='jiraform')
        if form.is_valid() and (jform is None or jform.is_valid()):
            scan_date = form.cleaned_data['scan_date']

            minimum_severity = form.cleaned_data['minimum_severity']
            scan = request.FILES.get('file', None)
            active = form.cleaned_data['active']
            verified = form.cleaned_data['verified']
            tags = form.cleaned_data['tags']
            version = form.cleaned_data.get('version', None)
            branch_tag = form.cleaned_data.get('branch_tag', None)
            build_id = form.cleaned_data.get('build_id', None)
            commit_hash = form.cleaned_data.get('commit_hash', None)
            api_scan_configuration = form.cleaned_data.get(
                'api_scan_configuration', None)
            service = form.cleaned_data.get('service', None)

            endpoints_to_add = None  # not available on reimport UI

            close_old_findings = form.cleaned_data.get('close_old_findings',
                                                       True)

            group_by = form.cleaned_data.get('group_by', None)

            # Tags are replaced, same behaviour as with django-tagging
            test.tags = tags
            test.version = version
            if scan and is_scan_file_too_large(scan):
                messages.add_message(
                    request,
                    messages.ERROR,
                    "Report file is too large. Maximum supported size is {} MB"
                    .format(settings.SCAN_FILE_MAX_SIZE),
                    extra_tags='alert-danger')
                return HttpResponseRedirect(
                    reverse('re_import_scan_results', args=(test.id, )))

            push_to_jira = push_all_jira_issues or (
                jform and jform.cleaned_data.get('push_to_jira'))
            error = False
            finding_count, new_finding_count, closed_finding_count, reactivated_finding_count, untouched_finding_count = 0, 0, 0, 0, 0
            reimporter = ReImporter()
            try:
                test, finding_count, new_finding_count, closed_finding_count, reactivated_finding_count, untouched_finding_count, _ = \
                    reimporter.reimport_scan(scan, scan_type, test, active=active, verified=verified,
                                                tags=None, minimum_severity=minimum_severity,
                                                endpoints_to_add=endpoints_to_add, scan_date=scan_date,
                                                version=version, branch_tag=branch_tag, build_id=build_id,
                                                commit_hash=commit_hash, push_to_jira=push_to_jira,
                                                close_old_findings=close_old_findings, group_by=group_by,
                                                api_scan_configuration=api_scan_configuration, service=service)
            except Exception as e:
                logger.exception(e)
                add_error_message_to_response(
                    'An exception error occurred during the report import:%s' %
                    str(e))
                error = True

            if not error:
                message = construct_imported_message(
                    scan_type,
                    finding_count,
                    new_finding_count=new_finding_count,
                    closed_finding_count=closed_finding_count,
                    reactivated_finding_count=reactivated_finding_count,
                    untouched_finding_count=untouched_finding_count)
                add_success_message_to_response(message)

            return HttpResponseRedirect(reverse('view_test', args=(test.id, )))

    product_tab = Product_Tab(engagement.product.id,
                              title="Re-upload a %s" % scan_type,
                              tab="engagements")
    product_tab.setEngagement(engagement)
    form.fields['endpoints'].queryset = Endpoint.objects.filter(
        product__id=product_tab.product.id)
    form.initial['api_scan_configuration'] = test.api_scan_configuration
    form.fields[
        'api_scan_configuration'].queryset = Product_API_Scan_Configuration.objects.filter(
            product__id=product_tab.product.id)
    return render(
        request, 'dojo/import_scan_results.html', {
            'form': form,
            'product_tab': product_tab,
            'eid': engagement.id,
            'additional_message': additional_message,
            'jform': jform,
            'scan_types': get_scan_types_sorted(),
        })
Exemple #10
0
def add_temp_finding(request, tid, fid):
    jform = None
    test = get_object_or_404(Test, id=tid)
    finding = get_object_or_404(Finding_Template, id=fid)
    findings = Finding_Template.objects.all()
    push_all_jira_issues = jira_helper.is_push_all_issues(finding)

    if request.method == 'POST':

        form = AddFindingForm(request.POST,
                              req_resp=None,
                              product=test.engagement.product)
        if jira_helper.get_jira_project(test):
            jform = JIRAFindingForm(
                push_all=jira_helper.is_push_all_issues(test),
                prefix='jiraform',
                jira_project=jira_helper.get_jira_project(test),
                finding_form=form)
            logger.debug('jform valid: %s', jform.is_valid())

        if (form['active'].value() is False or form['false_p'].value()
            ) and form['duplicate'].value() is False:
            closing_disabled = Note_Type.objects.filter(
                is_mandatory=True, is_active=True).count()
            if closing_disabled != 0:
                error_inactive = ValidationError(
                    'Can not set a finding as inactive without adding all mandatory notes',
                    code='not_active_or_false_p_true')
                error_false_p = ValidationError(
                    'Can not set a finding as false positive without adding all mandatory notes',
                    code='not_active_or_false_p_true')
                if form['active'].value() is False:
                    form.add_error('active', error_inactive)
                if form['false_p'].value():
                    form.add_error('false_p', error_false_p)
                messages.add_message(
                    request,
                    messages.ERROR,
                    'Can not set a finding as inactive or false positive without adding all mandatory notes',
                    extra_tags='alert-danger')
        if form.is_valid():
            finding.last_used = timezone.now()
            finding.save()
            new_finding = form.save(commit=False)
            new_finding.test = test
            new_finding.reporter = request.user
            new_finding.numerical_severity = Finding.get_numerical_severity(
                new_finding.severity)
            new_finding.date = form.cleaned_data['date'] or datetime.today()
            finding_helper.update_finding_status(new_finding, request.user)

            new_finding.save(dedupe_option=False, false_history=False)

            # Save and add new endpoints
            finding_helper.add_endpoints(new_finding, form)

            new_finding.save(false_history=True)
            if 'jiraform-push_to_jira' in request.POST:
                jform = JIRAFindingForm(
                    request.POST,
                    prefix='jiraform',
                    instance=new_finding,
                    push_all=push_all_jira_issues,
                    jira_project=jira_helper.get_jira_project(test),
                    finding_form=form)
                if jform.is_valid():
                    if jform.cleaned_data.get('push_to_jira'):
                        jira_helper.push_to_jira(new_finding)
                else:
                    add_error_message_to_response(
                        'jira form validation failed: %s' % jform.errors)

            messages.add_message(request,
                                 messages.SUCCESS,
                                 'Finding from template added successfully.',
                                 extra_tags='alert-success')

            return HttpResponseRedirect(reverse('view_test', args=(test.id, )))
        else:
            messages.add_message(
                request,
                messages.ERROR,
                'The form has errors, please correct them below.',
                extra_tags='alert-danger')

    else:
        form = AddFindingForm(req_resp=None,
                              product=test.engagement.product,
                              initial={
                                  'active': False,
                                  'date': timezone.now().date(),
                                  'verified': False,
                                  'false_p': False,
                                  'duplicate': False,
                                  'out_of_scope': False,
                                  'title': finding.title,
                                  'description': finding.description,
                                  'cwe': finding.cwe,
                                  'severity': finding.severity,
                                  'mitigation': finding.mitigation,
                                  'impact': finding.impact,
                                  'references': finding.references,
                                  'numerical_severity':
                                  finding.numerical_severity
                              })

        if jira_helper.get_jira_project(test):
            jform = JIRAFindingForm(
                push_all=jira_helper.is_push_all_issues(test),
                prefix='jiraform',
                jira_project=jira_helper.get_jira_project(test),
                finding_form=form)

    # logger.debug('form valid: %s', form.is_valid())
    # logger.debug('jform valid: %s', jform.is_valid())
    # logger.debug('form errors: %s', form.errors)
    # logger.debug('jform errors: %s', jform.errors)
    # logger.debug('jform errors: %s', vars(jform))

    product_tab = Product_Tab(test.engagement.product.id,
                              title="Add Finding",
                              tab="engagements")
    product_tab.setEngagement(test.engagement)
    return render(
        request, 'dojo/add_findings.html', {
            'form': form,
            'product_tab': product_tab,
            'jform': jform,
            'findings': findings,
            'temp': True,
            'fid': finding.id,
            'tid': test.id,
            'test': test,
        })
Exemple #11
0
def add_findings(request, tid):
    test = Test.objects.get(id=tid)
    form_error = False
    jform = None
    form = AddFindingForm(initial={'date': timezone.now().date()},
                          req_resp=None,
                          product=test.engagement.product)
    push_all_jira_issues = jira_helper.is_push_all_issues(test)
    use_jira = jira_helper.get_jira_project(test) is not None

    if request.method == 'POST':
        form = AddFindingForm(request.POST,
                              req_resp=None,
                              product=test.engagement.product)
        if (form['active'].value() is False or form['false_p'].value()
            ) and form['duplicate'].value() is False:
            closing_disabled = Note_Type.objects.filter(
                is_mandatory=True, is_active=True).count()
            if closing_disabled != 0:
                error_inactive = ValidationError(
                    'Can not set a finding as inactive without adding all mandatory notes',
                    code='inactive_without_mandatory_notes')
                error_false_p = ValidationError(
                    'Can not set a finding as false positive without adding all mandatory notes',
                    code='false_p_without_mandatory_notes')
                if form['active'].value() is False:
                    form.add_error('active', error_inactive)
                if form['false_p'].value():
                    form.add_error('false_p', error_false_p)
                messages.add_message(
                    request,
                    messages.ERROR,
                    'Can not set a finding as inactive or false positive without adding all mandatory notes',
                    extra_tags='alert-danger')
        if use_jira:
            jform = JIRAFindingForm(
                request.POST,
                prefix='jiraform',
                push_all=push_all_jira_issues,
                jira_project=jira_helper.get_jira_project(test),
                finding_form=form)

        if form.is_valid() and (jform is None or jform.is_valid()):
            if jform:
                logger.debug('jform.jira_issue: %s',
                             jform.cleaned_data.get('jira_issue'))
                logger.debug('jform.push_to_jira: %s',
                             jform.cleaned_data.get('push_to_jira'))

            new_finding = form.save(commit=False)
            new_finding.test = test
            new_finding.reporter = request.user
            new_finding.numerical_severity = Finding.get_numerical_severity(
                new_finding.severity)
            new_finding.tags = form.cleaned_data['tags']
            new_finding.save(dedupe_option=False, push_to_jira=False)

            # Save and add new endpoints
            finding_helper.add_endpoints(new_finding, form)

            # Push to jira?
            push_to_jira = False
            jira_message = None
            if jform and jform.is_valid():
                # can't use helper as when push_all_jira_issues is True, the checkbox gets disabled and is always false
                # push_to_jira = jira_helper.is_push_to_jira(new_finding, jform.cleaned_data.get('push_to_jira'))
                push_to_jira = push_all_jira_issues or jform.cleaned_data.get(
                    'push_to_jira')

                # if the jira issue key was changed, update database
                new_jira_issue_key = jform.cleaned_data.get('jira_issue')
                if new_finding.has_jira_issue:
                    jira_issue = new_finding.jira_issue

                    # everything in DD around JIRA integration is based on the internal id of the issue in JIRA
                    # instead of on the public jira issue key.
                    # I have no idea why, but it means we have to retrieve the issue from JIRA to get the internal JIRA id.
                    # we can assume the issue exist, which is already checked in the validation of the jform

                    if not new_jira_issue_key:
                        jira_helper.finding_unlink_jira(request, new_finding)
                        jira_message = 'Link to JIRA issue removed successfully.'

                    elif new_jira_issue_key != new_finding.jira_issue.jira_key:
                        jira_helper.finding_unlink_jira(request, new_finding)
                        jira_helper.finding_link_jira(request, new_finding,
                                                      new_jira_issue_key)
                        jira_message = 'Changed JIRA link successfully.'
                else:
                    logger.debug('finding has no jira issue yet')
                    if new_jira_issue_key:
                        logger.debug(
                            'finding has no jira issue yet, but jira issue specified in request. trying to link.'
                        )
                        jira_helper.finding_link_jira(request, new_finding,
                                                      new_jira_issue_key)
                        jira_message = 'Linked a JIRA issue successfully.'

            new_finding.save(false_history=True, push_to_jira=push_to_jira)
            create_notification(event='other',
                                title='Addition of %s' % new_finding.title,
                                finding=new_finding,
                                description='Finding "%s" was added by %s' %
                                (new_finding.title, request.user),
                                url=request.build_absolute_uri(
                                    reverse('view_finding',
                                            args=(new_finding.id, ))),
                                icon="exclamation-triangle")

            if 'request' in form.cleaned_data or 'response' in form.cleaned_data:
                burp_rr = BurpRawRequestResponse(
                    finding=new_finding,
                    burpRequestBase64=base64.b64encode(
                        form.cleaned_data['request'].encode()),
                    burpResponseBase64=base64.b64encode(
                        form.cleaned_data['response'].encode()),
                )
                burp_rr.clean()
                burp_rr.save()

            if '_Finished' in request.POST:
                return HttpResponseRedirect(
                    reverse('view_test', args=(test.id, )))
            else:
                return HttpResponseRedirect(
                    reverse('add_findings', args=(test.id, )))
        else:
            form_error = True
            add_error_message_to_response(
                'The form has errors, please correct them below.')
            add_field_errors_to_response(jform)
            add_field_errors_to_response(form)

    else:
        if use_jira:
            jform = JIRAFindingForm(
                push_all=jira_helper.is_push_all_issues(test),
                prefix='jiraform',
                jira_project=jira_helper.get_jira_project(test),
                finding_form=form)

    product_tab = Product_Tab(test.engagement.product.id,
                              title="Add Finding",
                              tab="engagements")
    product_tab.setEngagement(test.engagement)
    return render(
        request, 'dojo/add_findings.html', {
            'form': form,
            'product_tab': product_tab,
            'test': test,
            'temp': False,
            'tid': tid,
            'form_error': form_error,
            'jform': jform,
        })
Exemple #12
0
def endpoint_bulk_update_all(request, pid=None):
    if request.method == "POST":
        endpoints_to_update = request.POST.getlist('endpoints_to_update')
        finds = Endpoint.objects.filter(
            id__in=endpoints_to_update).order_by("endpoint_meta__product__id")
        total_endpoint_count = finds.count()

        if request.POST.get('delete_bulk_endpoints') and endpoints_to_update:

            if pid is None:
                if not request.user.is_staff:
                    raise PermissionDenied
            else:
                product = get_object_or_404(Product, id=pid)
                user_has_permission_or_403(request.user, product,
                                           Permissions.Endpoint_Delete)

            finds = get_authorized_endpoints(Permissions.Endpoint_Delete,
                                             finds, request.user)

            skipped_endpoint_count = total_endpoint_count - finds.count()
            deleted_endpoint_count = finds.count()

            product_calc = list(
                Product.objects.filter(
                    endpoint__id__in=endpoints_to_update).distinct())
            finds.delete()
            for prod in product_calc:
                calculate_grade(prod)

            if skipped_endpoint_count > 0:
                add_error_message_to_response(
                    'Skipped deletion of {} endpoints because you are not authorized.'
                    .format(skipped_endpoint_count))

            if deleted_endpoint_count > 0:
                messages.add_message(
                    request,
                    messages.SUCCESS,
                    'Bulk delete of {} endpoints was successful.'.format(
                        deleted_endpoint_count),
                    extra_tags='alert-success')
        else:
            if endpoints_to_update:

                if pid is None:
                    if not request.user.is_staff:
                        raise PermissionDenied
                else:
                    product = get_object_or_404(Product, id=pid)
                    user_has_permission_or_403(request.user, product,
                                               Permissions.Finding_Edit)

                finds = get_authorized_endpoints(Permissions.Endpoint_Edit,
                                                 finds, request.user)

                skipped_endpoint_count = total_endpoint_count - finds.count()
                updated_endpoint_count = finds.count()

                if skipped_endpoint_count > 0:
                    add_error_message_to_response(
                        'Skipped mitigation of {} endpoints because you are not authorized.'
                        .format(skipped_endpoint_count))

                for endpoint in finds:
                    endpoint.mitigated = not endpoint.mitigated
                    endpoint.save()

                if updated_endpoint_count > 0:
                    messages.add_message(
                        request,
                        messages.SUCCESS,
                        'Bulk mitigation of {} endpoints was successful.'.
                        format(updated_endpoint_count),
                        extra_tags='alert-success')
            else:
                messages.add_message(
                    request,
                    messages.ERROR,
                    'Unable to process bulk update. Required fields were not selected.',
                    extra_tags='alert-danger')
    return HttpResponseRedirect(reverse('endpoint', args=()))