示例#1
0
    def list_multiple_test_types(self, url, api_key, test_types, **kwargs):
        # Create parameters to be requested
        request_params = kwargs
        API_URL = url + '/api/v2'
        FINDINGS_URL = API_URL + '/findings/'

        # Get list of json responses
        json_out_list = list()
        for test_type in test_types:
            request_params['test__test_type'] = test_type
            response = Util().request_apiv2('GET',
                                            FINDINGS_URL,
                                            api_key,
                                            params=request_params)
            json_out_list.append(json.loads(response.text))

        # Merge responses
        json_out_result = dict()
        json_out_result['count'] = 0
        json_out_result['results'] = list()
        for json_out in json_out_list:
            for finding in json_out['results']:
                json_out_result['count'] += 1
                json_out_result['results'].append(finding)

        # Make a request passing the list of test_types so that the url at the tool output works properly
        request_params['test__test_type'] = test_types
        response = Util().request_apiv2('GET',
                                        FINDINGS_URL,
                                        api_key,
                                        params=request_params)
        # Replace the response body with the one we created
        type(response).text = PropertyMock(
            return_value=json.dumps(json_out_result))
        return response
    def list(self, url, api_key, finding_id=None, test_id=None, product_id=None, engagement_id=None,
             test_type=None, active=None, closed=None, valid=None, scope=None, limit=None, **kwargs):
        # Create parameters to be requested
        request_params = dict()
        API_URL = url+'/api/v2'
        FINDINGS_URL = API_URL+'/findings/'
        if finding_id is not None:
            request_params['id'] = finding_id
        if test_id is not None:
            request_params['test'] = test_id
        if product_id is not None:
            request_params['test__engagement__product'] = product_id
        if engagement_id is not None:
            request_params['test__engagement'] = engagement_id
        if test_type is not None:
        # In order to filter test_type we need to get its ID via API
            temp_params = dict()
            temp_params['name'] = test_type
            # Make a get request to /test_types passing the test_type as parameter
            temp_response = Util().request_apiv2('GET', API_URL+'/test_types/', api_key, params=temp_params)
            # Tranform the above response in json and get the id
            test_type_id = json.loads(temp_response.text)['results'][0]['id']
            # Add to request_params
            request_params['test__test_type'] = test_type_id
        if active is not None:
            if active is True:
                request_params['active'] = 2
            elif active is False:
                request_params['active'] = 3
        if closed:
            request_params['is_Mitigated'] = True
        if valid is not None:
            if valid is True:
                request_params['false_p'] = 3
            elif valid is False:
                request_params['false_p'] = 2
        if scope is not None:
            if scope is True:
                request_params['out_of_scope'] = 3
            elif scope is False:
                request_params['out_of_scope'] = 2
        if limit is not None:
            request_params['limit'] = limit
        else:
            # Make a request to API getting only one finding to retrieve the total amount of findings
            temp_params = request_params.copy()
            temp_params['url'] = url
            temp_params['api_key'] = api_key
            temp_params['limit'] = 1
            temp_response = self.list(**temp_params)
            limit = int(json.loads(temp_response.text)['count'])
            request_params['limit'] = limit

        # Make request
        response = Util().request_apiv2('GET', FINDINGS_URL, api_key, params=request_params)
        return response
示例#3
0
    def update(self, url, api_key, engagement_id, name=None, desc=None, product_id=None, lead_id=None,
               start_date=None, end_date=None, engagement_type=None, repo_url=None, branch_tag=None,
               product_version=None, status=None, **kwargs):
        # Prepare JSON data to be send
        request_json = dict()
        API_URL = url+'/api/v2'
        ENGAGEMENTS_URL = API_URL+'/engagements/'
        ENGAGEMENTS_ID_URL = ENGAGEMENTS_URL+engagement_id+'/'
        if name is not None:
            request_json['name'] = name
        if desc is not None:
            request_json['description'] = desc
        if product_id is not None:
            request_json['product'] = product_id
        if lead_id is not None:
            request_json['lead'] = lead_id
        if start_date is not None:
            request_json['target_start'] = start_date
        if end_date is not None:
            request_json['target_end'] = end_date
        if engagement_type is not None:
            request_json['engagement_type'] = engagement_type
        if repo_url is not None:
            request_json['source_code_management_uri'] = repo_url
        if branch_tag is not None:
            request_json['branch_tag'] = branch_tag
        if product_version is not None:
            request_json['version'] = product_version
        if status is not None:
            request_json['status'] = status
        request_json = json.dumps(request_json)

        # Make the request
        response = Util().request_apiv2('PATCH', ENGAGEMENTS_ID_URL, api_key, data=request_json)
        return response
示例#4
0
    def _list(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='List an engagement on DefectDojo',
                                         usage='defectdojo engagements list [<args>]')
        optional = parser._action_groups.pop()
        required = parser.add_argument_group('required arguments')
        required.add_argument(
            '--url',
            help='DefectDojo URL',
            required=True
        )
        required.add_argument(
            '--api_key',
            help='API v2 Key',
            required=True
        )
        optional.add_argument(
            '--name',
            help='Engagement name'
        )
        optional.add_argument(
            '--product_id',
            help='Product ID'
        )
        parser._action_groups.append(optional)
        # Parse out arguments ignoring the first three (because we're inside a sub_command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Update engagement
        response = self.list(**args)

        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=200)
示例#5
0
    def add_note(self,
                 url,
                 api_key,
                 finding_id,
                 entry,
                 private=None,
                 note_type=None,
                 **kwargs):
        # Prepare parameters
        API_URL = url + '/api/v2/'
        FINDINGS_URL = API_URL + 'findings/'
        FINDINGS_ID_URL = FINDINGS_URL + str(finding_id) + '/'
        FINDINGS_ID_NOTES_URL = FINDINGS_ID_URL + 'notes/'

        # Prepare JSON data to be send
        request_json = dict()
        request_json['entry'] = entry
        if private is not None:
            request_json['private'] = private
        if note_type is not None:
            request_json['note_type'] = note_type
        request_json = json.dumps(request_json)

        # Make the request
        response = Util().request_apiv2('POST',
                                        FINDINGS_ID_NOTES_URL,
                                        api_key,
                                        data=request_json)
        return response
    def _update(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='Update a finding on DefectDojo',
                                         usage='defectdojo finding update FINDING_ID [<args>]')
        optional = parser._action_groups.pop()
        required = parser.add_argument_group('required arguments')
        parser.add_argument('finding_id', help='ID of the finding to be updated')
        required.add_argument('--url', help='DefectDojo URL', required=True)
        required.add_argument('--api_key', help='API v2 Key', required=True)
        optional.add_argument('--active', help='Set finding as active (true) or inactive (false)',
                              choices=['true', 'false'])
        optional.add_argument('--mitigated', help='Indicates if the finding is mitigated (true) or not (false)',
                              choices=['true', 'false'])
        parser._action_groups.append(optional)
        # Parse out arguments ignoring the first three (because we're inside a sub_command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Adjust args
        if args['active'] is not None:
            if args['active'] == 'true':
                args['active'] = True
            else:
                args['active'] = False
        if args['mitigated'] is not None:
            if args['mitigated'] == 'true':
                args['mitigated'] = True
            else:
                args['mitigated'] = False

        # Update finding
        response = self.update(**args)

        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=200)
示例#7
0
    def _update(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='Update a engagement on DefectDojo',
                                         usage='defectdojo engagements update ENGAGEMENT_ID [<args>]')
        optional = parser._action_groups.pop()
        required = parser.add_argument_group('required arguments')
        parser.add_argument('engagement_id', help='ID of the engagement to be updated')
        required.add_argument('--url', help='DefectDojo URL', required=True)
        required.add_argument('--api_key', help='API v2 Key', required=True)
        optional.add_argument('--name', help='Engagement name')
        optional.add_argument('--desc', help='Engagement description', metavar='DESCRIPTION')
        optional.add_argument('--product_id', help='ID of the product the engagement belongs to')
        optional.add_argument('--lead_id', help='ID of the user responsible for this engagement')
        optional.add_argument('--start_date', help='Engagement starting date', metavar='YYYY-MM-DD')
        optional.add_argument('--end_date', help='Engagement ending date', metavar='YYYY-MM-DD')
        optional.add_argument('--type', help='Engagement type', choices=['Interactive', 'CI/CD'])
        optional.add_argument('--repo_url', help='Link to source code')
        optional.add_argument('--branch_tag', help='Tag or branch of the product the engagement tested',
                            metavar='TAG_OR_BRANCH')
        optional.add_argument('--product_version', help='Version of the product the engagement tested')
        optional.add_argument('--status', help='Engagement status',
                            choices=['Not Started', 'Blocked', 'Cancelled', 'Completed', 'In Progress',
                                     'On Hold', 'Waiting for Resource'])
        parser._action_groups.append(optional)
        # Parse out arguments ignoring the first three (because we're inside a sub_command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Update engagement
        response = self.update(**args)

        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=200)
示例#8
0
 def reopen(self, url, api_key, engagement_id, **kwargs):
     # Prepare parameters
     API_URL = url+'/api/v2'
     ENGAGEMENTS_URL = API_URL+'/engagements/'
     ENGAGEMENTS_ID_URL = ENGAGEMENTS_URL+engagement_id
     ENGAGEMENTS_CLOSE_URL = ENGAGEMENTS_ID_URL+'/reopen/'
     # Make the request
     response = Util().request_apiv2('POST', ENGAGEMENTS_CLOSE_URL, api_key)
     return response
示例#9
0
    def list(self, url, api_key, test_id=None, engagement_id=None,
             test_type=None, tag=None, limit=None, **kwargs):
        # Create parameters to be requested
        request_params = dict()
        API_URL = url+'/api/v2'
        TESTS_URL = API_URL+'/tests/'
        if test_id is not None:
            request_params['id'] = test_id
        if engagement_id is not None:
            request_params['engagement'] = engagement_id
        if test_type is not None:
            # In order to filter test_type we need to get its ID via API
            if type(test_type) is int:
                test_type_id = test_type
            else:
                temp_params = dict()
                temp_params['name'] = test_type
                # Make a get request to /test_types passing the test_type as parameter
                temp_response = Util().request_apiv2('GET', API_URL+'/test_types/', api_key, params=temp_params)
                # Tranform the above response in json and get the id
                test_type_id = json.loads(temp_response.text)['results'][0]['id']
            # Add to request_params
            request_params['test_type'] = test_type_id
        if tag is not None:
            if type(tag) is list:
                request_params['tags'] = ','.join(tag)
            else:
                request_params['tags'] = tag
        if limit is not None:
            request_params['limit'] = limit
        else:
            # Make a request to API getting only one test to retrieve the total amount of tests
            temp_params = request_params.copy()
            temp_params['url'] = url
            temp_params['api_key'] = api_key
            temp_params['limit'] = 1
            temp_response = self.list(**temp_params)
            limit = int(json.loads(temp_response.text)['count'])
            request_params['limit'] = limit

        # Make request
        response = Util().request_apiv2('GET', TESTS_URL, api_key, params=request_params)
        return response
    def create(self,
               url,
               api_key,
               name,
               desc,
               product_id,
               lead_id,
               start_date=None,
               end_date=None,
               engagement_type=None,
               status=None,
               build_id=None,
               repo_url=None,
               branch_tag=None,
               commit_hash=None,
               product_version=None,
               tracker=None,
               **kwargs):
        # Prepare JSON data to be send
        request_json = dict()
        API_URL = url + '/api/v2'
        ENGAGEMENTS_URL = API_URL + '/engagements/'
        request_json['name'] = name
        request_json['description'] = desc
        request_json['product'] = product_id
        request_json['lead'] = lead_id
        if start_date is not None:
            request_json['target_start'] = start_date
        if end_date is not None:
            request_json['target_end'] = end_date
        if engagement_type is not None:
            request_json['engagement_type'] = engagement_type
        if status is not None:
            request_json['status'] = status
        if build_id is not None:
            request_json['build_id'] = build_id
        if repo_url is not None:
            request_json['source_code_management_uri'] = repo_url
        if branch_tag is not None:
            request_json['branch_tag'] = branch_tag
        if commit_hash is not None:
            request_json['commit_hash'] = commit_hash
        if product_version is not None:
            request_json['version'] = product_version
        if tracker is not None:
            request_json['tracker'] = tracker
        request_json = json.dumps(request_json)

        # Make the request
        response = Util().request_apiv2('POST',
                                        ENGAGEMENTS_URL,
                                        api_key,
                                        data=request_json)
        return response
示例#11
0
    def reimport(self,
                 url,
                 api_key,
                 result_file,
                 scanner,
                 scan_date,
                 test_id,
                 active=None,
                 verified=None,
                 min_severity=None,
                 auto_close=None,
                 version=None,
                 build_id=None,
                 branch_tag=None,
                 commit_hash=None,
                 **kwargs):
        # Prepare JSON data to be send
        request_json = dict()
        API_URL = url + '/api/v2'
        REIMPORT_SCAN_URL = API_URL + '/reimport-scan/'
        request_json['scan_type'] = scanner
        request_json['scan_date'] = scan_date
        request_json['test'] = test_id

        if active is not None:
            request_json['active'] = active
        if verified is not None:
            request_json['verified'] = verified
        if min_severity is not None:
            request_json['minimum_severity'] = min_severity
        if auto_close is not None:
            request_json['close_old_findings'] = True
        if version is not None:
            request_json['version'] = version
        if build_id is not None:
            request_json['build_id'] = build_id
        if branch_tag is not None:
            request_json['branch_tag'] = branch_tag
        if commit_hash is not None:
            request_json['commit_hash'] = commit_hash

        # Prepare file data to be send
        files = dict()
        files['file'] = open(result_file)

        # Make request
        response = Util().request_apiv2('POST',
                                        REIMPORT_SCAN_URL,
                                        api_key,
                                        files=files,
                                        data=request_json)
        return response
示例#12
0
    def list(self, url, api_key, name=None, product_id=None, **kwargs):
        # Create parameters to be requested
        request_params = dict()
        API_URL = url+'/api/v2'
        ENGAGEMENTS_URL = API_URL+'/engagements/'
        if name is not None:
            request_params['name'] = name
        if product_id is not None:
            request_params['product'] = product_id

        # Make the request
        response = Util().request_apiv2('GET', ENGAGEMENTS_URL, api_key, params=request_params)
        return response
示例#13
0
    def _list(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='List tests stored on DefectDojo',
                                         usage='defectdojo tests list [<args>]')
        optional = parser._action_groups.pop()
        required = parser.add_argument_group('required arguments')
        required.add_argument(
            '--url',
            help='DefectDojo URL', required=True
        )
        required.add_argument(
            '--api_key',
            help='API v2 Key', required=True
        )
        optional.add_argument(
            '--id',
            help='Get tests with this id'
        )
        optional.add_argument(
            '--test_type',
            help='Filter by test type'
        )
        optional.add_argument(
            '--engagement_id',
            help='Filter by engagement'
        )
        optional.add_argument(
            '--tag',
            help='Test tag (can be used multiple times)', action='append'
        )
        optional.add_argument(
            '--limit',
            help='Number of results to return (by default it gets all the tests)'
        )
        optional.set_defaults(active=None, valid=None, scope=None)
        parser._action_groups.append(optional)
        # Parse out arguments ignoring the first three (because we're inside a sub-command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Adjust args
        if args['id'] is not None:
            # Rename key from 'id' to 'test_id' to match the argument of self.list
            args['test_id'] = args.pop('id')

        # Get tests
        response = self.list(**args)

        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=200)
示例#14
0
    def update(self, url, api_key, finding_id, active=None, mitigated=None, **kwargs):
        # Prepare JSON data to be send
        request_json = dict()
        API_URL = url+'/api/v2'
        FINDINGS_URL = API_URL+'/findings/'
        FINDINGS_ID_URL = FINDINGS_URL+str(finding_id)+'/'
        if active is not None:
            request_json['active'] = active
        if mitigated is not None:
            request_json['is_Mitigated'] = mitigated
        request_json = json.dumps(request_json)

        # Make the request
        response = Util().request_apiv2('PATCH', FINDINGS_ID_URL, api_key, data=request_json)
        return response
示例#15
0
    def _close(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='Close a finding on DefectDojo',
                                         usage='defectdojo finding close FINDING_ID [<args>]')
        required = parser.add_argument_group('required arguments')
        parser.add_argument('finding_id', help='ID of the finding to be closed')
        required.add_argument('--url', help='DefectDojo URL', required=True)
        required.add_argument('--api_key', help='API v2 Key', required=True)
        # Parse out arguments ignoring the first three (because we're inside a sub_command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Close finding
        response = self.close(**args)

        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=200)
示例#16
0
    def _reopen(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='Reopen an engagement on DefectDojo',
                                         usage='defectdojo engagements reopen ENGAGEMENT_ID')
        required = parser.add_argument_group('required arguments')
        parser.add_argument('engagement_id', help='ID of the engagement to be reopened')
        required.add_argument('--url', help='DefectDojo URL', required=True)
        required.add_argument('--api_key', help='API v2 Key', required=True)
        # Parse out arguments ignoring the first three (because we're inside a sub_command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Close engagement
        response = self.reopen(**args)

        # DefectDojo doesnt has an output when a engagement is successfully reopened so we need to create one
        if response.status_code == 200:
            type(response).text = PropertyMock(return_value='{"return": "sucess"}')
        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=200)
示例#17
0
    def upload(self, url, api_key, result_file, scanner, engagement_id, lead_id,
               active=None, verified=None, scan_date=None, min_severity=None,
               tag=None, test_type=None, env=None, auto_close=None,
               skip_duplicates=None, **kwargs):
        # Prepare JSON data to be send
        request_json = dict()
        API_URL = url+'/api/v2'
        IMPORT_SCAN_URL = API_URL+'/import-scan/'
        if scan_date is not None:
            request_json['scan_date'] = scan_date
        if scanner is not None:
            request_json['scan_type'] = scanner
        if verified is not None:
            request_json['verified'] = verified
        if engagement_id is not None:
            request_json['engagement'] = engagement_id
        if lead_id is not None:
            request_json['lead'] = lead_id
        if active is not None:
            request_json['active'] = active
        if min_severity is not None:
            request_json['minimum_severity'] = min_severity
        if tag is not None:
            request_json['tags'] = tag
        if test_type is not None:
            request_json['test_type'] = test_type
        if env is not None:
            request_json['environment'] = env
        if auto_close is not None:
            request_json['close_old_findings'] = True
        if skip_duplicates is not None:
            request_json['skip_duplicates'] = True

        # Prepare file data to be send
        files = dict()
        files['file'] = open(result_file)

        # Make request
        response = Util().request_apiv2('POST', IMPORT_SCAN_URL, api_key,
                                        files=files, data=request_json)
        return response
示例#18
0
    def update(self, url, api_key, test_id, title=None, desc=None,
               start_date=None, end_date=None, version=None, build_id=None,
               commit_hash=None, branch_tag=None, lead_id=None, test_type=None,
               environment=None, **kwargs):
        # Prepare JSON data to be send
        request_json = dict()
        API_URL = url+'/api/v2'
        TESTS_URL = API_URL+'/tests/'
        TESTS_ID_URL = TESTS_URL+test_id+'/'
        if title:
            request_json['title'] = title
        if desc:
            request_json['description'] = desc
        if start_date:
            request_json['target_start'] = start_date
        if end_date:
            request_json['target_end'] = end_date
        if version:
            request_json['version'] = version
        if build_id:
            request_json['build_id'] = build_id
        if commit_hash:
            request_json['commit_hash'] = commit_hash
        if branch_tag:
            request_json['branch_tag'] = branch_tag
        if lead_id:
            request_json['lead'] = lead_id
        if test_type:
            request_json['test_type'] = test_type
        if environment:
            request_json['enviroment'] = enviroment
        request_json = json.dumps(request_json)

        # Make the request
        response = Util().request_apiv2('PATCH', TESTS_ID_URL, api_key, data=request_json)
        return response
示例#19
0
    def _upload(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='Upload findings (scan results) to DefectDojo',
                                         usage='defectdojo findings upload RESULT_FILE [<args>]')
        optional = parser._action_groups.pop()
        required = parser.add_argument_group('required arguments')
        parser.add_argument('result_file', help='File with the results to be uploaded')
        required.add_argument('--scanner', help='Type of scanner',
                              choices=Util().ACCEPTED_SCANS, metavar='SCANNER', required=True)
        required.add_argument('--url', help='DefectDojo URL', required=True)
        required.add_argument('--api_key', help='API v2 Key', required=True)
        required.add_argument('--engagement_id', help='Engagement ID', required=True)
        required.add_argument('--lead_id', help='ID of the user conducting the operation', required=True)
        optional.add_argument('--test_type', help='Test type / title (default = scanner name)')
        optional.add_argument('--env', help='Environment')
        optional.add_argument('--scan_date', help='Date the scan was perfomed (default = TODAY)',
                              metavar='YYYY-MM-DD', default=datetime.now().strftime('%Y-%m-%d'))
        optional.add_argument('--active', help='Mark vulnerabilities found as active (default)',
                              action='store_true', dest='active')
        optional.add_argument('--inactive', help='Mark vulnerabilities found as inactive',
                              action='store_false', dest='active')
        optional.add_argument('--verified', help='Mark vulnerabilities found as verified',
                              action='store_true', dest='verified')
        optional.add_argument('--unverified', help='Mark vulnerabilities found as unverified (default)',
                              action='store_false', dest='verified')
        optional.set_defaults(active=True, verified=False)
        optional.add_argument('--min_severity', help='Ignore findings below this severity (default = "Low")',
                              choices=['Informational', 'Low', 'Medium', 'High', 'Critical'], default='Low')
        optional.add_argument('--tag', help='Scanner tag (can be used multiple times)', action='append')
        optional.add_argument('--note',
                              help='Add the string passed to this flag as a'
                                   'note to each finding uploaded'
                                   '(can have a big impact on performance'
                                   'depending on the amount of findings'
                                   'uploaded)')
        optional.add_argument('--auto_close',
                              help='Close all open findings from the same '
                                  +'--test_type that are not listed on '
                                  +'this upload (default = False)',
                              action='store_true')
        optional.add_argument(
            '--skip_duplicates',
            help='Dont upload duplicates '
                 '(requires deduplication) (default = False)',
            action='store_true'
        )
        parser._action_groups.append(optional)
        # Parse out arguments ignoring the first three (because we're inside a sub-command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Upload results
        response = self.upload(**args)
        # Load upload response as JSON
        upload_out = json.loads(response.text)

        # If --note flag was passed
        if args['note'] is not None:
            # Get the findings that were uploaded 
            tmp_args = dict()
            tmp_args['url'] = args['url']
            tmp_args['api_key'] = args['api_key']
            tmp_args['test_id'] = upload_out['test'] # Get the test ID from the upload output
            tmp_response = self.list(**tmp_args)
            uploaded_findings_out = json.loads(tmp_response.text)
            # Create a list with all the uploaded findings IDs
            uploaded_findings_ids = set()
            for uploaded_finding in uploaded_findings_out['results']:
                uploaded_findings_ids.add(uploaded_finding['id'])
            # Add note to each uploaded finding
            tmp_args = dict()
            tmp_args['url'] = args['url']
            tmp_args['api_key'] = args['api_key']
            tmp_args['entry'] = args['note']
            for uploaded_finding_id in uploaded_findings_ids:
                tmp_args['finding_id'] = uploaded_finding_id
                self.add_note(**tmp_args)

        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=201)
示例#20
0
    def _reimport(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(
            description='Re-import findings (scan results) to DefectDojo',
            usage='defectdojo findings reimport RESULT_FILE [<args>]')
        optional = parser._action_groups.pop()
        required = parser.add_argument_group('required arguments')

        parser.add_argument('result_file',
                            help='File with the results to be imported')

        required.add_argument('--scanner',
                              help='Type of scanner',
                              required=True)

        required.add_argument('--url', help='DefectDojo URL', required=True)

        required.add_argument('--api_key', help='API v2 Key', required=True)

        required.add_argument('--test_id',
                              help='Test to reimport',
                              required=True)

        optional.add_argument(
            '--scan_date',
            help='Date the scan was perfomed (default = TODAY)',
            metavar='YYYY-MM-DD',
            default=datetime.now().strftime('%Y-%m-%d'))

        optional.add_argument(
            '--active',
            help='Mark vulnerabilities found as active (default)',
            action='store_true',
            dest='active')

        optional.add_argument('--inactive',
                              help='Mark vulnerabilities found as inactive',
                              action='store_false',
                              dest='active')

        optional.add_argument('--verified',
                              help='Mark vulnerabilities found as verified',
                              action='store_true',
                              dest='verified')

        optional.add_argument(
            '--unverified',
            help='Mark vulnerabilities found as unverified (default)',
            action='store_false',
            dest='verified')

        optional.set_defaults(active=True, verified=False)

        optional.add_argument(
            '--min_severity',
            help='Ignore findings below this severity (default = "Low")',
            choices=['Informational', 'Low', 'Medium', 'High', 'Critical'],
            default='Low')

        optional.add_argument(
            '--auto_close',
            help='Close all open findings from the same --test_type that are '
            'not listed on this import (default = False)',
            action='store_true')

        optional.add_argument('--version',
                              help='Current version of the project')

        optional.add_argument('--build_id', help='Build ID')

        optional.add_argument('--branch_tag', help='Branch or tag scanned')

        optional.add_argument('--commit_hash', help='Commit HASH')

        parser._action_groups.append(optional)

        # Parse out arguments ignoring the first three (because we're inside a sub-command)
        args = vars(parser.parse_args(sys.argv[3:]))
        # Re-import results
        response = self.reimport(**args)
        # Load re-import response as JSON
        out_error = False
        try:
            import_out = json.loads(response.text)
        except:
            out_error = True

        # Pretty print JSON response
        if not out_error:
            Util().default_output(response, sucess_status_code=201)
        else:
            print(response.text)
示例#21
0
 def print_scanners(self):
     for scanner in Util().ACCEPTED_SCANS:
         print(scanner)
     exit(0)
示例#22
0
    def list(self,
             url,
             api_key,
             finding_id=None,
             test_id=None,
             product_id=None,
             engagement_id=None,
             test_type=None,
             active=None,
             closed=None,
             valid=None,
             scope=None,
             limit=None,
             tag_test=None,
             tags_operator=None,
             **kwargs):
        # Create parameters to be requested
        request_params = dict()
        API_URL = url + '/api/v2'
        FINDINGS_URL = API_URL + '/findings/'
        if finding_id is not None:
            request_params['id'] = finding_id
        if test_id is not None:
            request_params['test'] = test_id
        if product_id is not None:
            request_params['test__engagement__product'] = product_id
        if engagement_id is not None:
            request_params['test__engagement'] = engagement_id
        if active is not None:
            if active is True:
                request_params['active'] = 2
            elif active is False:
                request_params['active'] = 3
        if closed:
            request_params['is_Mitigated'] = True
        if valid is not None:
            if valid is True:
                request_params['false_p'] = 3
            elif valid is False:
                request_params['false_p'] = 2
        if scope is not None:
            if scope is True:
                request_params['out_of_scope'] = 3
            elif scope is False:
                request_params['out_of_scope'] = 2
        if limit is not None:
            request_params['limit'] = limit
        else:
            # Make a request to API getting only one finding to retrieve the total amount of findings
            temp_params = request_params.copy()
            temp_params['url'] = url
            temp_params['api_key'] = api_key
            temp_params['limit'] = 1
            temp_response = self.list(**temp_params)
            limit = int(json.loads(temp_response.text)['count'])
            request_params['limit'] = limit
        if tag_test:
            # First get all test types with the tags we're looking for
            test_type_list = Tests().get_test_type_by_tags(
                url, api_key, tag_test, tags_operator, engagement_id)
            # Add them to request parameters
            #   (so the tags aren't actually passed to request, only their test_types)
            if test_type is not None:
                # If a test_type was passed by the user, append it to the list
                test_type_list = test_type_list + test_type
            test_type = test_type_list
        if test_type is not None:
            # Transform test_type names to IDs
            test_type_ids = set()
            for tt in test_type:
                if type(tt) is str:
                    temp_params = dict()
                    temp_params['name'] = tt
                    # Make a get request to /test_types passing the test_type as parameter
                    temp_response = Util().request_apiv2('GET',
                                                         API_URL +
                                                         '/test_types/',
                                                         api_key,
                                                         params=temp_params)
                    # Tranform the above response in json and get the id
                    test_type_ids.add(
                        json.loads(temp_response.text)['results'][0]['id'])
                else:
                    test_type_ids.add(tt)
            # If there's only one test_type
            if (len(test_type_ids) == 1):
                # Add to request_params
                request_params['test__test_type'] = list(test_type_ids)[0]
            else:
                # Use the appropriate method
                return self.list_multiple_test_types(url, api_key,
                                                     test_type_ids,
                                                     **request_params)

        # Make request
        response = Util().request_apiv2('GET',
                                        FINDINGS_URL,
                                        api_key,
                                        params=request_params)
        return response
示例#23
0
    def _create(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='Create an engagement on DefectDojo',
                                         usage='defectdojo engagements create [<args>]')
        optional = parser._action_groups.pop()
        required = parser.add_argument_group('required arguments')
        required.add_argument('--url',
                              help='DefectDojo URL', required=True)
        required.add_argument('--api_key',
                              help='API v2 Key', required=True)
        required.add_argument('--name',
                              help='Engagement name', required=True)
        required.add_argument('--desc',
                              help='Engagement description',
                              required=True, metavar='DESCRIPTION')
        required.add_argument('--product_id',
                              help='ID of the product on which the engagement will be created',
                              required=True)
        required.add_argument('--lead_id',
                              help='ID of the user responsible for this engagement',
                              required=True)
        optional.add_argument('--start_date',
                              help='Engagement starting date (default=TODAY)',
                              metavar='YYYY-MM-DD', default=datetime.now().strftime('%Y-%m-%d'))
        optional.add_argument('--end_date',
                              help='Engagement ending date (default=TODAY)',
                              metavar='YYYY-MM-DD', default=datetime.now().strftime('%Y-%m-%d'))
        optional.add_argument('--type',
                              help='Engagement type (default = "CI/CD")',
                              choices=['Interactive', 'CI/CD'], default='CI/CD')
        optional.add_argument('--status',
                              help='Engagement status (default = "In Progress")',
                              choices=['Not Started', 'Blocked', 'Cancelled',
                                       'Completed', 'In Progress', 'On Hold',
                                       'Waiting for Resource'],
                              default='In Progress')
        optional.add_argument('--build_id',
                              help='Build ID')
        optional.add_argument('--repo_url',
                              help='Link to source code management')
        optional.add_argument('--branch_tag',
                              help='Tag or branch of the product the engagement tested',
                              metavar='TAG_OR_BRANCH')
        optional.add_argument('--commit_hash',
                              help='Commit HASH')
        optional.add_argument('--product_version',
                              help='Version of the product the engagement tested')
        optional.add_argument('--tracker',
                              help='Link to epic or ticket system with changes to version.')
        optional.add_argument(
            '--tag',
            help='Engagement tag (can be used multiple times)',
            action='append'
        )
        optional.add_argument(
            '--local_dedup',
            help='If enabled deduplication will only mark a finding in '
            'this engagement as duplicate of another finding if both '
            'findings are in this engagement. If disabled, deduplication '
            'is on the product level. (default = false)',
            choices=['true', 'false'],
            default='false'
        )
        parser._action_groups.append(optional)
        # Parse out arguments ignoring the first three (because we're inside a sub_command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Adjust args
        if args['type'] is not None:
            # Rename key from 'type' to 'engagement_type' to match the argument of self.create
            args['engagement_type'] = args.pop('type')

        # Create engagement
        response = self.create(**args)

        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=201)
示例#24
0
    def _update(self):
        # Read user-supplied arguments
        parser = argparse.ArgumentParser(description='Update a test on DefectDojo',
                                         usage='defectdojo tests update TEST_ID [<args>]')
        optional = parser._action_groups.pop()
        required = parser.add_argument_group('required arguments')

        parser.add_argument(
            'test_id', help='ID of the test to be updated'
        )

        required.add_argument(
            '--url', help='DefectDojo URL', required=True
        )

        required.add_argument(
            '--api_key', help='API v2 Key', required=True
        )

        optional.add_argument(
            '--title', help='Test title'
        )

        optional.add_argument(
            '--desc', help='Test description', metavar='DESCRIPTION'
        )

        optional.add_argument(
            '--start_date', help='Test starting date', metavar='YYYY-MM-DD'
        )

        optional.add_argument(
            '--end_date', help='Test ending date', metavar='YYYY-MM-DD'
        )

        optional.add_argument(
            '--version', help='Test version'
        )

        optional.add_argument(
            '--build_id', help='Test build ID'
        )

        optional.add_argument(
            '--commit_hash', help='Test commit hash'
        )

        optional.add_argument(
            '--test_type', help='Test type'
        )

        optional.add_argument(
            '--environment', help='Test environment'
        )

        optional.add_argument(
            '--branch_tag',
            help='Tag or branch of the product the engagement tested',
            metavar='TAG_OR_BRANCH'
        )

        optional.add_argument(
            '--lead_id', help='ID of the user responsible for this test'
        )

        parser._action_groups.append(optional)
        # Parse out arguments ignoring the first three (because we're inside a sub_command)
        args = vars(parser.parse_args(sys.argv[3:]))

        # Update engagement
        response = self.update(**args)

        # Pretty print JSON response
        Util().default_output(response, sucess_status_code=200)