def generateNetworkMap(source_api: QualysAPI.QualysAPI, target_api: QualysAPI.QualysAPI): srcurl = '%s/api/2.0/fo/network/?action=list' % source_api.server tgturl = '%s/api/2.0/fo/network/?action=list' % target_api.server srcresp = source_api.makeCall(url=srcurl) if not responseHandler(srcresp): return None tgtresp = target_api.makeCall(url=tgturl) if not responseHandler(tgtresp): return None srclist = srcresp.find('.//NETWORK_LIST') tgtlist = tgtresp.find('.//NETWORK_LIST') netmap = {} for net in srclist.findall('NETWORK'): if net.find('ID').text == '0': continue netname = net.find('NAME').text # Networks beginning 'Global' or 'Qualys' are reserved for system use if netname.find('Global') == 0 or netname.find('Qualys') == 0: continue netid = net.find('ID').text tgtnet = tgtlist.find(".//NETWORK/[NAME='%s']" % netname) # tgtnet = tgtlist.find('.//[NAME="%s"]/..' % netname) if tgtnet is None: print('ERROR: Could not find Network %s in target subscription' % netname) return None tgtnetid = tgtnet.find('ID').text netmap[netid] = tgtnetid return netmap
def build_asset_group_map(source_api: QualysAPI.QualysAPI, target_api: QualysAPI.QualysAPI, prefix: str = None): baseurl = '/api/2.0/fo/asset/group/?action=list&show_attributes=ID,TITLE' source_url = '%s%s' % (source_api.server, baseurl) target_url = '%s%s' % (target_api.server, baseurl) source_ags = source_api.makeCall(url=source_url) target_ags = target_api.makeCall(url=target_url) source_ag_dict = {} target_ag_dict = {} ag_map = {} for i in source_ags.findall('.//ASSET_GROUP'): source_ag_dict[i.find('TITLE').text] = i.find('ID').text for i in target_ags.findall('.//ASSET_GROUP'): target_ag_dict[i.find('TITLE').text] = i.find('ID').text for i in source_ag_dict.keys(): if prefix is not None: target_ag_name = '%s %s' % (prefix, i) if target_ag_name in target_ag_dict.keys(): ag_map[source_ag_dict[i]] = target_ag_dict[target_ag_name] else: if i in target_ag_dict.keys(): ag_map[source_ag_dict[i]] = target_ag_dict[i] return ag_map
def generateConnectorMap(source_api: QualysAPI.QualysAPI, target_api: QualysAPI.QualysAPI): connectormap = {} srcurl = '%s/qps/rest/2.0/search/am/assetdataconnector/?fields=id,name,type' % source_api.server tgturl = '%s/qps/rest/2.0/search/am/assetdataconnector/?fields=id,name,type' % target_api.server slist = source_api.makeCall(url=srcurl) tlist = target_api.makeCall(url=tgturl) if slist.find('.//AssetDataConnector') is None: print( 'QualysCloudConnectorProcessor.generateConnectorMap: Failed to generate list of source connectors' ) return None if tlist.find('.//AssetDataConnector') is None: print( 'QualysCloudConnectorProcessor.generateConnectorMap: Failed to generate list of target connectors' ) return None for adc in slist.findall('.//AssetDataConnector'): if tlist.find('.//AssetDataConnector/[name="%s"]' % adc.find('name').text) is None: print( 'QualysCloudConnectorProcessor.generateConnectorMap: Could not match %s with target' % adc.find('name').text) continue connectormap[slist.find('name').text] = [ slist.find('id').text, tlist.find('id').text ] del slist, tlist return connectormap
def generateApplianceMap(source_api: QualysAPI.QualysAPI, target_api: QualysAPI.QualysAPI, appliance_name_map: dict = None): appliancemap = {} srcurl = '%s/api/2.0/fo/appliance/?action=list' % source_api.server tgturl = '%s/api/2.0/fo/appliance/?action=list' % target_api.server sourceresp = source_api.makeCall(srcurl) targetresp = target_api.makeCall(tgturl) sourcelist = sourceresp.find('.//APPLIANCE_LIST') targetlist = targetresp.find('.//APPLIANCE_LIST') if sourcelist is None: print( 'QualysApplianceInput.generateApplianceMap ERROR: Unable to obtain appliance list from source ' 'subscription') if source_api.debug: print( ET.tostring(sourceresp, method='xml', encoding='utf-8').decode()) return None if targetlist is None: print( 'QualysApplianceInput.generateApplianceMap Error: Unable to obtain appliance list from target ' 'subscription') if target_api.debug: print( ET.tostring(targetresp, method='xml', encoding='utf-8').decode()) return None for sourceappliance in sourcelist.findall('APPLIANCE'): srcappliancename = sourceappliance.find('NAME').text srcapplianceid = sourceappliance.find('ID').text if appliance_name_map is None: tgtappliance = targetlist.find('.//*[NAME="%s"]' % srcappliancename) else: tgtappliance = targetlist.find( './/*[NAME="%s"]' % appliance_name_map[srcappliancename]) if tgtappliance is None: print('ERROR: Unable to find Appliance %s in target subscription' % appliance_name_map[srcappliancename]) return None appliancemap[srcapplianceid] = tgtappliance.find('ID').text return appliancemap
def updateSchedule(api: QualysAPI.QualysAPI, schedulexml: ET.Element): fullurl = '%s/qps/rest/3.0/update/was/wasscanschedule' % api.server payload = ET.tostring(schedulexml, method='xml', encoding='utf-8').decode() resp = api.makeCall(url=fullurl, payload=payload) return resp
def getScanReportTemplates(source_api: QualysAPI.QualysAPI): scanurl = '%s/api/2.0/fo/report/template/scan/?action=export&report_format=xml' % source_api.server scantemplates = source_api.makeCall(url=scanurl, method='GET') if not responseHandler(scantemplates): print('ERROR: Could not get Scan Templates') scantemplates = None return scantemplates
def getMapReportTemplates(source_api: QualysAPI.QualysAPI): mapurl = '%s/api/2.0/fo/report/template/map/?action=export&report_format=xml' % source_api.server maptemplates = source_api.makeCall(url=mapurl, method='GET') if not responseHandler(maptemplates): print('ERROR: Could not get Map Templates') maptemplates = None return maptemplates
def getPatchReportTemplates(source_api: QualysAPI.QualysAPI): patchurl = '%s/api/2.0/fo/report/template/patch/?action=export&report_format=xml' % source_api.server patchtemplates = source_api.makeCall(url=patchurl, method='GET') if not responseHandler(patchtemplates): print('ERROR: Could not get Patch Templates') patchtemplates = None return patchtemplates
def createStaticSearchList(target_api: QualysAPI.QualysAPI, searchlist: ET.Element, simulate: bool = False): comments = '' isGlobal = '0' qidlist = [] title = searchlist.find('TITLE').text if searchlist.find('GLOBAL') == 'Yes': isGlobal = '1' if searchlist.find('COMMENTS') is not None: comments = searchlist.find('COMMENTS').text for qid in searchlist.findall('.//QID'): qidlist.append(qid.text) fullurl = '%s/api/2.0/fo/qid/search_list/static/?action=create&title=%s&qids=%s&global=%s' % ( target_api.server, title, ','.join(qidlist), isGlobal) if comments != '': fullurl = '%s&%s' % (fullurl, comments) if simulate: print('Request URL : %s' % fullurl) return fullurl resp = target_api.makeCall(url=fullurl) if not responseHandler(resp): print('QualysSearchListProcessor.createStaticSearchList failed') return None return resp
def listExceptions(api: QualysAPI.QualysAPI, status: str = None, truncation_limit: int = 1000): fullurl = '%s/api/2.0/fo/compliance/exception/?action=list&details=All' % api.server if status is not None: if status in ['Pending', 'Approved', 'Rejected', 'Expired']: fullurl = '%s&status=%s' % (fullurl, status) else: print( 'ERROR: QualysComplianceExceptionProcessor.listExceptions: status %s not recognised. Must be ' '\'Pending\', \'Approved\', \'Rejected\' or \'Expired\'') return None exceptions = ET.Element('EXCEPTION_LIST') url = '%s&truncation_limit=%s' % (fullurl, str(truncation_limit)) more_results = True while more_results: resp = api.makeCall(url=url) if resp.find('RESPONSE/WARNING/URL') is not None: url = resp.find('RESPONSE/WARNING/URL').text more_results = True else: more_results = False for i in resp.findall('.//EXCEPTION'): exceptions.append(i) return exceptions, resp
def updateException(api: QualysAPI.QualysAPI, exceptions: str, comments: str, reassignto: str = None, reopen: bool = False, status: str = None, end_date: str = None): fullurl = '%s/api/2.0/fo/compliance/exception/?action=update' % api.server fullurl = '%s&exception_numbers=%s&comments=%s' % (fullurl, exceptions, comments) if reassignto is not None: fullurl = '%s&reassign_to=%s' % (fullurl, reassignto) if reopen: fullurl = '%s&reopen_on_evidence_change=1' % fullurl if status is not None: fullurl = '%s&status=%s' % (fullurl, status) if end_date is not None: fullurl = '%s&end_date=%s' % (fullurl, end_date) resp = api.makeCall(url=fullurl) if not responseHandler(resp): print( 'ERROR: QualysComplianceExceptionProcessor.updateException failed') return None return resp
def requestException(api: QualysAPI.QualysAPI, controlid: str, hostid: str, policyid: str, technologyid: str, assigneeid: str, comments: str, reopen: bool = False, instancestring: str = None): fullurl = '%s/api/2.0/fo/compliance/exception/?action=request' % api.server fullurl = '%s&control_id=%s&host_id=%s&policy_id=%s&technology_id=%s&assignee_id=%s&comments=%s' % \ (fullurl, controlid, hostid, policyid, technologyid, assigneeid, comments) if reopen: fullurl = '%s&reopen_on_evidence_change=1' % fullurl else: fullurl = '%s&reopen_on_evidence_change=0' % fullurl resp = api.makeCall(url=fullurl, method='POST') if not responseHandler(resp): print( 'ERROR: QualysComplianceExceptionProcessor.requestException failed' ) return None return resp
def createDNSOverride(api: QualysAPI.QualysAPI, name: str, mappings: list, tagids: str = None): fullurl = '%s/qps/rest/3.0/create/was/dnsoverride/' % api.server sr = ET.Element('ServiceRequest') data = ET.SubElement(sr, 'data') dnsoverride = ET.SubElement(data, 'DnsOverride') recordname = ET.SubElement(dnsoverride, 'name') recordname.text = name overridemaps = ET.SubElement(dnsoverride, 'mappings') mapset = ET.SubElement(overridemaps, 'set') for mapping in mappings: override = ET.SubElement(mapset, 'DnsMapping') hostname = ET.SubElement(override, 'hostName') hostname.text = mapping['hostName'] ipaddr = ET.SubElement(override, 'ipAddress') ipaddr.text = mapping['ipAddress'] if tagids is not None: tags = ET.SubElement(dnsoverride, 'tags') tagset = ET.SubElement(tags, 'set') for tagid in tagids.split(','): tagxml = ET.SubElement(tagset, 'Tag') tagidxml = ET.SubElement(tagxml, 'id') tagidxml.text = tagid.strip() payload = ET.tostring(sr, method='xml', encoding='utf-8').decode() resp = api.makeCall(url=fullurl, payload=payload) return resp
def setAssetGroupAssignment(target_api: QualysAPI.QualysAPI, asset_group_ids: str, policy_id: str): update_url = '%s/api/2.0/fo/compliance/policy?action=set_asset_group_ids&id=%s&asset_group_ids=%s' % ( target_api.server, policy_id, asset_group_ids) resp = target_api.makeCall(url=update_url) return resp
def getAssets(api: QualysAPI.QualysAPI, key: str): fullurl = '%s/qps/rest/2.0/search/am/hostasset/?fields=name,agentInfo.agentId' % api.server startat = 1 pagesize = 1000 assets = [] endofdata = False while not endofdata: sr = ET.Element('ServiceRequest') pref = ET.SubElement(sr, 'preferences') sfo = ET.SubElement(pref, 'startFromOffset') sfo.text = str(startat) lr = ET.SubElement(pref, 'limitResults') lr.text = str(pagesize) filters = ET.SubElement(sr, 'filters') criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'activationKey') criteria.set('operator', 'EQUALS') criteria.text = key payload = ET.tostring(sr, encoding='utf-8', method='xml').decode() headers = {'Content-type': 'text/xml', 'Accept': 'application/json'} resp = api.makeCall(url=fullurl, payload=payload, headers=headers, returnwith='text') jresp = json.loads(resp) assets.extend(jresp["ServiceResponse"]["data"]) if jresp["ServiceResponse"]["hasMoreRecords"] == 'false': endofdata = True startat = startat + pagesize return assets
def createActivationKey(api: QualysAPI.QualysAPI, activationKey: dict): fullurl = '%s/qps/rest/1.0/create/ca/agentactkey/' % api.server sr = ET.Element('ServiceRequest') data = ET.SubElement(sr, 'data') actkey = ET.SubElement(data, 'AgentActKey') title = ET.SubElement(actkey, 'title') title.text = activationKey["AgentActKey"]["title"] keytype = ET.SubElement(actkey, 'type') keytype.text = activationKey["AgentActKey"]["type"] modules = ET.SubElement(actkey, 'modules') modlist = ET.SubElement(modules, 'list') for mod in activationKey["AgentActKey"]["modules"]["list"]: modh1 = ET.SubElement(modlist, 'ActivationKeyModule') module = ET.SubElement(modh1, 'license') module.text = mod["ActivationKeyModule"]["license"] modh1 = ET.SubElement(modlist, 'ActivationKeyModule') module = ET.SubElement(modh1, 'license') module.text = 'GAV' tags = ET.SubElement(actkey, 'tags') taglist = ET.SubElement(tags, 'list') for t in activationKey["AgentActKey"]["tags"]["list"]: tag = ET.SubElement(taglist, "Tag") name = ET.SubElement(tag, 'name') name.text = t["Tag"]["name"] payload = ET.tostring(sr, encoding='utf-8', method='xml').decode() resp = api.makeCall(url=fullurl, payload=payload) return resp
def getVirtualHosts(source_api: QualysAPI.QualysAPI, networks: bool = False): hostlist = [] fullurl = '%s/api/2.0/fo/asset/vhost/?action=list' % source_api.server resp = source_api.makeCall(url=fullurl, method='GET') if not responseHandler(resp): print( 'QualysVirtualHostProcessor.getVirtualHosts ERROR: Could not obtain Virtual Host list from source' ) return None if resp.find('RESPONSE/VIRTUAL_HOST_LIST') is not None: for host in resp.findall('.//VIRTUAL_HOST'): vhost = {} fqdns = [] vhost['IP'] = host.find('IP').text vhost['PORT'] = host.find('PORT').text for fqdn in host.findall('FQDN'): fqdns.append(fqdn.text) vhost['FQDNS'] = fqdns if networks: if host.find('NETWORK_ID') is None: print( 'QualysVirtualHostProcessor.getVirtualHosts ERROR: Networks enabled but no Network ID found' ) return None vhost['NETWORK_ID'] = host.find('NETWORK_ID').text hostlist.append(vhost) else: print( 'QualysVirtualHostProcessor.getVirtualHosts ERROR: Cannot find Virtual Host List in output' ) return None return hostlist
def getActivationKeys(api: QualysAPI.QualysAPI): fullurl = '%s/qps/rest/1.0/search/ca/agentactkey/' % api.server startat = 1 pagesize = 1000 keys = [] endofdata = False while not endofdata: sr = ET.Element('ServiceRequest') pref = ET.SubElement(sr, 'preferences') sfo = ET.SubElement(pref, 'startFromOffset') sfo.text = str(startat) lr = ET.SubElement(pref, 'limitResults') lr.text = str(pagesize) payload = ET.tostring(sr, encoding='utf-8', method='xml').decode() headers = {'Content-type': 'text/xml', 'Accept': 'application/json'} resp = api.makeCall(url=fullurl, payload=payload, headers=headers, returnwith='text') jresp = json.loads(resp) keys.extend(jresp["ServiceResponse"]["data"]) if jresp["ServiceResponse"]["hasMoreRecords"] == 'false': endofdata = True startat = startat + pagesize return keys
def exportOptionProfiles(source_api: QualysAPI.QualysAPI): fullurl = '%s/api/2.0/fo/subscription/option_profile/?action=export&include_system_option_profiles=0' %\ source_api.server response = source_api.makeCall(url=fullurl, method='GET') if not responseHandler(response): return None return response
def handleSystemParents(target_api: QualysAPI.QualysAPI, tags: ET.Element): # Cloud Agent and Asset Search tags are a special case # The parent is system-generated however children are user-generated # For Cloud Agent tags we simply re-parent the children to the target's Cloud Agent tag # For Asset Search tags, the parent only exists in the target subscription when an Asset Search tag is created # so we simply create a new parent tag (with a different name, so as to avoid errors later) and re-parent the # children to it queryurl = "%s/qps/rest/2.0/search/am/tag" % target_api.server sr = ET.Element('ServiceRequest') filters = ET.SubElement(sr, 'filters') criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'name') criteria.set('operator', 'EQUALS') criteria.text = 'Cloud Agent' payload = ET.tostring(sr, method='html', encoding='utf-8').decode() resp = target_api.makeCall(url=queryurl, payload=payload) if resp.find('.//id') is None: print('ERROR: Failed to get ID for Cloud Agent tag') return None tagID_cloudagent = resp.find('.//id').text # Handle Cloud Agent tags ca_tree = tags.find('.//Tag/[name="Cloud Agent"]') # If the Cloud Agent tag has children, we need to process them if ca_tree.find('children') is not None: ca_children = ca_tree.find('children') # Sometimes there is a children node but not a list node if ca_children.find('list') is not None: ca_list = ca_children.find('list') for ca_tag in ca_list.findall('./*Tag'): # First add the parentTagId node parent = ET.SubElement(ca_tag, 'parentTagId') parent.text = tagID_cloudagent # remove the tag from the child list ca_list.remove(ca_tag) # Then add it to the root tags.append(ca_tag) tags.remove(ca_tree) # Handle Asset Search tags asp_tag = ET.SubElement(tags, 'Tag') asp_tagname = ET.SubElement(asp_tag, 'name') asp_tagname.text = 'Imported Asset Search Tags' asp_children = ET.SubElement(asp_tag, 'children') asp_tagset = ET.SubElement(asp_children, 'set') as_tree = tags.find('.//Tag/[name="Asset Search Tags"]') # If the Asset Search tag has children, we need to process them if as_tree.find('children') is not None: as_children = as_tree.find('children') if as_children.find('set') is not None: as_list = as_children.find('set') for as_tag in as_list.findall('Tag'): # remove the tag from the child list as_list.remove(as_tag) # then add it to the new parent asp_tagset.append(as_tag) tags.remove(as_tree) return tags
def createAppliance(target_api: QualysAPI.QualysAPI, name: str, polling_interval: str = '180', asset_group: str = None): fullurl = '%s/api/2.0/fo/appliance/?action=create&name=%s&polling_interval=%s&asset_group=%s' % ( target_api.server, name, polling_interval, asset_group) resp = target_api.makeCall(url=fullurl) if not responseHandler(resp): print('QualysApplianceProcessor.createAppliance failed') return None return resp.find('.//*ID').text
def updateAppliance(target_api: QualysAPI.QualysAPI, appliance_id: str, vlans: list, routes: list): baseurl = '%s/api/2.0/fo/appliance/?action=update&id=%s&set_vlans=%s&set_routes=%s' % ( appliance_id, target_api.server, ','.join(vlans), ','.join(routes)) resp = target_api.makeCall(url=baseurl) if not responseHandler(resp): print('QualysApplianceProcessor.updateAppliance failed') return False return True
def getTagSet(api: QualysAPI.QualysAPI, sr: ET.Element): fullurl = '%s/qps/rest/2.0/search/am/tag' % api.server payload = ET.tostring(sr, method='html', encoding='utf-8').decode() resp = api.makeCall(url=fullurl, payload=payload) if not checkResponse(resp): return None return resp
def createScheduledPCScan(api: QualysAPI.QualysAPI, requeststr: str, payload: dict): fullurl = '%s/%s' % (api.server, requeststr) resp = api.makeCall(url=fullurl, payload=payload) # if not responseHandler(resp): # print('QualysComplianceScanScheduleProcessor.createScheduledPCScan failed') # return False # return True return resp
def getStaticSearchLists(source_api: QualysAPI.QualysAPI, ids: str = None): fullurl = '%s/api/2.0/fo/qid/search_list/static/?action=list' % source_api.server if ids is not None: fullurl = '%s&ids=%s' % (fullurl, ids) resp = source_api.makeCall(url=fullurl, method='GET') if not responseHandler(resp): print('QualysSearchListProcessor.getStaticSearchLists failed') return None return resp.find('.//STATIC_LISTS')
def importSubscriptionConfig(api: QualysAPI.QualysAPI, configxml: ET.Element): fullurl = "%s/api/2.0/fo/subscription/index.php?action=import" % api.server payload = ET.tostring(configxml, method='html', encoding='utf-8').decode() headers = {'Content-type': 'text/xml'} response = api.makeCall(url=fullurl, method='POST', payload=payload, headers=headers) return response
def searchReport(api: QualysAPI.QualysAPI, name: str = None, tagsid: str = None, tagsname: str = None, creationdate: str = None, reporttype: str = None, reportformat: str = None, status: str = None): fullurl = '%s/qps/rest/3.0/search/was/report' % api.server payload = "" sr = ET.Element('ServiceRequest') filters = ET.SubElement(sr, 'filters') if name is not None: criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'name') criteria.set('operator', 'CONTAINS') criteria.text = name if tagsid is not None: criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'tags.id') criteria.set('operator', 'IN') criteria.text = tagsid if tagsname is not None: criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'tags.name') criteria.set('operator', 'CONTAINS') criteria.text = tagsname if creationdate is not None: criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'creationDate') criteria.set('operator', 'CONTAINS') criteria.text = creationdate if reporttype is not None: criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'type') criteria.set('operator', 'CONTAINS') criteria.text = reporttype if reportformat is not None: criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'format') criteria.set('operator', 'CONTAINS') criteria.text = reportformat if status is not None: criteria = ET.SubElement(filters, 'Criteria') criteria.set('field', 'status') criteria.set('operator', 'CONTAINS') criteria.text = status if len(filters) > 0: payload = ET.tostring(sr, method='xml', encoding='utf-8').decode() resp = api.makeCall(fullurl, payload=payload) return resp
def createVirtualHosts(urllist: list, target_api: QualysAPI.QualysAPI): for url in urllist: fullurl = '%s%s' % (target_api.server, url) resp = target_api.makeCall(url=fullurl) if not responseHandler(resp): print( 'QualysVirtualHostProcessor.createVirtualHosts ERROR: Could not validate response' ) return False return True
def createReportFromXML(api: QualysAPI.QualysAPI, reportxml: ET.Element): fullurl = '%s/qps/rest/3.0/create/was/report' % api.server sr = ET.Element('ServiceRequest') data = ET.SubElement(sr, 'data') data.append(reportxml) payload = ET.tostring(sr, method='xml', encoding='utf-8').decode() resp = api.makeCall(url=fullurl, payload=payload) return resp
def getAssetGroups(source_api: QualysAPI.QualysAPI): fullurl = '%s/api/2.0/fo/asset/group/?action=list&show_attributes=ALL' % source_api.server resp = source_api.makeCall(url=fullurl) if not responseHandler(resp): return None usesnetworks = False aglist = resp.find('.//ASSET_GROUP_LIST') return aglist