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
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
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)
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)
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)
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
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
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
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
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)
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
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)
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)
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
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
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)
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)
def print_scanners(self): for scanner in Util().ACCEPTED_SCANS: print(scanner) exit(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
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)
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)