diff --git a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/ComplianceController.java b/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/ComplianceController.java index f3baa0f66..5d36208a4 100644 --- a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/ComplianceController.java +++ b/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/ComplianceController.java @@ -1,750 +1,715 @@ -/******************************************************************************* - * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - ******************************************************************************/ -package com.tmobile.pacman.api.compliance.controller; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; -import java.util.TimeZone; - -import org.apache.commons.collections.MapUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import com.google.common.base.Strings; -import com.tmobile.pacman.api.commons.Constants; -import com.tmobile.pacman.api.commons.exception.ServiceException; -import com.tmobile.pacman.api.commons.utils.ResponseUtils; -import com.tmobile.pacman.api.compliance.domain.DitributionDTO; -import com.tmobile.pacman.api.compliance.domain.IssueAuditLogRequest; -import com.tmobile.pacman.api.compliance.domain.IssueResponse; -import com.tmobile.pacman.api.compliance.domain.IssuesException; -import com.tmobile.pacman.api.compliance.domain.KernelVersion; -import com.tmobile.pacman.api.compliance.domain.OutputDTO; -import com.tmobile.pacman.api.compliance.domain.PolicyDescription; -import com.tmobile.pacman.api.compliance.domain.PolicyViolationDetails; -import com.tmobile.pacman.api.compliance.domain.Request; -import com.tmobile.pacman.api.compliance.domain.ResourceTypeResponse; -import com.tmobile.pacman.api.compliance.domain.ResponseData; -import com.tmobile.pacman.api.compliance.domain.ResponseWithOrder; -import com.tmobile.pacman.api.compliance.domain.RevokeIssuesException; -import com.tmobile.pacman.api.compliance.domain.RuleDetails; -import com.tmobile.pacman.api.compliance.service.ComplianceService; -import com.tmobile.pacman.api.compliance.service.VulnerabilityService; - -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; - -/** - * The Class ComplianceController. - */ -@RestController -@PreAuthorize("@securityService.hasPermission(authentication, 'ROLE_USER')") -public class ComplianceController implements Constants { - - /** The compliance service. */ - @Autowired - private ComplianceService complianceService; - - /** The vuln service. */ - @Autowired - private VulnerabilityService vulnService; - - /** - * Gets the issues details.Request expects asssetGroup and domain as - * mandatory, ruleId as optional.If API receives assetGroup and domain as - * request parameter, it gives details of all open issues for all the rules - * associated to that domain. If API receives assetGroup, domain and ruleId - * as request parameter,it gives only open issues of that rule associated to - * that domain. SearchText is used to match any text you are looking - * for.From and size are for the pagination - * - * @param request request body - * @return issues - */ - - @RequestMapping(path = "/v1/issues", method = RequestMethod.POST) - @ResponseBody - public ResponseEntity getIssues(@RequestBody(required = false) Request request) { - String assetGroup = request.getAg(); - Map filters = request.getFilter(); - - if (Strings.isNullOrEmpty(assetGroup) || MapUtils.isEmpty(filters) - || Strings.isNullOrEmpty(filters.get(DOMAIN))) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_GROUP_DOMAIN)); - } - ResponseWithOrder response = null; - try { - response = complianceService.getIssues(request); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the issues count. asssetGroup and domain are mandatory & ruleId is - * optional parameter, it gives issues count of all open issues for all the rules - * associated to that domain. If API receives assetGroup,domain and ruleId - * as request parameter,it gives issues count of all open issues for that - * rule associated to that domain. - * - * @param assetGroup name of the asset group - * @param domain the domain - * @param ruleId the rule id - * @return the issues count - */ - - @RequestMapping(path = "/v1/issues/count", method = RequestMethod.GET) - public ResponseEntity getIssuesCount(@RequestParam("ag") String assetGroup, - @RequestParam("domain") String domain, @RequestParam(name = "ruleId", required = false) String ruleId) { - if (Strings.isNullOrEmpty(assetGroup) || Strings.isNullOrEmpty(domain)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_GROUP_DOMAIN)); - } - Map response = new HashMap<>(); - try { - response.put("total_issues", complianceService.getIssuesCount(assetGroup, ruleId, domain)); - } catch (ServiceException e) { - return ResponseUtils.buildFailureResponse(e); - } - - return ResponseUtils.buildSucessResponse(response); - - } - - /** - * Gets the issue distribution by ruleCategory and severity.asssetGroup - * is mandatory, domain is optional. API return issue distribution rule - * severity & rule Category for given asset group - * - * @param assetGroup name of the asset group - * @param domain the domain - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/issues/distribution", method = RequestMethod.GET) - public ResponseEntity getDistribution(@RequestParam("ag") String assetGroup, - @RequestParam(name = "domain", required = false) String domain) { - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); - } - DitributionDTO distribution = null; - try { - distribution = new DitributionDTO(complianceService.getDistribution(assetGroup, domain)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(distribution); - } - - /** - * Gets the tagging compliance summary.asssetGroup is mandatory and - * targetType is optional If API receives assetGroup as request parameter, - * api returns tagged/un-tagged/asset count of all the target types for that - * asset group. If API receives both assetGroup and targetType as request - * parameter,api returns tagged/un-tagged/asset count of specified target - * type. - * - * @param assetGroup name of the asset group - * @param targetType the target type - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/tagging", method = RequestMethod.GET) - public ResponseEntity getTagging(@RequestParam("ag") String assetGroup, - @RequestParam(name = "targettype", required = false) String targetType) { - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); - } - OutputDTO output = null; - try { - output = new OutputDTO(complianceService.getTagging(assetGroup, targetType)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(output); - } - - /** - * Gets the vulnerabilities.asssetGroup is mandatory. API returns count of - * totalVulnerabilities/totalAssets/totalVulnerabilites Assets - * - * @param assetGroup name of the asset group - * @return ResponseEntity - */ - // @Cacheable("trends") - - @RequestMapping(path = "/v1/vulnerabilites", method = RequestMethod.GET) - public ResponseEntity getVulnerabilities(@RequestParam("ag") String assetGroup) { - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); - } - OutputDTO output = null; - try { - Map vulnerabilities = new HashMap<>(); - Map vulnSummary = vulnService.getVulnerabilitySummary(assetGroup,SEVERITY_LEVELS); - vulnerabilities.put("vulnerabilities", Long.valueOf(vulnSummary.get("vulnerabilities").toString())); - vulnerabilities.put("hosts", Long.valueOf(vulnSummary.get("hosts").toString())); - vulnerabilities.put("totalVulnerableAssets", - Long.valueOf(vulnSummary.get("totalVulnerableAssets").toString())); - vulnSummary.remove("compliantpercent"); - output = new OutputDTO(vulnerabilities); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(output); - } - - /** - * Gets the certificates compliance details.asssetGroup is mandatory. API - * returns count of expiredCertificates with in 60days and totalCertificates - * for given assetGroup - * - * @param assetGroup name of the asset group - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/certificates", method = RequestMethod.GET) - public ResponseEntity getCertificates(@RequestParam("ag") String assetGroup) { - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); - } - OutputDTO output = null; - try { - output = new OutputDTO(complianceService.getCertificates(assetGroup)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(output); - } - - /** - * Gets the patching compliance details.AssetGroup is mandatory. API returns - * count of totalPached/toalUnpatched/TotalInstances for given assetGroup - * - * @param assetGroup name of the asset group - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/patching", method = RequestMethod.GET) - public ResponseEntity getPatching(@RequestParam("ag") String assetGroup) { - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception("Asset group is mandatory")); - } - OutputDTO output = null; - try { - output = new OutputDTO(complianceService.getPatching(assetGroup, null)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(output); - } - - /** - * Gets the recommendations details by policy.asssetGroup is mandatory and - * targetType is optional. If API receives assetGroup as request parameter, - * API returns list of all the issue counts which are related to - * recommendations rules from the ES for the given assetGroup with all the - * targetTypes.If API receives both assetGroup and targetType as request - * parameter,API returns list of all the issue counts which are related to - * recommendations rules from the ES for the given targetType & assetGroup. - * - * @param assetGroup name of the asset group - * @param targetType the target type - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/recommendations", method = RequestMethod.GET) - public ResponseEntity getRecommendations(@RequestParam("ag") String assetGroup, - @RequestParam(name = "targettype", required = false) String targetType) { - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); - } - ResponseData response = null; - try { - response = new ResponseData(complianceService.getRecommendations(assetGroup, targetType)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(response); - - } - - /** - * Gets the issue audit details.This request accepts - * annotationId,targetType,size as mandatory. If API receives - * annotationId,targetType,size as request parameter, API returns list of - * data source, audit date and status of that annotationId. searchText is used - * to match any text you are looking for. from and size are for pagination. - * - * @param request the request - * @return the issue audit - */ - - @RequestMapping(path = "/v1/issueauditlog", method = RequestMethod.POST) - public ResponseEntity getIssueAudit(@RequestBody IssueAuditLogRequest request) { - String issueId = request.getIssueId(); - String targetType = request.getTargetType(); - int from = request.getFrom(); - int size = request.getSize(); - String searchText = request.getSearchText(); - if (Strings.isNullOrEmpty(issueId) || Strings.isNullOrEmpty(targetType) || from < 0 || size <= 0) { - return ResponseUtils.buildFailureResponse(new Exception("IssueId/Targettype/from/size is Mandatory")); - } - ResponseWithOrder response = null; - try { - response = complianceService.getIssueAuditLog(issueId, targetType, from, size, searchText); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(response); - - } - - /** - * Gets the resource details.assetGroup and resourceId are mandatory. API - * returns map details for given resourceId - * - * @param assetGroup name of the asset group - * @param resourceId the resource id - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/resourcedetails", method = RequestMethod.GET) - public ResponseEntity getResourceDetails(@RequestParam("ag") String assetGroup, - @RequestParam("resourceId") String resourceId) { - if (Strings.isNullOrEmpty(resourceId)) { - return ResponseUtils.buildFailureResponse(new Exception("assetGroup/resourceId is mandatory")); - } - ResponseData response = null; - try { - response = new ResponseData(complianceService.getResourceDetails(assetGroup, resourceId)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Close issues.ruleDetails expects ruleId,reason and userId, Api returns - * true if its successfully closes all issues in ES for that ruleId else - * false - * - * @param ruleDetails the rule details - * @return ResponseEntity - */ - @ApiOperation(httpMethod = "PUT", value = "Close Issues by Rule Details") - @RequestMapping(path = "/v1/issues/close-by-rule-id", method = RequestMethod.PUT) - @ResponseBody - - public ResponseEntity closeIssues( - @ApiParam(value = "Provide valid Rule Details ", required = true) @RequestBody(required = true) RuleDetails ruleDetails) { - Map response = complianceService.closeIssuesByRule(ruleDetails); - if (Integer.parseInt(response.get("status").toString()) == TWO_HUNDRED) { - return new ResponseEntity<>(response, HttpStatus.OK); - } else { - return new ResponseEntity<>(response, HttpStatus.FORBIDDEN); - } - } - - /** - * Adds the issue exception.issueException expects - * issueId,exceptionGrantedDate,exceptionEndDate and exceptionReason, API is - * for adding issue exception to the corresponding target type. - * - * @param issueException the issue exception - * @return ResponseEntity - */ - @ApiOperation(httpMethod = "POST", value = "Adding issue exception to the corresponding target type") - @RequestMapping(path = "/v1/issues/add-exception", method = RequestMethod.POST) - @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully Added Issue Exception"), - @ApiResponse(code = 401, message = "You are not authorized to Add Issue Exception"), - @ApiResponse(code = 403, message = "Add Issue Exception is forbidden") }) - @ResponseBody - - public ResponseEntity addIssueException( - @ApiParam(value = "Provide Issue Exception Details", required = true) @RequestBody(required = true) IssueResponse issueException) { - try { - Boolean isExempted = complianceService.addIssueException(issueException); - if (isExempted) { - return ResponseUtils.buildSucessResponse("Successfully Added Issue Exception"); - } else { - return ResponseUtils.buildFailureResponse(new Exception("Failed in Adding Issue Exception")); - } - } catch (ServiceException exception) { - return ResponseUtils.buildFailureResponse(exception); - } - } - - /** - * Revoke issue exception. - * - * @param issueId the issue id - * @return ResponseEntity - */ - @ApiOperation(httpMethod = "POST", value = "Revoking issue exception to the corresponding target type") - @RequestMapping(path = "/v1/issues/revoke-exception", method = RequestMethod.POST) - @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully Revoked Issue Exception"), - @ApiResponse(code = 401, message = "You are not authorized to Revoke Issue Exception"), - @ApiResponse(code = 403, message = "Revoke IssueException is forbidden") }) - @ResponseBody - - public ResponseEntity revokeIssueException( - @ApiParam(value = "Provide Issue Id", required = true) @RequestParam(required = true) String issueId) { - try { - Boolean isIssueExceptionRevoked = complianceService.revokeIssueException(issueId); - if (isIssueExceptionRevoked) { - return ResponseUtils.buildSucessResponse("Successfully Revoked Issue Exception"); - } else { - return ResponseUtils.buildFailureResponse(new Exception("Failed in Revoking Issue Exception")); - } - } catch (ServiceException exception) { - return ResponseUtils.buildFailureResponse(exception); - } - } - - /** - * Gets the non compliance policy by rule.request expects asset group and - * domain as mandatory.Api returns list of all the rules associated to that - * domain with compliance percentage/severity/ruleCategory etc fields. - * - * @param request the request - * @return ResponseEntity - */ - @RequestMapping(path = "/v1/noncompliancepolicy", method = RequestMethod.POST) - // @Cacheable(cacheNames="compliance",unless="#result.status==200") - // commenting to performance after refacoting - // @Cacheable(cacheNames="compliance",key="#request.key") - - public ResponseEntity getNonCompliancePolicyByRule(@RequestBody(required = false) Request request) { - String assetGroup = request.getAg(); - - Map filters = request.getFilter(); - - if (Strings.isNullOrEmpty(assetGroup) || MapUtils.isEmpty(filters) - || Strings.isNullOrEmpty(filters.get(DOMAIN))) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_GROUP_DOMAIN)); - } - ResponseWithOrder response = null; - try { - response = (complianceService.getRulecompliance(request)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(response); - - } - - /** - * Gets the policy details by application.asssetGroup and ruleId are - * mandatory. API returns total/application/compliant/compliantPercentage of - * the ruleId for given assetGroup. SearchText is used to match any text you - * are looking for - * - * @param assetGroup name of the asset group - * @param ruleId the rule id - * @param searchText the search text - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/policydetailsbyapplication", method = RequestMethod.GET) - // @Cacheable(cacheNames="compliance",unless="#result.status==200") - - public ResponseEntity getPolicydetailsbyApplication(@RequestParam("ag") String assetGroup, - @RequestParam("ruleId") String ruleId, - @RequestParam(name = "searchText", required = false) String searchText) { - if (Strings.isNullOrEmpty(assetGroup) || Strings.isNullOrEmpty(ruleId)) { - return ResponseUtils.buildFailureResponse(new Exception("Assetgroup/ruleId is mandatory")); - } - ResponseData response = null; - try { - - response = new ResponseData(complianceService.getRuleDetailsbyApplication(assetGroup, ruleId, searchText)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the policy details by environment.asssetGroup,application and ruleId - * are mandatory. API returns - * total/environment/compliant/compliantPercentage of the ruleId for given - * assetGroup and application. SearchText is used to match any text you are - * looking for - * - * @param assetGroup name of the asset group - * @param application name of the application - * @param ruleId the rule id - * @param searchText the search text - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/policydetailsbyenvironment", method = RequestMethod.GET) - - public ResponseEntity getpolicydetailsbyEnvironment(@RequestParam("ag") String assetGroup, - @RequestParam("application") String application, @RequestParam("ruleId") String ruleId, - @RequestParam(name = "searchText", required = false) String searchText) { - - if (Strings.isNullOrEmpty(assetGroup) || Strings.isNullOrEmpty(application) || Strings.isNullOrEmpty(ruleId)) { - return ResponseUtils.buildFailureResponse(new Exception("assetgroup/application/ruleId is mandatory")); - } - ResponseData response = null; - try { - response = new ResponseData(complianceService.getRuleDetailsbyEnvironment(assetGroup, ruleId, application, - searchText)); - - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * API returns details of the given ruleId. - * - * @param ruleId the rule id - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/policydescription", method = RequestMethod.GET) - - public ResponseEntity getPolicyDescription(@RequestParam("ruleId") String ruleId) { - - if (Strings.isNullOrEmpty(ruleId)) { - return ResponseUtils.buildFailureResponse(new Exception("ruleId Mandatory")); - } - PolicyDescription response = null; - try { - response = new PolicyDescription(complianceService.getRuleDescription(ruleId)); - - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * API returns the kernel version of the given instanceId if it is - * from web service. - * - * @param instanceId the instance id - * @return ResponseEntity - */ - @RequestMapping(path = "/v1/kernelcompliancebyinstanceid", method = RequestMethod.GET) - - public ResponseEntity getKernelComplianceByInstanceId(@RequestParam("instanceId") String instanceId) { - - if (Strings.isNullOrEmpty(instanceId)) { - return ResponseUtils.buildFailureResponse(new Exception("instanceId is mandatory")); - } - PolicyDescription output = null; - try { - output = new PolicyDescription(complianceService.getKernelComplianceByInstanceIdFromDb(instanceId)); - - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(output); - } - - /** - * API returns true if it updates the kernel version for the given - * instanceId successfully. - * - * @param kernelVersion the kernel version - * @return ResponseEntity - */ - - @ApiOperation(httpMethod = "PUT", value = "Update Kernel Version by InstanceId") - @RequestMapping(path = "/v1/update-kernel-version", method = RequestMethod.PUT) - @ResponseBody - - public ResponseEntity updateKernelVersion( - @ApiParam(value = "Provide valid Rule Details ", required = true) @RequestBody(required = true) KernelVersion kernelVersion) { - Map response = complianceService.updateKernelVersion(kernelVersion); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - /** - * API returns overall compliance based on rule category and severity weightages - * for given asset group and domain. - * - * @param assetGroup - String - * @param domain - String - * @return ResponseEntity . - */ - @RequestMapping(path = "/v1/overallcompliance", method = RequestMethod.GET) - - public ResponseEntity getOverallCompliance(@RequestParam("ag") String assetGroup, - @RequestParam(name = "domain") String domain) { - if (Strings.isNullOrEmpty(assetGroup) || Strings.isNullOrEmpty(domain)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_GROUP_DOMAIN)); - } - DitributionDTO distribution = null; - try { - distribution = new DitributionDTO(complianceService.getOverallComplianceByDomain(assetGroup, domain)); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(distribution); - } - - /** - * API returns targetTypes for given asset group and domain based on - * project target types configurations. - * - * @param assetgroup the assetgroup - * @param domain the domain - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/targetType", method = RequestMethod.GET) - - public ResponseEntity getTargetType(@RequestParam("ag") String assetgroup, - @RequestParam(name = "domain", required = false) String domain) { - - if (Strings.isNullOrEmpty(assetgroup)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); - } - ResourceTypeResponse response; - try { - - response = new ResourceTypeResponse(complianceService.getResourceType(assetgroup, domain)); - } catch (Exception e) { - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * API returns reason for violation along with other details for the - * given asset group and issueId. - * - * @param assetgroup the assetgroup - * @param issueId the issue id - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/policyViolationReason", method = RequestMethod.GET) - public ResponseEntity policyViolationReason(@RequestParam("ag") String assetgroup, - @RequestParam(name = "issueId") String issueId) { - - if (Strings.isNullOrEmpty(assetgroup) && Strings.isNullOrEmpty(issueId)) { - return ResponseUtils.buildFailureResponse(new Exception("AssetGroup/IssueId is Mandatory")); - } - PolicyViolationDetails response = null; - try { - response = complianceService.getPolicyViolationDetailsByIssueId(assetgroup, issueId); - } catch (ServiceException e) { - return complianceService.formatException(e); - } - return ResponseUtils.buildSucessResponse(response); - - } - - - /** - * API returns current kernel versions. - * - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/get-current-kernel-versions", method = RequestMethod.GET) - public ResponseEntity getCurrentKernelVersions() { - return ResponseUtils.buildSucessResponse(complianceService.getCurrentKernelVersions()); - } - - /** - * Adds the issues exception. - * - * @param issuesException the issues exception - * @return the response entity - */ - @ApiOperation(httpMethod = "POST", value = "Adding issue exception to the corresponding target type") - @RequestMapping(path = "/v2/issue/add-exception", method = RequestMethod.POST) - @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully Added Issue Exception"), - @ApiResponse(code = 401, message = "You are not authorized to Add Issue Exception"), - @ApiResponse(code = 403, message = "Add Issue Exception is forbidden") }) - @ResponseBody - - public ResponseEntity addIssuesException( - @ApiParam(value = "Provide Issue Exception Details", required = true) @RequestBody(required = true) IssuesException issuesException) { - try { - - if (issuesException.getExceptionGrantedDate() == null) { - return ResponseUtils.buildFailureResponse(new Exception("Exception Granted Date is mandatory")); - } - if (issuesException.getExceptionEndDate() == null) { - return ResponseUtils.buildFailureResponse(new Exception("Exception End Date is mandatory")); - } - - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); - Calendar cal = Calendar.getInstance(); - cal.setTimeZone(TimeZone.getTimeZone("UTC")); - if(sdf.parse(sdf.format(issuesException.getExceptionGrantedDate())).before(sdf.parse(sdf.format(cal.getTime())))) { - return ResponseUtils.buildFailureResponse(new Exception("Exception Granted Date cannot be earlier date than today")); - } - if(sdf.parse(sdf.format(issuesException.getExceptionEndDate())).before(sdf.parse(sdf.format(cal.getTime())))) { - return ResponseUtils.buildFailureResponse(new Exception("Exception End Date cannot be earlier date than today")); - } - if(issuesException.getIssueIds().isEmpty()) { - return ResponseUtils.buildFailureResponse(new Exception("Atleast one issue id is required")); - } - return ResponseUtils.buildSucessResponse(complianceService.addMultipleIssueException(issuesException)); - } catch (ServiceException | ParseException exception) { - return ResponseUtils.buildFailureResponse(exception); - } - } - - /** - * Revoke issue exception. - * - * @param issueIds the issue ids - * @return ResponseEntity - */ - @ApiOperation(httpMethod = "POST", value = "Revoking issue exception to the corresponding target type") - @RequestMapping(path = "/v2/issue/revoke-exception", method = RequestMethod.POST) - @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully Revoked Issue Exception"), - @ApiResponse(code = 401, message = "You are not authorized to Revoke Issue Exception"), - @ApiResponse(code = 403, message = "Revoke IssueException is forbidden") }) - @ResponseBody - - public ResponseEntity revokeIssuesException( - @ApiParam(value = "Provide Issue Id", required = true) @RequestBody(required = true) RevokeIssuesException revokeIssuesException) { - try { - if(revokeIssuesException.getIssueIds().isEmpty()) { - return ResponseUtils.buildFailureResponse(new Exception("Atleast one issue id is required")); - } - return ResponseUtils.buildSucessResponse(complianceService.revokeMultipleIssueException(revokeIssuesException.getIssueIds())); - } catch (ServiceException exception) { - return ResponseUtils.buildFailureResponse(exception); - } - } -} +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.compliance.controller; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; + +import org.apache.commons.collections.MapUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import com.google.common.base.Strings; +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.ServiceException; +import com.tmobile.pacman.api.commons.utils.ResponseUtils; +import com.tmobile.pacman.api.compliance.domain.DitributionDTO; +import com.tmobile.pacman.api.compliance.domain.IssueAuditLogRequest; +import com.tmobile.pacman.api.compliance.domain.IssueResponse; +import com.tmobile.pacman.api.compliance.domain.IssuesException; +import com.tmobile.pacman.api.compliance.domain.KernelVersion; +import com.tmobile.pacman.api.compliance.domain.OutputDTO; +import com.tmobile.pacman.api.compliance.domain.PolicyDescription; +import com.tmobile.pacman.api.compliance.domain.PolicyViolationDetails; +import com.tmobile.pacman.api.compliance.domain.Request; +import com.tmobile.pacman.api.compliance.domain.ResourceTypeResponse; +import com.tmobile.pacman.api.compliance.domain.ResponseData; +import com.tmobile.pacman.api.compliance.domain.ResponseWithOrder; +import com.tmobile.pacman.api.compliance.domain.RevokeIssuesException; +import com.tmobile.pacman.api.compliance.domain.RuleDetails; +import com.tmobile.pacman.api.compliance.service.ComplianceService; + +/** + * The Class ComplianceController. + */ +@RestController +@PreAuthorize("@securityService.hasPermission(authentication, 'ROLE_USER')") +public class ComplianceController implements Constants { + + /** The compliance service. */ + @Autowired + private ComplianceService complianceService; + + /** + * Gets the issues details.Request expects asssetGroup and domain as + * mandatory, ruleId as optional.If API receives assetGroup and domain as + * request parameter, it gives details of all open issues for all the rules + * associated to that domain. If API receives assetGroup, domain and ruleId + * as request parameter,it gives only open issues of that rule associated to + * that domain. SearchText is used to match any text you are looking + * for.From and size are for the pagination + * + * @param request request body + * @return issues + */ + + @RequestMapping(path = "/v1/issues", method = RequestMethod.POST) + @ResponseBody + public ResponseEntity getIssues(@RequestBody(required = false) Request request) { + String assetGroup = request.getAg(); + Map filters = request.getFilter(); + + if (Strings.isNullOrEmpty(assetGroup) || MapUtils.isEmpty(filters) + || Strings.isNullOrEmpty(filters.get(DOMAIN))) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_GROUP_DOMAIN)); + } + ResponseWithOrder response = null; + try { + response = complianceService.getIssues(request); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the issues count. asssetGroup and domain are mandatory & ruleId is + * optional parameter, it gives issues count of all open issues for all the rules + * associated to that domain. If API receives assetGroup,domain and ruleId + * as request parameter,it gives issues count of all open issues for that + * rule associated to that domain. + * + * @param assetGroup name of the asset group + * @param domain the domain + * @param ruleId the rule id + * @return the issues count + */ + + @RequestMapping(path = "/v1/issues/count", method = RequestMethod.GET) + public ResponseEntity getIssuesCount(@RequestParam("ag") String assetGroup, + @RequestParam("domain") String domain, @RequestParam(name = "ruleId", required = false) String ruleId) { + if (Strings.isNullOrEmpty(assetGroup) || Strings.isNullOrEmpty(domain)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_GROUP_DOMAIN)); + } + Map response = new HashMap<>(); + try { + response.put("total_issues", complianceService.getIssuesCount(assetGroup, ruleId, domain)); + } catch (ServiceException e) { + return ResponseUtils.buildFailureResponse(e); + } + + return ResponseUtils.buildSucessResponse(response); + + } + + /** + * Gets the issue distribution by ruleCategory and severity.asssetGroup + * is mandatory, domain is optional. API return issue distribution rule + * severity & rule Category for given asset group + * + * @param assetGroup name of the asset group + * @param domain the domain + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/issues/distribution", method = RequestMethod.GET) + public ResponseEntity getDistribution(@RequestParam("ag") String assetGroup, + @RequestParam(name = "domain", required = false) String domain) { + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + DitributionDTO distribution = null; + try { + distribution = new DitributionDTO(complianceService.getDistribution(assetGroup, domain)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(distribution); + } + + /** + * Gets the tagging compliance summary.asssetGroup is mandatory and + * targetType is optional If API receives assetGroup as request parameter, + * api returns tagged/un-tagged/asset count of all the target types for that + * asset group. If API receives both assetGroup and targetType as request + * parameter,api returns tagged/un-tagged/asset count of specified target + * type. + * + * @param assetGroup name of the asset group + * @param targetType the target type + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/tagging", method = RequestMethod.GET) + public ResponseEntity getTagging(@RequestParam("ag") String assetGroup, + @RequestParam(name = "targettype", required = false) String targetType) { + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + OutputDTO output = null; + try { + output = new OutputDTO(complianceService.getTagging(assetGroup, targetType)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(output); + } + + /** + * Gets the certificates compliance details.asssetGroup is mandatory. API + * returns count of expiredCertificates with in 60days and totalCertificates + * for given assetGroup + * + * @param assetGroup name of the asset group + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/certificates", method = RequestMethod.GET) + public ResponseEntity getCertificates(@RequestParam("ag") String assetGroup) { + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + OutputDTO output = null; + try { + output = new OutputDTO(complianceService.getCertificates(assetGroup)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(output); + } + + /** + * Gets the patching compliance details.AssetGroup is mandatory. API returns + * count of totalPached/toalUnpatched/TotalInstances for given assetGroup + * + * @param assetGroup name of the asset group + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/patching", method = RequestMethod.GET) + public ResponseEntity getPatching(@RequestParam("ag") String assetGroup) { + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception("Asset group is mandatory")); + } + OutputDTO output = null; + try { + output = new OutputDTO(complianceService.getPatching(assetGroup, null)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(output); + } + + /** + * Gets the recommendations details by policy.asssetGroup is mandatory and + * targetType is optional. If API receives assetGroup as request parameter, + * API returns list of all the issue counts which are related to + * recommendations rules from the ES for the given assetGroup with all the + * targetTypes.If API receives both assetGroup and targetType as request + * parameter,API returns list of all the issue counts which are related to + * recommendations rules from the ES for the given targetType & assetGroup. + * + * @param assetGroup name of the asset group + * @param targetType the target type + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/recommendations", method = RequestMethod.GET) + public ResponseEntity getRecommendations(@RequestParam("ag") String assetGroup, + @RequestParam(name = "targettype", required = false) String targetType) { + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + ResponseData response = null; + try { + response = new ResponseData(complianceService.getRecommendations(assetGroup, targetType)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(response); + + } + + /** + * Gets the issue audit details.This request accepts + * annotationId,targetType,size as mandatory. If API receives + * annotationId,targetType,size as request parameter, API returns list of + * data source, audit date and status of that annotationId. searchText is used + * to match any text you are looking for. from and size are for pagination. + * + * @param request the request + * @return the issue audit + */ + + @RequestMapping(path = "/v1/issueauditlog", method = RequestMethod.POST) + public ResponseEntity getIssueAudit(@RequestBody IssueAuditLogRequest request) { + String issueId = request.getIssueId(); + String targetType = request.getTargetType(); + int from = request.getFrom(); + int size = request.getSize(); + String searchText = request.getSearchText(); + if (Strings.isNullOrEmpty(issueId) || Strings.isNullOrEmpty(targetType) || from < 0 || size <= 0) { + return ResponseUtils.buildFailureResponse(new Exception("IssueId/Targettype/from/size is Mandatory")); + } + ResponseWithOrder response = null; + try { + response = complianceService.getIssueAuditLog(issueId, targetType, from, size, searchText); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(response); + + } + + /** + * Gets the resource details.assetGroup and resourceId are mandatory. API + * returns map details for given resourceId + * + * @param assetGroup name of the asset group + * @param resourceId the resource id + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/resourcedetails", method = RequestMethod.GET) + public ResponseEntity getResourceDetails(@RequestParam("ag") String assetGroup, + @RequestParam("resourceId") String resourceId) { + if (Strings.isNullOrEmpty(resourceId)) { + return ResponseUtils.buildFailureResponse(new Exception("assetGroup/resourceId is mandatory")); + } + ResponseData response = null; + try { + response = new ResponseData(complianceService.getResourceDetails(assetGroup, resourceId)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Close issues.ruleDetails expects ruleId,reason and userId, Api returns + * true if its successfully closes all issues in ES for that ruleId else + * false + * + * @param ruleDetails the rule details + * @return ResponseEntity + */ + @ApiOperation(httpMethod = "PUT", value = "Close Issues by Rule Details") + @RequestMapping(path = "/v1/issues/close-by-rule-id", method = RequestMethod.PUT) + @ResponseBody + + public ResponseEntity closeIssues( + @ApiParam(value = "Provide valid Rule Details ", required = true) @RequestBody(required = true) RuleDetails ruleDetails) { + Map response = complianceService.closeIssuesByRule(ruleDetails); + if (Integer.parseInt(response.get("status").toString()) == TWO_HUNDRED) { + return new ResponseEntity<>(response, HttpStatus.OK); + } else { + return new ResponseEntity<>(response, HttpStatus.FORBIDDEN); + } + } + + /** + * Adds the issue exception.issueException expects + * issueId,exceptionGrantedDate,exceptionEndDate and exceptionReason, API is + * for adding issue exception to the corresponding target type. + * + * @param issueException the issue exception + * @return ResponseEntity + */ + @ApiOperation(httpMethod = "POST", value = "Adding issue exception to the corresponding target type") + @RequestMapping(path = "/v1/issues/add-exception", method = RequestMethod.POST) + @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully Added Issue Exception"), + @ApiResponse(code = 401, message = "You are not authorized to Add Issue Exception"), + @ApiResponse(code = 403, message = "Add Issue Exception is forbidden") }) + @ResponseBody + + public ResponseEntity addIssueException( + @ApiParam(value = "Provide Issue Exception Details", required = true) @RequestBody(required = true) IssueResponse issueException) { + try { + Boolean isExempted = complianceService.addIssueException(issueException); + if (isExempted) { + return ResponseUtils.buildSucessResponse("Successfully Added Issue Exception"); + } else { + return ResponseUtils.buildFailureResponse(new Exception("Failed in Adding Issue Exception")); + } + } catch (ServiceException exception) { + return ResponseUtils.buildFailureResponse(exception); + } + } + + /** + * Revoke issue exception. + * + * @param issueId the issue id + * @return ResponseEntity + */ + @ApiOperation(httpMethod = "POST", value = "Revoking issue exception to the corresponding target type") + @RequestMapping(path = "/v1/issues/revoke-exception", method = RequestMethod.POST) + @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully Revoked Issue Exception"), + @ApiResponse(code = 401, message = "You are not authorized to Revoke Issue Exception"), + @ApiResponse(code = 403, message = "Revoke IssueException is forbidden") }) + @ResponseBody + + public ResponseEntity revokeIssueException( + @ApiParam(value = "Provide Issue Id", required = true) @RequestParam(required = true) String issueId) { + try { + Boolean isIssueExceptionRevoked = complianceService.revokeIssueException(issueId); + if (isIssueExceptionRevoked) { + return ResponseUtils.buildSucessResponse("Successfully Revoked Issue Exception"); + } else { + return ResponseUtils.buildFailureResponse(new Exception("Failed in Revoking Issue Exception")); + } + } catch (ServiceException exception) { + return ResponseUtils.buildFailureResponse(exception); + } + } + + /** + * Gets the non compliance policy by rule.request expects asset group and + * domain as mandatory.Api returns list of all the rules associated to that + * domain with compliance percentage/severity/ruleCategory etc fields. + * + * @param request the request + * @return ResponseEntity + */ + @RequestMapping(path = "/v1/noncompliancepolicy", method = RequestMethod.POST) + // @Cacheable(cacheNames="compliance",unless="#result.status==200") + // commenting to performance after refacoting + // @Cacheable(cacheNames="compliance",key="#request.key") + + public ResponseEntity getNonCompliancePolicyByRule(@RequestBody(required = false) Request request) { + String assetGroup = request.getAg(); + + Map filters = request.getFilter(); + + if (Strings.isNullOrEmpty(assetGroup) || MapUtils.isEmpty(filters) + || Strings.isNullOrEmpty(filters.get(DOMAIN))) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_GROUP_DOMAIN)); + } + ResponseWithOrder response = null; + try { + response = (complianceService.getRulecompliance(request)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(response); + + } + + /** + * Gets the policy details by application.asssetGroup and ruleId are + * mandatory. API returns total/application/compliant/compliantPercentage of + * the ruleId for given assetGroup. SearchText is used to match any text you + * are looking for + * + * @param assetGroup name of the asset group + * @param ruleId the rule id + * @param searchText the search text + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/policydetailsbyapplication", method = RequestMethod.GET) + // @Cacheable(cacheNames="compliance",unless="#result.status==200") + + public ResponseEntity getPolicydetailsbyApplication(@RequestParam("ag") String assetGroup, + @RequestParam("ruleId") String ruleId, + @RequestParam(name = "searchText", required = false) String searchText) { + if (Strings.isNullOrEmpty(assetGroup) || Strings.isNullOrEmpty(ruleId)) { + return ResponseUtils.buildFailureResponse(new Exception("Assetgroup/ruleId is mandatory")); + } + ResponseData response = null; + try { + + response = new ResponseData(complianceService.getRuleDetailsbyApplication(assetGroup, ruleId, searchText)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the policy details by environment.asssetGroup,application and ruleId + * are mandatory. API returns + * total/environment/compliant/compliantPercentage of the ruleId for given + * assetGroup and application. SearchText is used to match any text you are + * looking for + * + * @param assetGroup name of the asset group + * @param application name of the application + * @param ruleId the rule id + * @param searchText the search text + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/policydetailsbyenvironment", method = RequestMethod.GET) + + public ResponseEntity getpolicydetailsbyEnvironment(@RequestParam("ag") String assetGroup, + @RequestParam("application") String application, @RequestParam("ruleId") String ruleId, + @RequestParam(name = "searchText", required = false) String searchText) { + + if (Strings.isNullOrEmpty(assetGroup) || Strings.isNullOrEmpty(application) || Strings.isNullOrEmpty(ruleId)) { + return ResponseUtils.buildFailureResponse(new Exception("assetgroup/application/ruleId is mandatory")); + } + ResponseData response = null; + try { + response = new ResponseData(complianceService.getRuleDetailsbyEnvironment(assetGroup, ruleId, application, + searchText)); + + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * API returns details of the given ruleId. + * + * @param ruleId the rule id + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/policydescription", method = RequestMethod.GET) + + public ResponseEntity getPolicyDescription(@RequestParam("ruleId") String ruleId) { + + if (Strings.isNullOrEmpty(ruleId)) { + return ResponseUtils.buildFailureResponse(new Exception("ruleId Mandatory")); + } + PolicyDescription response = null; + try { + response = new PolicyDescription(complianceService.getRuleDescription(ruleId)); + + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * API returns the kernel version of the given instanceId if it is + * from web service. + * + * @param instanceId the instance id + * @return ResponseEntity + */ + @RequestMapping(path = "/v1/kernelcompliancebyinstanceid", method = RequestMethod.GET) + + public ResponseEntity getKernelComplianceByInstanceId(@RequestParam("instanceId") String instanceId) { + + if (Strings.isNullOrEmpty(instanceId)) { + return ResponseUtils.buildFailureResponse(new Exception("instanceId is mandatory")); + } + PolicyDescription output = null; + try { + output = new PolicyDescription(complianceService.getKernelComplianceByInstanceIdFromDb(instanceId)); + + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(output); + } + + /** + * API returns true if it updates the kernel version for the given + * instanceId successfully. + * + * @param kernelVersion the kernel version + * @return ResponseEntity + */ + + @ApiOperation(httpMethod = "PUT", value = "Update Kernel Version by InstanceId") + @RequestMapping(path = "/v1/update-kernel-version", method = RequestMethod.PUT) + @ResponseBody + + public ResponseEntity updateKernelVersion( + @ApiParam(value = "Provide valid Rule Details ", required = true) @RequestBody(required = true) KernelVersion kernelVersion) { + Map response = complianceService.updateKernelVersion(kernelVersion); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + /** + * API returns overall compliance based on rule category and severity weightages + * for given asset group and domain. + * + * @param assetGroup - String + * @param domain - String + * @return ResponseEntity . + */ + @RequestMapping(path = "/v1/overallcompliance", method = RequestMethod.GET) + + public ResponseEntity getOverallCompliance(@RequestParam("ag") String assetGroup, + @RequestParam(name = "domain") String domain) { + if (Strings.isNullOrEmpty(assetGroup) || Strings.isNullOrEmpty(domain)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_GROUP_DOMAIN)); + } + DitributionDTO distribution = null; + try { + distribution = new DitributionDTO(complianceService.getOverallComplianceByDomain(assetGroup, domain)); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(distribution); + } + + /** + * API returns targetTypes for given asset group and domain based on + * project target types configurations. + * + * @param assetgroup the assetgroup + * @param domain the domain + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/targetType", method = RequestMethod.GET) + + public ResponseEntity getTargetType(@RequestParam("ag") String assetgroup, + @RequestParam(name = "domain", required = false) String domain) { + + if (Strings.isNullOrEmpty(assetgroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + ResourceTypeResponse response; + try { + + response = new ResourceTypeResponse(complianceService.getResourceType(assetgroup, domain)); + } catch (Exception e) { + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * API returns reason for violation along with other details for the + * given asset group and issueId. + * + * @param assetgroup the assetgroup + * @param issueId the issue id + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/policyViolationReason", method = RequestMethod.GET) + public ResponseEntity policyViolationReason(@RequestParam("ag") String assetgroup, + @RequestParam(name = "issueId") String issueId) { + + if (Strings.isNullOrEmpty(assetgroup) && Strings.isNullOrEmpty(issueId)) { + return ResponseUtils.buildFailureResponse(new Exception("AssetGroup/IssueId is Mandatory")); + } + PolicyViolationDetails response = null; + try { + response = complianceService.getPolicyViolationDetailsByIssueId(assetgroup, issueId); + } catch (ServiceException e) { + return complianceService.formatException(e); + } + return ResponseUtils.buildSucessResponse(response); + + } + + + /** + * API returns current kernel versions. + * + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/get-current-kernel-versions", method = RequestMethod.GET) + public ResponseEntity getCurrentKernelVersions() { + return ResponseUtils.buildSucessResponse(complianceService.getCurrentKernelVersions()); + } + + /** + * Adds the issues exception. + * + * @param issuesException the issues exception + * @return the response entity + */ + @ApiOperation(httpMethod = "POST", value = "Adding issue exception to the corresponding target type") + @RequestMapping(path = "/v2/issue/add-exception", method = RequestMethod.POST) + @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully Added Issue Exception"), + @ApiResponse(code = 401, message = "You are not authorized to Add Issue Exception"), + @ApiResponse(code = 403, message = "Add Issue Exception is forbidden") }) + @ResponseBody + + public ResponseEntity addIssuesException( + @ApiParam(value = "Provide Issue Exception Details", required = true) @RequestBody(required = true) IssuesException issuesException) { + try { + + if (issuesException.getExceptionGrantedDate() == null) { + return ResponseUtils.buildFailureResponse(new Exception("Exception Granted Date is mandatory")); + } + if (issuesException.getExceptionEndDate() == null) { + return ResponseUtils.buildFailureResponse(new Exception("Exception End Date is mandatory")); + } + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("UTC")); + if(sdf.parse(sdf.format(issuesException.getExceptionGrantedDate())).before(sdf.parse(sdf.format(cal.getTime())))) { + return ResponseUtils.buildFailureResponse(new Exception("Exception Granted Date cannot be earlier date than today")); + } + if(sdf.parse(sdf.format(issuesException.getExceptionEndDate())).before(sdf.parse(sdf.format(cal.getTime())))) { + return ResponseUtils.buildFailureResponse(new Exception("Exception End Date cannot be earlier date than today")); + } + if(issuesException.getIssueIds().isEmpty()) { + return ResponseUtils.buildFailureResponse(new Exception("Atleast one issue id is required")); + } + return ResponseUtils.buildSucessResponse(complianceService.addMultipleIssueException(issuesException)); + } catch (ServiceException | ParseException exception) { + return ResponseUtils.buildFailureResponse(exception); + } + } + + /** + * Revoke issue exception. + * + * @param issueIds the issue ids + * @return ResponseEntity + */ + @ApiOperation(httpMethod = "POST", value = "Revoking issue exception to the corresponding target type") + @RequestMapping(path = "/v2/issue/revoke-exception", method = RequestMethod.POST) + @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully Revoked Issue Exception"), + @ApiResponse(code = 401, message = "You are not authorized to Revoke Issue Exception"), + @ApiResponse(code = 403, message = "Revoke IssueException is forbidden") }) + @ResponseBody + + public ResponseEntity revokeIssuesException( + @ApiParam(value = "Provide Issue Id", required = true) @RequestBody(required = true) RevokeIssuesException revokeIssuesException) { + try { + if(revokeIssuesException.getIssueIds().isEmpty()) { + return ResponseUtils.buildFailureResponse(new Exception("Atleast one issue id is required")); + } + return ResponseUtils.buildSucessResponse(complianceService.revokeMultipleIssueException(revokeIssuesException.getIssueIds())); + } catch (ServiceException exception) { + return ResponseUtils.buildFailureResponse(exception); + } + } +} diff --git a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/DownloadController.java b/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/DownloadController.java index eddb772f6..80747d25b 100644 --- a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/DownloadController.java +++ b/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/DownloadController.java @@ -97,10 +97,6 @@ public class DownloadController implements Constants { @Autowired(required=false) private CertificateController certificateController; - /** The vulnerability controller. */ - @Autowired(required=false) - private VulnerabilityController vulnerabilityController; - /** The compliance controller. */ @Autowired private ComplianceController complianceController; diff --git a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/VulnerabilityController.java b/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/VulnerabilityController.java deleted file mode 100644 index 222dd56c4..000000000 --- a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/controller/VulnerabilityController.java +++ /dev/null @@ -1,786 +0,0 @@ -/******************************************************************************* - * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - ******************************************************************************/ -/* - * - */ -package com.tmobile.pacman.api.compliance.controller; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cache.annotation.CacheConfig; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.google.common.base.Strings; -import com.tmobile.pacman.api.commons.Constants; -import com.tmobile.pacman.api.commons.exception.DataException; -import com.tmobile.pacman.api.commons.exception.ServiceException; -import com.tmobile.pacman.api.commons.utils.ResponseUtils; -import com.tmobile.pacman.api.compliance.domain.DitributionDTO; -import com.tmobile.pacman.api.compliance.domain.Request; -import com.tmobile.pacman.api.compliance.domain.ResponseData; -import com.tmobile.pacman.api.compliance.domain.ResponseWithCount; -import com.tmobile.pacman.api.compliance.domain.TrendNote; -import com.tmobile.pacman.api.compliance.domain.TrendRequest; -import com.tmobile.pacman.api.compliance.service.VulnerabilityService; - -/** - * The Class VulnerabilityController. - */ -@RestController -@PreAuthorize("@securityService.hasPermission(authentication, 'ROLE_USER')") -@CacheConfig(cacheNames = { "trends" }) -@ConditionalOnProperty(name="features.vulnerability.enabled") -public class VulnerabilityController implements Constants { - - private static final Logger LOGGER = LoggerFactory.getLogger(VulnerabilityController.class); - - @Autowired - private VulnerabilityService vulnerabilityService; - - /** - * Gets the vulnerabilities details. - * - * @param request the request - * @return ResponseEntity - */ - - @SuppressWarnings("unchecked") - @PostMapping(value = "/v1/vulnerabilities/detail") - public ResponseEntity getVulnerabilitiesDetails( - @RequestBody(required = true) Request request) { - - ResponseWithCount response; - String assetGroup = request.getAg(); - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception( - ASSET_MANDATORY)); - } - - int from = request.getFrom(); - int size = request.getSize(); - if (from < 0) { - return ResponseUtils.buildFailureResponse(new Exception( - "From should not be a negative number")); - - } - - String searchText = request.getSearchtext(); - - Map filter = request.getFilter(); - if (filter == null) { - filter = new HashMap<>(); - } - - try { - - List> masterDetailList = vulnerabilityService - .getVulnerabilitiesDetails(assetGroup, filter); - - masterDetailList = (List>) vulnerabilityService - .filterMatchingCollectionElements(masterDetailList, - searchText, true); - if (masterDetailList.isEmpty()) { - return ResponseUtils.buildSucessResponse(new ResponseWithCount( - new ArrayList>(), 0)); - } - - if (from >= masterDetailList.size()) { - return ResponseUtils.buildFailureResponse(new Exception( - "From exceeds the size of list")); - } - - int endIndex = 0; - - if ((from + size) > masterDetailList.size()) { - endIndex = masterDetailList.size(); - } else { - endIndex = from + size; - } - - if (endIndex == 0) { - endIndex = masterDetailList.size(); - } - - // from - inclusive, endIndex - exclusive - List> subDetailList = masterDetailList.subList( - from, endIndex); - - response = new ResponseWithCount(subDetailList, - masterDetailList.size()); - - } catch (Exception e) { - LOGGER.error(EXE_VULN , e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the vulnerability summary. - * - * @param assetGroup the asset group - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/vulnerabilities/summary", method = RequestMethod.GET) - public ResponseEntity getVulnerabilitysummary( - @RequestParam(name = "ag", required = true) String assetGroup, @RequestParam( name="severity",required=false) String severity) { - - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception( - ASSET_MANDATORY)); - } - if(Strings.isNullOrEmpty(severity)){ - severity = SEVERITY_LEVELS; - } - DitributionDTO response; - try { - response = new DitributionDTO( - vulnerabilityService.getVulnerabilitySummary(assetGroup,severity)); - } catch (Exception e) { - LOGGER.error("Exception in getVulnerabilitysummary ", e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the vulnerability by applications. - * - * @param assetGroup the asset group - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/vulnerabilities/summarybyapplication", method = RequestMethod.GET) - public ResponseEntity getVulnerabilityByApplications( - @RequestParam("ag") String assetGroup) { - - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception( - ASSET_MANDATORY)); - } - ResponseData response; - try { - response = new ResponseData( - vulnerabilityService.getVulnerabilityByAppAndEnv( - assetGroup, "tags.Application.keyword", "")); - } catch (Exception e) { - LOGGER.error("Exception in vulnerabilitybyapplications ",e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the vulnerabilities trend. - * - * @param request the request - * @return ResponseEntity - */ - - @PostMapping(value = "/v1/vulnerabilities/trend") - public ResponseEntity getVulnerabilitiesTrend( - @RequestBody(required = true) TrendRequest request) { - - Map response = new HashMap<>(); - String assetGroup = request.getAg(); - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception( - ASSET_MANDATORY)); - } - Date from = request.getFrom(); - Date to = request.getTo(); - Map filter = request.getFilter(); - try { - if (from == null && to == null) { - Calendar cal = Calendar.getInstance(); - cal.setTimeZone(TimeZone.getTimeZone("UTC")); - to = cal.getTime(); - cal.add(Calendar.DATE, NEG_THIRTY); - from = cal.getTime(); - } - response.put("ag", assetGroup); - List> trendList = vulnerabilityService - .getVulnerabilityTrend(assetGroup, filter, from, to); - response.put("trend", trendList); - } catch (Exception e) { - LOGGER.error(EXE_VULN , e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the vulnerability by environment. - * - * @param assetGroup the asset group - * @param application the application - * @return ResponseEntity - */ - - @RequestMapping(path = "/v1/vulnerabilities/summarybyenvironment", method = RequestMethod.GET) - public ResponseEntity getVulnerabilityByEnvironment( - @RequestParam("ag") String assetGroup, - @RequestParam(name = "application", required = false) String application) { - - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception( - ASSET_MANDATORY)); - } - ResponseData response; - try { - response = new ResponseData( - vulnerabilityService - .getVulnerabilityByAppAndEnv(assetGroup, - "tags.Environment.keyword", application)); - } catch (Exception e) { - LOGGER.error("Exception in vulnerabilitybyenvironment ", e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the vulnerability distribution. - * - * @param assetGroup the asset group - * @return ResponseEntity - */ - @RequestMapping(path = "/v1/vulnerabilities/distribution", method = RequestMethod.GET) - public ResponseEntity getVulnerabilityDistribution( - @RequestParam("ag") String assetGroup) { - - if (Strings.isNullOrEmpty(assetGroup)) { - return ResponseUtils.buildFailureResponse(new Exception( - ASSET_MANDATORY)); - } - ResponseData response; - try { - response = new ResponseData( - vulnerabilityService - .getVulnerabilitiesDistribution(assetGroup)); - } catch (Exception e) { - LOGGER.error("Exception in getVulnerabilityDistribution ", e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the vulnerabilitysummary by resource id. - * - * @param resourceId the resource id - * @return ResponseEntity - */ - @RequestMapping(path = "/v1/vulnerabilities/summary/{resourceId}", method = RequestMethod.GET) - public ResponseEntity getVulnerabilitysummaryByResourceId( - @PathVariable(name = "resourceId", required = true) String resourceId) { - - DitributionDTO response; - try { - response = new DitributionDTO( - vulnerabilityService - .getVulnerabilitysummaryByResourceId(resourceId)); - } catch (Exception e) { - LOGGER.error("Exception in getVulnerabilitysummary ", e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the vulnerability details by resource id. - * - * @param resourceId the resource id - * @param searchtext the searchtext - * @param from the from - * @param size the size - * @return ResponseEntity - */ - @SuppressWarnings("unchecked") - @RequestMapping(path = "/v1/vulnerabilities/detail/{resourceId}", method = RequestMethod.GET) - public ResponseEntity getVulnerabilityDetailsByResourceId( - @PathVariable(name = "resourceId", required = true) String resourceId, - @RequestParam(name = "searchtext", required = false) String searchtext, - @RequestParam(name = "from", required = false) Integer from, - @RequestParam(name = "size", required = false) Integer size) { - - Integer iFrom = from == null ? 0 : from; - Integer iSize = size == null ? 0 : size; - - ResponseWithCount response; - try { - List> masterDetailList = vulnerabilityService - .getVulnerabilityDetailsByResourceId(resourceId); - masterDetailList = (List>) vulnerabilityService - .filterMatchingCollectionElements(masterDetailList, - searchtext, true); - if (masterDetailList.isEmpty()) { - return ResponseUtils.buildSucessResponse(new ResponseWithCount( - new ArrayList>(), 0)); - } - - if (iFrom >= masterDetailList.size()) { - return ResponseUtils.buildFailureResponse(new Exception( - "From exceeds the size of list")); - } - - int endIndex = 0; - - if (iSize == 0) { - iSize = masterDetailList.size(); - } - - if ((iFrom + iSize) > masterDetailList.size()) { - endIndex = masterDetailList.size(); - } else { - endIndex = iFrom + iSize; - } - - List> subDetailList = masterDetailList.subList( - iFrom, endIndex); - - response = new ResponseWithCount(subDetailList, - masterDetailList.size()); - - } catch (Exception e) { - LOGGER.error(EXE_VULN , e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the vulnerability distribution summary. - * - * @param assetGroup the asset group - * @param severity the severity - * @return ResponseEntity - */ - @RequestMapping(path = "/v1/vulnerabilities/distributionsummary", method = RequestMethod.GET) - public ResponseEntity getVulnerabilityDistributionSummary( - @RequestParam("ag") String assetGroup, - @RequestParam(name = "severity", required = false) String severity) { - - return ResponseUtils.buildSucessResponse(vulnerabilityService - .getVulnerabilityDistributionSummary(assetGroup, severity)); - } - - /** - * Gets the aging distribution summary. - * - * @param assetGroup the asset group - * @param severity the severity - * @return ResponseEntity - */ - @RequestMapping(path = "/v1/vulnerabilities/aging/distributionsummary", method = RequestMethod.GET) - public ResponseEntity getAgingDistributionSummary( - @RequestParam("ag") String assetGroup, - @RequestParam(name = "severity", required = false) String severity) { - return ResponseUtils.buildSucessResponse(vulnerabilityService - .getAgingDistributionSummary(assetGroup, severity)); - } - - /** - * Gets the aging summary. - * - * @param assetGroup the asset group - * @return ResponseEntity - */ - @RequestMapping(path = "/v1/vulnerabilities/aging/summary", method = RequestMethod.GET) - public ResponseEntity getAgingSummary( - @RequestParam("ag") String assetGroup) { - return ResponseUtils.buildSucessResponse(vulnerabilityService - .getAgingSummary(assetGroup)); - } - - /** - * Gets the vulnerability by qid. - * - * @param qid the qid - * @return ResponseEntity - */ - @RequestMapping(path = "/v1/vulnerabilities/qids", method = RequestMethod.GET) - public ResponseEntity getVulnerabilityByQid( - @RequestParam("qid") String qid) { - return ResponseUtils.buildSucessResponse(vulnerabilityService - .getVulnerabilityByQid(qid)); - } - - /** - * Gets the distribution summary by vuln type. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the distribution summary by vuln type - */ - @RequestMapping(path = "/v1/vulnerabilities/distribution-vulntype", method = RequestMethod.GET) - public ResponseEntity getDistributionSummaryByVulnType( - @RequestParam("ag") String assetGroup, @RequestParam( name="severity",required=false) String severity) { - - Map response = new HashMap<>(); - - List> distributionList = new ArrayList<>(); - try { - distributionList = vulnerabilityService.getDistributionSummaryByVulnType(assetGroup, severity); - } catch (DataException e) { - LOGGER.error("Error in getDistributionSummaryByVulnType",e); - return ResponseUtils.buildFailureResponse(e); - } - - response.put(DISTRIBUTION, distributionList); - return ResponseUtils.buildSucessResponse(response); - - } - - /** - * Gets the distribution summary by infra type. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the distribution summary by infra type - */ - @RequestMapping(path = "/v1/vulnerabilities/distribution-infra", method = RequestMethod.GET) - public ResponseEntity getDistributionSummaryByInfraType( - @RequestParam("ag") String assetGroup, @RequestParam( name="severity",required=false) String severity) { - Map response = new HashMap<>(); - - List> distributionList = new ArrayList<>(); - try { - distributionList = vulnerabilityService.getDistributionSummaryByInfraType(assetGroup, severity); - } catch (ServiceException e) { - LOGGER.error("Error in getDistributionSummaryByInfraType",e); - return ResponseUtils.buildFailureResponse(e); - } - response.put(DISTRIBUTION, distributionList); - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the distribution summary by env. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the distribution summary by env - */ - @RequestMapping(path = "/v1/vulnerabilities/distribution-env", method = RequestMethod.GET) - public ResponseEntity getDistributionSummaryByEnv( - @RequestParam("ag") String assetGroup, @RequestParam( name="severity",required=false) String severity) { - Map response = new HashMap<>(); - - List> distributionList = new ArrayList<>(); - try { - distributionList = vulnerabilityService.getDistributionSummaryByEnv(assetGroup, severity); - } catch (ServiceException e) { - LOGGER.error("Error in getDistributionSummaryByEnv",e); - return ResponseUtils.buildFailureResponse(e); - } - response.put(DISTRIBUTION, distributionList); - return ResponseUtils.buildSucessResponse(response); - } - - - /** - * Gets the remediation actions summary. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the remediation actions summary - */ - @RequestMapping(path = "/v1/vulnerabilities/remediations/summary", method = RequestMethod.GET) - public ResponseEntity getRemediationActionsSummary( - @RequestParam("ag") String assetGroup, @RequestParam( name="severity",required=false) String severity) { - Map response = new HashMap<>(); - - List> remediationList = new ArrayList<>(); - try { - remediationList = vulnerabilityService.getRemediationActionsSummary(assetGroup, severity); - } catch (DataException e) { - LOGGER.error("Error in getRemediationActionsSummary",e); - return ResponseUtils.buildFailureResponse(e); - } - - response.put("actions", remediationList); - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the highest lowest performers. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the highest lowest performers - */ - @RequestMapping(path = "/v1/vulnerabilities/performers", method = RequestMethod.GET) - public ResponseEntity getHighestLowestPerformers( - @RequestParam("ag") String assetGroup, @RequestParam( name="severity",required=false) String severity) { - Map response = new HashMap<>(); - - List> responseList = new ArrayList<>(); - Map directorData = vulnerabilityService.getHighestLowestPerformers(assetGroup, severity,"org"); - Set keys = directorData.keySet(); - String[] keysArray = keys.toArray(new String[keys.size()]); - - if(keysArray.length >= 10 ) { - - Map info = new HashMap<>(); - info.put(CATEGORY, HIGHEST); - List< Map> directorList = new ArrayList<>(); - - for(int i=0; i director = new HashMap<>(); - director.put(keysArray[i], directorData.get(keysArray[i])); - directorList.add(director); - } - info.put(DIRECTORS, directorList); - responseList.add(info); - - info = new HashMap<>(); - info.put(CATEGORY, "Lowest"); - directorList = new ArrayList<>(); - - for(int i=keysArray.length-Constants.ONE; i>keysArray.length-Constants.SIX && i>=0;i--) { - Map director = new HashMap<>(); - director.put(keysArray[i], directorData.get(keysArray[i])); - directorList.add(director); - } - info.put(DIRECTORS, directorList); - responseList.add(info); - } else { - - if(keysArray.length % 2 == 0) { - Map info = new HashMap<>(); - info.put(CATEGORY, HIGHEST); - List< Map> directorList = new ArrayList<>(); - - for(int i=0; i director = new HashMap<>(); - director.put(keysArray[i], directorData.get(keysArray[i])); - directorList.add(director); - } - info.put(DIRECTORS, directorList); - responseList.add(info); - - } else { - - Map info = new HashMap<>(); - info.put(CATEGORY, HIGHEST); - List< Map> directorList = new ArrayList<>(); - - for(int i=0; i<(keysArray.length/2)+1;i++) { - Map director = new HashMap<>(); - director.put(keysArray[i], directorData.get(keysArray[i])); - directorList.add(director); - } - info.put(DIRECTORS, directorList); - responseList.add(info); - } - - Mapinfo = new HashMap<>(); - info.put(CATEGORY, "Lowest"); - List< Map> directorList = new ArrayList<>(); - - for(int i=keysArray.length-Constants.ONE; i>keysArray.length/2 && i>=0;i--) { - Map director = new HashMap<>(); - director.put(keysArray[i], directorData.get(keysArray[i])); - directorList.add(director); - } - info.put(DIRECTORS, directorList); - responseList.add(info); - } - response.put("response", responseList); - return ResponseUtils.buildSucessResponse(response); - } - - /** - * Gets the highest lowest performers. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the highest lowest performers - */ - @RequestMapping(path = "/v2/vulnerabilities/performers", method = RequestMethod.GET) - public ResponseEntity getHighestLowestPerformers( - @RequestParam("ag") String assetGroup, @RequestParam( name="severity",required=false) String severity,@RequestParam( name="type") PerfType type) { - Map response = new HashMap<>(); - - Map perfData = vulnerabilityService.getHighestLowestPerformers(assetGroup, severity,type.name()); - List< Map> perfList = new ArrayList<>(); - Map info = new HashMap<>(); - String typeName = type.name(); - switch(typeName){ - case "org": - info.put(CATEGORY, "Director"); - break; - case "application": - info.put(CATEGORY, "Application"); - break; - case "environment": - info.put(CATEGORY, "Environment"); - break; - } - - if(perfData.size() > 1) { - Set keys = perfData.keySet(); - String[] keysArray = keys.toArray(new String[keys.size()]); - - for(int i=0; i director = new HashMap<>(); - director.put(keysArray[i], perfData.get(keysArray[i])); - perfList.add(director); - } - } - - info.put("data", perfList); - - response.put("response", info); - return ResponseUtils.buildSucessResponse(response); - } - - - /** - * Gets the vulnerability trend. - * - * @param request the request - * @return the vulnerability trend - */ - @Cacheable(cacheNames = "trends", key = "#request.vulnCacheKey" ,unless="#result.statusCodeValue!=200" ) - @RequestMapping(path = "/v1/vulnerabilities/trend/open-new", method = RequestMethod.POST) - public ResponseEntity getVulnerabilityTrend( - @RequestBody(required = true) TrendRequest request) { - - String ag = request.getAg(); - if (Strings.isNullOrEmpty(ag)) { - return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); - } - Date from = request.getFrom(); - if(from == null){ - Calendar cal = Calendar.getInstance(); - cal.setTimeZone(TimeZone.getTimeZone("UTC")); - cal.add(Calendar.DATE, NEG_THIRTY); - from = cal.getTime(); - } - - Map filter = request.getFilter(); - String severity = SEVERITY_LEVELS; - if(filter!=null){ - severity = filter.get("severity"); - if(severity==null){ - severity =SEVERITY_LEVELS; - } - } - - Map response = new HashMap<>(); - try { - List< Map> trendList = vulnerabilityService.getVulnerabilityNewOpenTrend(ag,severity,from); - response.put("trend",trendList); - return ResponseUtils.buildSucessResponse(response); - } catch (Exception e) { - return ResponseUtils.buildFailureResponse(e); - } - - } - - /** - * Creates the trend annotation. - * - * @param request the request - * @return the response entity - */ - @RequestMapping(path = "/v1/vulnerabilities/trend/notes", method = RequestMethod.POST) - public ResponseEntity createTrendAnnotation( @RequestBody(required = true) TrendNote request) { - - try { - if(vulnerabilityService.createTrendAnnotation(request)) { - return ResponseUtils.buildSucessResponse("Annotation created"); - } else { - return ResponseUtils.buildFailureResponse(new Exception("Annotation creation failed")); - } - } catch (JsonProcessingException e) { - LOGGER.error("Error in createTrendAnnotation ",e); - return ResponseUtils.buildFailureResponse(e); - } - } - - /** - * Gets the trend annotations. - * - * @param assetGroup the asset group - * @param from the from - * @return the trend annotations - */ - @RequestMapping(path = "/v1/vulnerabilities/trend/notes", method = RequestMethod.GET) - public ResponseEntity getTrendAnnotations(@RequestParam("ag") String assetGroup, @RequestParam( name="from",required=false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date from) { - - if(from == null){ - Calendar cal = Calendar.getInstance(); - cal.setTimeZone(TimeZone.getTimeZone("UTC")); - cal.add(Calendar.DATE, NEG_THIRTY); - from = cal.getTime(); - } - Map notes = new HashMap<>(); - try { - notes .put("notes", vulnerabilityService.getTrendAnnotations(assetGroup, from)); - } catch (DataException e) { - LOGGER.error("Error in getTrendAnnotations ",e); - return ResponseUtils.buildFailureResponse(e); - } - return ResponseUtils.buildSucessResponse(notes); - - } - - /** - * Delete trend annotation. - * - * @param noteId the note id - * @return the response entity - */ - @RequestMapping(path = "/v1/vulnerabilities/trend/notes/{noteId}", method = RequestMethod.DELETE) - public ResponseEntity deleteTrendAnnotation(@PathVariable(name="noteId", required = true) String noteId) { - - if(vulnerabilityService.deleteTrendAnnotation(noteId)) { - return ResponseUtils.buildSucessResponse("Annotation Deleted"); - } else { - return ResponseUtils.buildFailureResponse(new Exception("Annotation deletion failed")); - } - } -} - -enum PerfType { - org,application,environment -} diff --git a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/service/IssueTrendServiceImpl.java b/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/service/IssueTrendServiceImpl.java index d3c44a4c3..021a00359 100644 --- a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/service/IssueTrendServiceImpl.java +++ b/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/service/IssueTrendServiceImpl.java @@ -1,854 +1,830 @@ -/******************************************************************************* - * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - ******************************************************************************/ -package com.tmobile.pacman.api.compliance.service; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.util.Assert; - -import com.google.common.base.Strings; -import com.tmobile.pacman.api.commons.Constants; -import com.tmobile.pacman.api.commons.exception.DataException; -import com.tmobile.pacman.api.commons.exception.ServiceException; -import com.tmobile.pacman.api.commons.repo.ElasticSearchRepository; -import com.tmobile.pacman.api.compliance.client.AuthServiceClient; -import com.tmobile.pacman.api.compliance.domain.Asset; -import com.tmobile.pacman.api.compliance.domain.Request; -import com.tmobile.pacman.api.compliance.domain.ResponseWithOrder; -import com.tmobile.pacman.api.compliance.repository.ComplianceRepository; -import com.tmobile.pacman.api.compliance.repository.TrendRepository; -/** - * The Class IssueTrendServiceImpl. - */ -@Service -public class IssueTrendServiceImpl implements IssueTrendService, Constants { - - /** The es host. */ - @Value("${elastic-search.host}") - private String esHost; - - /** The es port. */ - @Value("${elastic-search.port}") - private int esPort; - - /** The es cluster name. */ - @Value("${elastic-search.clusterName}") - private String esClusterName; - - /** The date format. */ - @Value("${formats.date}") - private String dateFormat; - - /** The logger. */ - private final Logger logger = LoggerFactory.getLogger(getClass()); - - /** The statistics client. */ - - /** The auth client. */ - @Autowired - private AuthServiceClient authClient; - - /** The repository. */ - @Autowired - private TrendRepository repository; - - /** The compliance service. */ - @Autowired - private ComplianceService complianceService; - - /** The elastic search repository. */ - @Autowired - private ElasticSearchRepository elasticSearchRepository; - - /** The compliance repository. */ - @Autowired - private ComplianceRepository complianceRepository; - - /** The vuln service. */ - @Autowired - private VulnerabilityService vulnService; - - /** - * {@inheritDoc} - */ - public Asset finfindByName(String accountName) { - Assert.hasLength(accountName, "accountName cannot be null or empty"); - return null; - } - - /* (non-Javadoc) - * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#getTrendForIssues(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) - */ - @Override - public Map getTrendForIssues(String assetGroup, - String fromDate, String toDate, String severity, String ruleId, - String policyId, String app, String env) throws ServiceException { - Assert.hasLength(assetGroup, "assetGroup cannot be null or empty"); - - RangeGenerator generator = new RangeGenerator(); - Map mustNotFilter = new HashMap<>(); - Map mustFilter = new HashMap<>(); - if (!Strings.isNullOrEmpty(severity)) { - mustFilter.put("severity.keyword", severity); - } - - if (!Strings.isNullOrEmpty(policyId)) { - mustFilter.put("policyId.keyword", policyId); - } - - if (!Strings.isNullOrEmpty(ruleId)) { - mustFilter.put("ruleId.keyword", ruleId); - } - if (!Strings.isNullOrEmpty(app)) { - mustFilter.put("tags.Application.keyword", app); - } - - if (!Strings.isNullOrEmpty(env)) { - mustFilter.put("tags.Environment.keyword", env); - } - - List hosts = Arrays.asList(esHost, - ""); - // this has to move to config, this is just an additional end point, - // even if not provided default end point from config will work - - try { - return generator.generateTrend(esClusterName, hosts, - NINE_THOUSAND_THREE_HUNDRED, assetGroup, "issue", - "createdDate", "modifiedDate", mustNotFilter, mustFilter, - "yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - } catch (DataException e) { - - throw new ServiceException(e); - } - - } - - /* (non-Javadoc) - * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#getComplianceTrendProgress(java.lang.String, java.time.LocalDate, java.lang.String) - */ - @Override - public Map getComplianceTrendProgress(String assetGroup, - LocalDate fromDate, String domain) throws ServiceException { - Map parentMap = new HashMap<>(); - parentMap.put("ag", assetGroup); - // get list of targetypes mapped - String ttypes = complianceRepository.getTargetTypeForAG(assetGroup, - domain); - List> ruleDetails = null; - List> inputList; - List> complianceInfoList; - - if (!Strings.isNullOrEmpty(ttypes)) { - try { - ruleDetails = complianceRepository - .getRuleIdWithDisplayNameQuery(ttypes); - } catch (DataException e) { - throw new ServiceException(e); - } - } - // Make map of rule severity,category - Set ruleCat = new HashSet<>(); - List> ruleSevCatDetails; - - ruleSevCatDetails = complianceService - .getRuleSevCatDetails(ruleDetails); - - - - Map ruleCatDetails = ruleSevCatDetails.parallelStream() - .collect( - Collectors.toMap(c -> c.get(RULEID).toString(), - c -> c.get(RULE_CATEGORY), - (oldvalue, newValue) -> newValue)); - ruleCatDetails.entrySet().parallelStream() - .forEach(entry -> ruleCat.add(entry.getValue().toString())); - complianceInfoList = new ArrayList<>(); - - try { - inputList = repository - .getComplianceTrendProgress(assetGroup, fromDate, domain, - ruleCat); - } catch (DataException e) { - throw new ServiceException(e); - } - - if (!inputList.isEmpty()) { - // Sort the list by the date in ascending order - Comparator> comp = (m1, m2) -> LocalDate.parse( - m1.get("date").toString(), DateTimeFormatter.ISO_DATE) - .compareTo( - LocalDate.parse(m2.get("date").toString(), - DateTimeFormatter.ISO_DATE)); - - Collections.sort(inputList, comp); - useRealTimeDataForLatestDate(inputList, assetGroup, - COMPLIANCE_PERCENTAGE, null, domain); - inputList.forEach(inputMap -> { - Map outputMap = new HashMap<>(); - inputMap.forEach((key, value) -> { - // Other than the specified keys, ignore all other kv pairs - if ((!Strings.isNullOrEmpty(key)) - && !("_id".equalsIgnoreCase(key))) { - - outputMap.put(key, value); - } - }); - - complianceInfoList.add(outputMap); - }); - - Collections.sort(complianceInfoList, comp); - } - parentMap.put("compliance_info", complianceInfoList); - return parentMap; - } - - /* (non-Javadoc) - * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#getTrendProgress(java.lang.String, java.lang.String, java.time.LocalDate, java.time.LocalDate, java.lang.String) - */ - @Override - public Map getTrendProgress(String assetGroup, - String ruleId, LocalDate startDate, LocalDate endDate, - String trendCategory) throws ServiceException { - - List> trendList; - try{ - trendList = repository.getTrendProgress( - assetGroup, ruleId, startDate, endDate, trendCategory); - }catch(DataException e){ - throw new ServiceException(e); - } - if (!trendList.isEmpty()) { - - // Sort the list by the date in ascending order - Comparator> comp = (m1, m2) -> LocalDate.parse( - m1.get("date").toString(), DateTimeFormatter.ISO_DATE) - .compareTo( - LocalDate.parse(m2.get("date").toString(), - DateTimeFormatter.ISO_DATE)); - Collections.sort(trendList, comp); - LocalDate trendStartDate = LocalDate.parse(trendList.get(0) - .get("date").toString()); - - // Elastic Search might not have data for some dates. But we want to - // send consistent data to the consumers of this service, so we will - // populate previous where data is unavailable - fillNoDataDatesWithPrevious(trendList, trendStartDate, endDate); - - useRealTimeDataForLatestDate(trendList, assetGroup, trendCategory, - ruleId, null); - - // ADD compliance_percent if not available . This is done - // temporarily.Will update with compliance_percent at source - - appendWithCompliancePercent(trendList); - - return segregateTrendProgressByWeek(assetGroup, trendList, - trendStartDate, endDate); - } else { - return new HashMap<>(); - } - } - - /* (non-Javadoc) - * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#useRealTimeDataForLatestDate(java.util.List, java.lang.String, java.lang.String, java.lang.String, java.lang.String) - */ - @Override - public void useRealTimeDataForLatestDate( - List> trendList, String ag, - String trendCategory, String ruleId, String domain) - throws ServiceException { - Map latestDaysTrendData = new HashMap<>( - trendList.get(trendList.size() - 1)); - Map baseApiReturnMap = new HashMap<>(); - Map overallCompliance = new HashMap<>(); - long compliantQuantity = 0; - long noncompliantQuantity = 0; - long total = 0; - double compliance; - LocalDate today; - try { - switch (trendCategory) { - case "tagcompliance": - baseApiReturnMap = complianceService.getTagging(ag, null); - compliantQuantity = baseApiReturnMap.get("tagged"); - noncompliantQuantity = baseApiReturnMap.get("untagged"); - total = baseApiReturnMap.get("assets"); - compliance = baseApiReturnMap.get(COMPLIANCE_PERCENTAGE); - - latestDaysTrendData.put(COMPLAINT, compliantQuantity); - latestDaysTrendData.put(NON_COMPLIANT, noncompliantQuantity); - latestDaysTrendData.put(TOTAL, total); - latestDaysTrendData.put(COMPLIANCE_PERCENT, compliance); - break; - - case "certcompliance": - - baseApiReturnMap = complianceService.getCertificates(ag); - total = baseApiReturnMap.get("certificates"); - noncompliantQuantity = baseApiReturnMap - .get("certificates_expiring"); - compliantQuantity = total - noncompliantQuantity; - - latestDaysTrendData.put(COMPLAINT, compliantQuantity); - latestDaysTrendData.put(NON_COMPLIANT, noncompliantQuantity); - latestDaysTrendData.put(TOTAL, total); - if (total > 0) { - compliance = Math - .floor(compliantQuantity * HUNDRED / total); - } else { - compliance = INT_HUNDRED; - } - latestDaysTrendData.put(COMPLIANCE_PERCENT, compliance); - break; - - case "vulncompliance": - Map vulnSummary = vulnService - .getVulnerabilitySummary(ag,SEVERITY_LEVELS); - total = Long.valueOf(vulnSummary.get("hosts").toString()); - noncompliantQuantity = Long.valueOf(vulnSummary.get( - "totalVulnerableAssets").toString()); - compliantQuantity = total - noncompliantQuantity; - - latestDaysTrendData.put(COMPLAINT, compliantQuantity); - latestDaysTrendData.put(NON_COMPLIANT, noncompliantQuantity); - latestDaysTrendData.put(TOTAL, total); - if (total > 0) { - compliance = Math - .floor(compliantQuantity * HUNDRED / total); - } else { - compliance = INT_HUNDRED; - } - latestDaysTrendData.put(COMPLIANCE_PERCENT, compliance); - break; - - case "issuecompliance": - Request request = new Request(); - request.setAg(ag); - Map filters = new HashMap<>(); - filters.put("ruleId.keyword", ruleId); - filters.put("domain", domain); - request.setFilter(filters); - ResponseWithOrder responseWithOrder = complianceService - .getRulecompliance(request); - latestDaysTrendData.put(COMPLAINT, responseWithOrder - .getResponse().get(0).get("passed")); - latestDaysTrendData.put(NON_COMPLIANT, responseWithOrder - .getResponse().get(0).get("failed")); - latestDaysTrendData.put(TOTAL, responseWithOrder.getResponse() - .get(0).get("assetsScanned")); - latestDaysTrendData.put(COMPLIANCE_PERCENT, responseWithOrder - .getResponse().get(0).get(COMPLIANCE_PERCENT)); - - break; - - case "patching": - baseApiReturnMap = complianceService.getPatching(ag, null); - compliantQuantity = baseApiReturnMap.get("patched_instances"); - noncompliantQuantity = baseApiReturnMap - .get("unpatched_instances"); - total = baseApiReturnMap.get("total_instances"); - compliance = baseApiReturnMap.get("patching_percentage"); - latestDaysTrendData.put("patched_instances", compliantQuantity); - latestDaysTrendData.put("unpatched_instances", - noncompliantQuantity); - latestDaysTrendData.put("total_instances", total); - latestDaysTrendData.put("patching_percentage", compliance); - break; - - case COMPLIANCE_PERCENTAGE: - overallCompliance = complianceService - .getOverallComplianceByDomain(ag, domain); - if(!overallCompliance.isEmpty()){ - for (Map.Entry entry : overallCompliance.entrySet()) { - latestDaysTrendData.put(entry.getKey(),entry.getValue()); - } - } - break; - - case "issues": - Map distroMap = complianceService - .getDistribution(ag, domain); - Map distroBySev = (Map) distroMap - .get("distribution_by_severity"); - latestDaysTrendData.put(TOTAL, distroMap.get("total_issues")); - - for (Map.Entry severity : distroBySev - .entrySet()) { - latestDaysTrendData.put(severity.getKey(), - severity.getValue()); - } - break; - default: - //nothings - } - - // Check if the trend already has todays data (Compare dates) - // If yes, overwrite. If not, add at the end. - LocalDate date = null; - today = LocalDate.now(); - date = LocalDate.parse(latestDaysTrendData.get("date").toString(), - DateTimeFormatter.ISO_LOCAL_DATE); - - if (date.isEqual(today)) { - logger.info("Latest days data available in trend data, so overwriting"); - trendList.set(trendList.size() - 1, latestDaysTrendData); - } else if (date.isEqual(today.minusDays(1))) { - // Ideally we need to consider this case only else, we may - // unnecessarily append wrong data. FOr eg. In case of patching - // if any previous/ progress is requested. - logger.info("Latest days data is NOT available in trend data, so adding at the end"); - latestDaysTrendData.put("date", - today.format(DateTimeFormatter.ISO_LOCAL_DATE)); - trendList.add(latestDaysTrendData); - } - - } catch (ServiceException e) { - logger.error("Call to Base API to get todays data failed" , e); - return; - } - - } - - /** - * Append with compliance percent. - * - * @param trendList the trend list - */ - private void appendWithCompliancePercent(List> trendList) { - - trendList.parallelStream().forEach( - trend -> { - if (trend.get(COMPLIANCE_PERCENT) == null) { - double total = Double.parseDouble(trend.get(TOTAL) - .toString()); - double compliant = Double.parseDouble(trend.get(COMPLAINT) - .toString()); - double compliancePercent = HUNDRED; - if (total > 0) { - compliancePercent = Math.floor(compliant * HUNDRED - / total); - } - trend.put(COMPLIANCE_PERCENT, compliancePercent); - } - }); - } - - /** - * Fill no data dates with previous. - * - * @param trendList the trend list - * @param firstDay the first day - * @param lastDay the last day - */ - private void fillNoDataDatesWithPrevious( - List> trendList, LocalDate firstDay, - LocalDate lastDay) { - - // We don't want data for future weeks. If the quarter being - // requested is the ongoing quarter, the max we we are interested - // is data up to and including the ongoing day in the ongoing week. - if (lastDay.isAfter(LocalDate.now())) { - lastDay = LocalDate.now(); - } - - List listOfAllDates = new ArrayList<>(); - - LocalDate iterationDate = firstDay; - - // Have a temp variable called iterationDate. Keep incrementing it by 1, - // until we reach the end date. In each such iteration, add each date to - // our list of dates - while (!iterationDate.isAfter(lastDay)) { - listOfAllDates.add(iterationDate); - iterationDate = iterationDate.plusDays(1); - } - - // Iterate through each date. If the data from ES is missing for any - // such - // date, add a dummy map with zero values - Map currentData = new LinkedHashMap<>(); - currentData.put(TOTAL, 0); - currentData.put(COMPLAINT, 0); - currentData.put(NON_COMPLIANT, 0); - currentData.put(COMPLIANCE_PERCENT, HUNDRED); - - for (int i = 0; i < listOfAllDates.size(); i++) { - LocalDate date = listOfAllDates.get(i); - Map trendInfo = getTrendDataForDate(trendList, date); - if (trendInfo == null) { - trendInfo = new LinkedHashMap<>(); - trendInfo.put("date", date.format(DateTimeFormatter.ISO_DATE)); - trendInfo.put(NON_COMPLIANT, currentData.get(NON_COMPLIANT)); - trendInfo.put(TOTAL, currentData.get(TOTAL)); - trendInfo.put(COMPLAINT, currentData.get(COMPLAINT)); - if (currentData.get(COMPLIANCE_PERCENT) != null) { - trendInfo.put(COMPLIANCE_PERCENT, - currentData.get(COMPLIANCE_PERCENT)); - } - trendList.add(i, trendInfo); - } else { - currentData = trendInfo; - } - } - - } - - /** - * Gets the trend data for date. - * - * @param trendList the trend list - * @param date the date - * @return the trend data for date - */ - private Map getTrendDataForDate( - List> trendList, LocalDate date) { - - List> match = trendList - .stream() - .filter(trendMap -> { - LocalDate dateInThisIteration = LocalDate - .parse(trendMap.get("date").toString(), - DateTimeFormatter.ISO_DATE); - return dateInThisIteration.isEqual(date); - }).collect(Collectors.toList()); - if (match != null && !match.isEmpty()) { - return match.get(0); - } - return null; - } - - /** - * Segregate trend progress by week. - * - * @param assetGroup the asset group - * @param trendProgressList the trend progress list - * @param startDate the start date - * @param endDate the end date - * @return the map - */ - private Map segregateTrendProgressByWeek(String assetGroup, - List> trendProgressList, LocalDate startDate, - LocalDate endDate) { - - long maxInstancesForTheCompleteDateRange = 0; - - long totalNumberRunningValue = 0; - long compliantRunningValue = 0; - long noncompliantRunningValue = 0; - double complianceRunningValue = 0; - - List> allWeeksDataList = new ArrayList<>(); - - // The first day of date range is taken as the first day of week 1 of - // the - // quarter. This - // could be a Monday, Thursday or ANY day. - LocalDate startingDayOfWeek = startDate; - - // Add 6 days to get the end date. If we start on a Thursday, the week - // ends on next Wednesday - LocalDate endingDayOfWeek = startingDayOfWeek.plusDays(SIX); - - List> trendListForTheWeek = new ArrayList<>(); - - // We will send 100 weeks at most. Start with week 1(There - // is no week zero!) - for (int weekNumber = 1; weekNumber <= HUNDRED; weekNumber++) { - - LocalDate startingDayOfWeekLocalCopy = startingDayOfWeek; - LocalDate endingDayOfWeekLocalCopy = endingDayOfWeek; - - trendProgressList - .forEach(ruleTrendProgressMap -> ruleTrendProgressMap.forEach(( - key, value) -> { - - if ("date".equals(key)) { - - // Check if this date falls in the week that we are - // currently interested in - LocalDate dateInThisIteration = LocalDate.parse( - value.toString(), - DateTimeFormatter.ISO_DATE); - if (dateInThisIteration - .isAfter(startingDayOfWeekLocalCopy - .minusDays(1)) - && (dateInThisIteration - .isBefore(endingDayOfWeekLocalCopy - .plusDays(1)))) { - // If the date matches, lets pick the map which - // represents this date's patching data and add - // it to - // the weeks list - trendListForTheWeek.add(ruleTrendProgressMap); - } - - } - - })); - - Map mapForTheWeek = new LinkedHashMap<>(); - - // First some k-v pairs for week number,week start date, week end - // date - mapForTheWeek.put("week", weekNumber); - mapForTheWeek.put("start_date", - startingDayOfWeek.format(DateTimeFormatter.ISO_DATE)); - mapForTheWeek.put("end_date", - endingDayOfWeek.format(DateTimeFormatter.ISO_DATE)); - - // Lets calculate the compliance for the week. We simply get the - // compliance for the last day of the week - - complianceRunningValue = calculateWeeklyCompliance(trendListForTheWeek); - mapForTheWeek.put(COMPLIANCE_PERCENTAGE, complianceRunningValue); - trendListForTheWeek.forEach(ruleTrendProgressMap -> { - // We don't need _id in the response - ruleTrendProgressMap.remove("_id"); - }); - - // Store a 'copy' of the weeks array list instead of the original, - // as we will clear the original and reuse it for the next - // iteration. Lets call this by the key 'compliance_info' - mapForTheWeek.put("compliance_info", - new ArrayList>(trendListForTheWeek)); - - if (!trendListForTheWeek.isEmpty()) { - allWeeksDataList.add(mapForTheWeek); - - totalNumberRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList( - TOTAL, trendListForTheWeek); - compliantRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList( - COMPLAINT, trendListForTheWeek); - noncompliantRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList( - NON_COMPLIANT, trendListForTheWeek); - - // Maintain a max instance number for the quarter that is being - // processed. - long maxInstancesRunningValue = (long) getMaxValueNumericDataFromAWeeklyDataList( - TOTAL, trendListForTheWeek); - if (maxInstancesRunningValue > maxInstancesForTheCompleteDateRange) { - maxInstancesForTheCompleteDateRange = maxInstancesRunningValue; - } - - } - - // Now, lets get ready for the iteration for next week - trendListForTheWeek.clear(); - startingDayOfWeek = startingDayOfWeek.plusDays(7); - endingDayOfWeek = endingDayOfWeek.plusDays(7); - - // If week ending date bypasses the quarter end date, lets rewind - // back to quarter end date. The quarter end date will be set as the - // week ending date. - } - - Map quarterlyDataMap = new LinkedHashMap<>(); - quarterlyDataMap.put("ag", assetGroup); - quarterlyDataMap.put("start_date", - startDate.format(DateTimeFormatter.ISO_DATE)); - quarterlyDataMap.put("end_date", - endDate.format(DateTimeFormatter.ISO_DATE)); - quarterlyDataMap.put("max", maxInstancesForTheCompleteDateRange); - quarterlyDataMap.put(TOTAL, totalNumberRunningValue); - quarterlyDataMap.put(COMPLAINT, compliantRunningValue); - quarterlyDataMap.put(NON_COMPLIANT, noncompliantRunningValue); - quarterlyDataMap.put(COMPLIANCE_PERCENTAGE, complianceRunningValue); - - quarterlyDataMap.put("compliance_trend", allWeeksDataList); - - return quarterlyDataMap; - - } - - /** - * Gets the latest days numeric data from A weekly data list. - * - * @param dataKeyName the data key name - * @param ruleTrendProgressListForTheWeek the rule trend progress list for the week - * @return the latest days numeric data from A weekly data list - */ - private double getLatestDaysNumericDataFromAWeeklyDataList( - String dataKeyName, - List> ruleTrendProgressListForTheWeek) { - - int index = ruleTrendProgressListForTheWeek.size() - 1; - - // We take the latest days data, provided its a non-zero value - while (index >= 0) { - Object obj = ruleTrendProgressListForTheWeek.get(index).get( - dataKeyName); - if (null != obj && Double.valueOf(obj.toString()) != 0) { - return Double.valueOf(obj.toString()); - } - index--; - } - - return 0; - } - - /** - * Gets the max value numeric data from A weekly data list. - * - * @param dataKeyName the data key name - * @param trendProgressListForTheWeek the trend progress list for the week - * @return the max value numeric data from A weekly data list - */ - private double getMaxValueNumericDataFromAWeeklyDataList( - String dataKeyName, - List> trendProgressListForTheWeek) { - - double maxValue = 0; - int index = trendProgressListForTheWeek.size() - 1; - - while (index >= 0) { - Object obj = trendProgressListForTheWeek.get(index) - .get(dataKeyName); - if (null != obj && Double.valueOf(obj.toString()) != 0 - && (Double.valueOf(obj.toString()) > maxValue)) { - maxValue = Double.valueOf(obj.toString()); - } - index--; - } - - return maxValue; - } - - /** - * Calculate weekly compliance. - * - * @param trendProgressListForTheWeek the trend progress list for the week - * @return the double - */ - private double calculateWeeklyCompliance( - List> trendProgressListForTheWeek) { - - int index = trendProgressListForTheWeek.size() - 1; - while (index >= 0) { - Object percentObj = trendProgressListForTheWeek.get(index).get( - COMPLIANCE_PERCENT); - if (null != percentObj - && Double.valueOf(percentObj.toString()) != 0) { - return Double.valueOf(percentObj.toString()); - } - index--; - } - return HUNDRED; - - } - - /* (non-Javadoc) - * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#getTrendIssues(java.lang.String, java.time.LocalDate, java.time.LocalDate, java.util.Map, java.lang.String) - */ - @Override - public Map getTrendIssues(String assetGroup, - LocalDate from, LocalDate to, Map filter, - String domain) throws ServiceException { - - Map parentMap = new HashMap<>(); - parentMap.put("ag", assetGroup); - - String ttypes = complianceRepository.getTargetTypeForAG(assetGroup, - filter.get(DOMAIN)); - List> ruleDetails = null; - if (!Strings.isNullOrEmpty(ttypes)) { - try{ - ruleDetails = complianceRepository - .getRuleIdWithDisplayNameQuery(ttypes); - }catch(DataException e){ - throw new ServiceException(e); - } - } - - Set ruleSev = new HashSet<>(); - List> ruleSevCatDetails; - ruleSevCatDetails = complianceService - .getRuleSevCatDetails(ruleDetails); - - Map ruleSevDetails = ruleSevCatDetails.parallelStream() - .collect( - Collectors.toMap(r -> r.get(RULEID).toString(), - r -> r.get(SEVERITY), - (oldvalue, newValue) -> newValue)); - ruleSevDetails.entrySet().parallelStream() - .forEach(entry -> ruleSev.add(entry.getValue().toString())); - - List> issueInfoList; - try { - issueInfoList = repository.getTrendIssues( - assetGroup, from, to, filter, ruleSev); - } catch (DataException e) { - throw new ServiceException(e); - } - if (!issueInfoList.isEmpty()) { - issueInfoList.parallelStream().forEach( - issuemap -> { - issuemap.remove("_id"); - double total = Double.parseDouble(issuemap.get(TOTAL) - .toString()); - if (total == 0) { - for (Map.Entry issue : issuemap - .entrySet()) { - if (!"date".equals(issue.getKey())) { - issuemap.put(issue.getKey(), 0); - } - } - } else { - - for (Map.Entry issue : issuemap - .entrySet()) { - if (issuemap.get(issue.getKey()) == null) - issuemap.put(issue.getKey(), 0); - } - } - - }); - // Sort the list by the date in ascending order - Comparator> comp = (m1, m2) -> LocalDate.parse( - m1.get("date").toString(), DateTimeFormatter.ISO_DATE) - .compareTo( - LocalDate.parse(m2.get("date").toString(), - DateTimeFormatter.ISO_DATE)); - - Collections.sort(issueInfoList, comp); - - useRealTimeDataForLatestDate(issueInfoList, assetGroup, "issues", - null, domain); - - parentMap.put("issues_info", issueInfoList); - } - return parentMap; - } - -} +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.compliance.service; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import com.google.common.base.Strings; +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.exception.ServiceException; +import com.tmobile.pacman.api.commons.repo.ElasticSearchRepository; +import com.tmobile.pacman.api.compliance.client.AuthServiceClient; +import com.tmobile.pacman.api.compliance.domain.Asset; +import com.tmobile.pacman.api.compliance.domain.Request; +import com.tmobile.pacman.api.compliance.domain.ResponseWithOrder; +import com.tmobile.pacman.api.compliance.repository.ComplianceRepository; +import com.tmobile.pacman.api.compliance.repository.TrendRepository; +/** + * The Class IssueTrendServiceImpl. + */ +@Service +public class IssueTrendServiceImpl implements IssueTrendService, Constants { + + /** The es host. */ + @Value("${elastic-search.host}") + private String esHost; + + /** The es port. */ + @Value("${elastic-search.port}") + private int esPort; + + /** The es cluster name. */ + @Value("${elastic-search.clusterName}") + private String esClusterName; + + /** The date format. */ + @Value("${formats.date}") + private String dateFormat; + + /** The logger. */ + private final Logger logger = LoggerFactory.getLogger(getClass()); + + /** The statistics client. */ + + /** The auth client. */ + @Autowired + private AuthServiceClient authClient; + + /** The repository. */ + @Autowired + private TrendRepository repository; + + /** The compliance service. */ + @Autowired + private ComplianceService complianceService; + + /** The elastic search repository. */ + @Autowired + private ElasticSearchRepository elasticSearchRepository; + + /** The compliance repository. */ + @Autowired + private ComplianceRepository complianceRepository; + + /** + * {@inheritDoc} + */ + public Asset finfindByName(String accountName) { + Assert.hasLength(accountName, "accountName cannot be null or empty"); + return null; + } + + /* (non-Javadoc) + * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#getTrendForIssues(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) + */ + @Override + public Map getTrendForIssues(String assetGroup, + String fromDate, String toDate, String severity, String ruleId, + String policyId, String app, String env) throws ServiceException { + Assert.hasLength(assetGroup, "assetGroup cannot be null or empty"); + + RangeGenerator generator = new RangeGenerator(); + Map mustNotFilter = new HashMap<>(); + Map mustFilter = new HashMap<>(); + if (!Strings.isNullOrEmpty(severity)) { + mustFilter.put("severity.keyword", severity); + } + + if (!Strings.isNullOrEmpty(policyId)) { + mustFilter.put("policyId.keyword", policyId); + } + + if (!Strings.isNullOrEmpty(ruleId)) { + mustFilter.put("ruleId.keyword", ruleId); + } + if (!Strings.isNullOrEmpty(app)) { + mustFilter.put("tags.Application.keyword", app); + } + + if (!Strings.isNullOrEmpty(env)) { + mustFilter.put("tags.Environment.keyword", env); + } + + List hosts = Arrays.asList(esHost, + ""); + // this has to move to config, this is just an additional end point, + // even if not provided default end point from config will work + + try { + return generator.generateTrend(esClusterName, hosts, + NINE_THOUSAND_THREE_HUNDRED, assetGroup, "issue", + "createdDate", "modifiedDate", mustNotFilter, mustFilter, + "yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + } catch (DataException e) { + + throw new ServiceException(e); + } + + } + + /* (non-Javadoc) + * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#getComplianceTrendProgress(java.lang.String, java.time.LocalDate, java.lang.String) + */ + @Override + public Map getComplianceTrendProgress(String assetGroup, + LocalDate fromDate, String domain) throws ServiceException { + Map parentMap = new HashMap<>(); + parentMap.put("ag", assetGroup); + // get list of targetypes mapped + String ttypes = complianceRepository.getTargetTypeForAG(assetGroup, + domain); + List> ruleDetails = null; + List> inputList; + List> complianceInfoList; + + if (!Strings.isNullOrEmpty(ttypes)) { + try { + ruleDetails = complianceRepository + .getRuleIdWithDisplayNameQuery(ttypes); + } catch (DataException e) { + throw new ServiceException(e); + } + } + // Make map of rule severity,category + Set ruleCat = new HashSet<>(); + List> ruleSevCatDetails; + + ruleSevCatDetails = complianceService + .getRuleSevCatDetails(ruleDetails); + + + + Map ruleCatDetails = ruleSevCatDetails.parallelStream() + .collect( + Collectors.toMap(c -> c.get(RULEID).toString(), + c -> c.get(RULE_CATEGORY), + (oldvalue, newValue) -> newValue)); + ruleCatDetails.entrySet().parallelStream() + .forEach(entry -> ruleCat.add(entry.getValue().toString())); + complianceInfoList = new ArrayList<>(); + + try { + inputList = repository + .getComplianceTrendProgress(assetGroup, fromDate, domain, + ruleCat); + } catch (DataException e) { + throw new ServiceException(e); + } + + if (!inputList.isEmpty()) { + // Sort the list by the date in ascending order + Comparator> comp = (m1, m2) -> LocalDate.parse( + m1.get("date").toString(), DateTimeFormatter.ISO_DATE) + .compareTo( + LocalDate.parse(m2.get("date").toString(), + DateTimeFormatter.ISO_DATE)); + + Collections.sort(inputList, comp); + useRealTimeDataForLatestDate(inputList, assetGroup, + COMPLIANCE_PERCENTAGE, null, domain); + inputList.forEach(inputMap -> { + Map outputMap = new HashMap<>(); + inputMap.forEach((key, value) -> { + // Other than the specified keys, ignore all other kv pairs + if ((!Strings.isNullOrEmpty(key)) + && !("_id".equalsIgnoreCase(key))) { + + outputMap.put(key, value); + } + }); + + complianceInfoList.add(outputMap); + }); + + Collections.sort(complianceInfoList, comp); + } + parentMap.put("compliance_info", complianceInfoList); + return parentMap; + } + + /* (non-Javadoc) + * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#getTrendProgress(java.lang.String, java.lang.String, java.time.LocalDate, java.time.LocalDate, java.lang.String) + */ + @Override + public Map getTrendProgress(String assetGroup, + String ruleId, LocalDate startDate, LocalDate endDate, + String trendCategory) throws ServiceException { + + List> trendList; + try{ + trendList = repository.getTrendProgress( + assetGroup, ruleId, startDate, endDate, trendCategory); + }catch(DataException e){ + throw new ServiceException(e); + } + if (!trendList.isEmpty()) { + + // Sort the list by the date in ascending order + Comparator> comp = (m1, m2) -> LocalDate.parse( + m1.get("date").toString(), DateTimeFormatter.ISO_DATE) + .compareTo( + LocalDate.parse(m2.get("date").toString(), + DateTimeFormatter.ISO_DATE)); + Collections.sort(trendList, comp); + LocalDate trendStartDate = LocalDate.parse(trendList.get(0) + .get("date").toString()); + + // Elastic Search might not have data for some dates. But we want to + // send consistent data to the consumers of this service, so we will + // populate previous where data is unavailable + fillNoDataDatesWithPrevious(trendList, trendStartDate, endDate); + + useRealTimeDataForLatestDate(trendList, assetGroup, trendCategory, + ruleId, null); + + // ADD compliance_percent if not available . This is done + // temporarily.Will update with compliance_percent at source + + appendWithCompliancePercent(trendList); + + return segregateTrendProgressByWeek(assetGroup, trendList, + trendStartDate, endDate); + } else { + return new HashMap<>(); + } + } + + /* (non-Javadoc) + * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#useRealTimeDataForLatestDate(java.util.List, java.lang.String, java.lang.String, java.lang.String, java.lang.String) + */ + @Override + public void useRealTimeDataForLatestDate( + List> trendList, String ag, + String trendCategory, String ruleId, String domain) + throws ServiceException { + Map latestDaysTrendData = new HashMap<>( + trendList.get(trendList.size() - 1)); + Map baseApiReturnMap = new HashMap<>(); + Map overallCompliance = new HashMap<>(); + long compliantQuantity = 0; + long noncompliantQuantity = 0; + long total = 0; + double compliance; + LocalDate today; + try { + switch (trendCategory) { + case "tagcompliance": + baseApiReturnMap = complianceService.getTagging(ag, null); + compliantQuantity = baseApiReturnMap.get("tagged"); + noncompliantQuantity = baseApiReturnMap.get("untagged"); + total = baseApiReturnMap.get("assets"); + compliance = baseApiReturnMap.get(COMPLIANCE_PERCENTAGE); + + latestDaysTrendData.put(COMPLAINT, compliantQuantity); + latestDaysTrendData.put(NON_COMPLIANT, noncompliantQuantity); + latestDaysTrendData.put(TOTAL, total); + latestDaysTrendData.put(COMPLIANCE_PERCENT, compliance); + break; + + case "certcompliance": + + baseApiReturnMap = complianceService.getCertificates(ag); + total = baseApiReturnMap.get("certificates"); + noncompliantQuantity = baseApiReturnMap + .get("certificates_expiring"); + compliantQuantity = total - noncompliantQuantity; + + latestDaysTrendData.put(COMPLAINT, compliantQuantity); + latestDaysTrendData.put(NON_COMPLIANT, noncompliantQuantity); + latestDaysTrendData.put(TOTAL, total); + if (total > 0) { + compliance = Math + .floor(compliantQuantity * HUNDRED / total); + } else { + compliance = INT_HUNDRED; + } + latestDaysTrendData.put(COMPLIANCE_PERCENT, compliance); + break; + + case "issuecompliance": + Request request = new Request(); + request.setAg(ag); + Map filters = new HashMap<>(); + filters.put("ruleId.keyword", ruleId); + filters.put("domain", domain); + request.setFilter(filters); + ResponseWithOrder responseWithOrder = complianceService + .getRulecompliance(request); + latestDaysTrendData.put(COMPLAINT, responseWithOrder + .getResponse().get(0).get("passed")); + latestDaysTrendData.put(NON_COMPLIANT, responseWithOrder + .getResponse().get(0).get("failed")); + latestDaysTrendData.put(TOTAL, responseWithOrder.getResponse() + .get(0).get("assetsScanned")); + latestDaysTrendData.put(COMPLIANCE_PERCENT, responseWithOrder + .getResponse().get(0).get(COMPLIANCE_PERCENT)); + + break; + + case "patching": + baseApiReturnMap = complianceService.getPatching(ag, null); + compliantQuantity = baseApiReturnMap.get("patched_instances"); + noncompliantQuantity = baseApiReturnMap + .get("unpatched_instances"); + total = baseApiReturnMap.get("total_instances"); + compliance = baseApiReturnMap.get("patching_percentage"); + latestDaysTrendData.put("patched_instances", compliantQuantity); + latestDaysTrendData.put("unpatched_instances", + noncompliantQuantity); + latestDaysTrendData.put("total_instances", total); + latestDaysTrendData.put("patching_percentage", compliance); + break; + + case COMPLIANCE_PERCENTAGE: + overallCompliance = complianceService + .getOverallComplianceByDomain(ag, domain); + if(!overallCompliance.isEmpty()){ + for (Map.Entry entry : overallCompliance.entrySet()) { + latestDaysTrendData.put(entry.getKey(),entry.getValue()); + } + } + break; + + case "issues": + Map distroMap = complianceService + .getDistribution(ag, domain); + Map distroBySev = (Map) distroMap + .get("distribution_by_severity"); + latestDaysTrendData.put(TOTAL, distroMap.get("total_issues")); + + for (Map.Entry severity : distroBySev + .entrySet()) { + latestDaysTrendData.put(severity.getKey(), + severity.getValue()); + } + break; + default: + //nothings + } + + // Check if the trend already has todays data (Compare dates) + // If yes, overwrite. If not, add at the end. + LocalDate date = null; + today = LocalDate.now(); + date = LocalDate.parse(latestDaysTrendData.get("date").toString(), + DateTimeFormatter.ISO_LOCAL_DATE); + + if (date.isEqual(today)) { + logger.info("Latest days data available in trend data, so overwriting"); + trendList.set(trendList.size() - 1, latestDaysTrendData); + } else if (date.isEqual(today.minusDays(1))) { + // Ideally we need to consider this case only else, we may + // unnecessarily append wrong data. FOr eg. In case of patching + // if any previous/ progress is requested. + logger.info("Latest days data is NOT available in trend data, so adding at the end"); + latestDaysTrendData.put("date", + today.format(DateTimeFormatter.ISO_LOCAL_DATE)); + trendList.add(latestDaysTrendData); + } + + } catch (ServiceException e) { + logger.error("Call to Base API to get todays data failed" , e); + return; + } + + } + + /** + * Append with compliance percent. + * + * @param trendList the trend list + */ + private void appendWithCompliancePercent(List> trendList) { + + trendList.parallelStream().forEach( + trend -> { + if (trend.get(COMPLIANCE_PERCENT) == null) { + double total = Double.parseDouble(trend.get(TOTAL) + .toString()); + double compliant = Double.parseDouble(trend.get(COMPLAINT) + .toString()); + double compliancePercent = HUNDRED; + if (total > 0) { + compliancePercent = Math.floor(compliant * HUNDRED + / total); + } + trend.put(COMPLIANCE_PERCENT, compliancePercent); + } + }); + } + + /** + * Fill no data dates with previous. + * + * @param trendList the trend list + * @param firstDay the first day + * @param lastDay the last day + */ + private void fillNoDataDatesWithPrevious( + List> trendList, LocalDate firstDay, + LocalDate lastDay) { + + // We don't want data for future weeks. If the quarter being + // requested is the ongoing quarter, the max we we are interested + // is data up to and including the ongoing day in the ongoing week. + if (lastDay.isAfter(LocalDate.now())) { + lastDay = LocalDate.now(); + } + + List listOfAllDates = new ArrayList<>(); + + LocalDate iterationDate = firstDay; + + // Have a temp variable called iterationDate. Keep incrementing it by 1, + // until we reach the end date. In each such iteration, add each date to + // our list of dates + while (!iterationDate.isAfter(lastDay)) { + listOfAllDates.add(iterationDate); + iterationDate = iterationDate.plusDays(1); + } + + // Iterate through each date. If the data from ES is missing for any + // such + // date, add a dummy map with zero values + Map currentData = new LinkedHashMap<>(); + currentData.put(TOTAL, 0); + currentData.put(COMPLAINT, 0); + currentData.put(NON_COMPLIANT, 0); + currentData.put(COMPLIANCE_PERCENT, HUNDRED); + + for (int i = 0; i < listOfAllDates.size(); i++) { + LocalDate date = listOfAllDates.get(i); + Map trendInfo = getTrendDataForDate(trendList, date); + if (trendInfo == null) { + trendInfo = new LinkedHashMap<>(); + trendInfo.put("date", date.format(DateTimeFormatter.ISO_DATE)); + trendInfo.put(NON_COMPLIANT, currentData.get(NON_COMPLIANT)); + trendInfo.put(TOTAL, currentData.get(TOTAL)); + trendInfo.put(COMPLAINT, currentData.get(COMPLAINT)); + if (currentData.get(COMPLIANCE_PERCENT) != null) { + trendInfo.put(COMPLIANCE_PERCENT, + currentData.get(COMPLIANCE_PERCENT)); + } + trendList.add(i, trendInfo); + } else { + currentData = trendInfo; + } + } + + } + + /** + * Gets the trend data for date. + * + * @param trendList the trend list + * @param date the date + * @return the trend data for date + */ + private Map getTrendDataForDate( + List> trendList, LocalDate date) { + + List> match = trendList + .stream() + .filter(trendMap -> { + LocalDate dateInThisIteration = LocalDate + .parse(trendMap.get("date").toString(), + DateTimeFormatter.ISO_DATE); + return dateInThisIteration.isEqual(date); + }).collect(Collectors.toList()); + if (match != null && !match.isEmpty()) { + return match.get(0); + } + return null; + } + + /** + * Segregate trend progress by week. + * + * @param assetGroup the asset group + * @param trendProgressList the trend progress list + * @param startDate the start date + * @param endDate the end date + * @return the map + */ + private Map segregateTrendProgressByWeek(String assetGroup, + List> trendProgressList, LocalDate startDate, + LocalDate endDate) { + + long maxInstancesForTheCompleteDateRange = 0; + + long totalNumberRunningValue = 0; + long compliantRunningValue = 0; + long noncompliantRunningValue = 0; + double complianceRunningValue = 0; + + List> allWeeksDataList = new ArrayList<>(); + + // The first day of date range is taken as the first day of week 1 of + // the + // quarter. This + // could be a Monday, Thursday or ANY day. + LocalDate startingDayOfWeek = startDate; + + // Add 6 days to get the end date. If we start on a Thursday, the week + // ends on next Wednesday + LocalDate endingDayOfWeek = startingDayOfWeek.plusDays(SIX); + + List> trendListForTheWeek = new ArrayList<>(); + + // We will send 100 weeks at most. Start with week 1(There + // is no week zero!) + for (int weekNumber = 1; weekNumber <= HUNDRED; weekNumber++) { + + LocalDate startingDayOfWeekLocalCopy = startingDayOfWeek; + LocalDate endingDayOfWeekLocalCopy = endingDayOfWeek; + + trendProgressList + .forEach(ruleTrendProgressMap -> ruleTrendProgressMap.forEach(( + key, value) -> { + + if ("date".equals(key)) { + + // Check if this date falls in the week that we are + // currently interested in + LocalDate dateInThisIteration = LocalDate.parse( + value.toString(), + DateTimeFormatter.ISO_DATE); + if (dateInThisIteration + .isAfter(startingDayOfWeekLocalCopy + .minusDays(1)) + && (dateInThisIteration + .isBefore(endingDayOfWeekLocalCopy + .plusDays(1)))) { + // If the date matches, lets pick the map which + // represents this date's patching data and add + // it to + // the weeks list + trendListForTheWeek.add(ruleTrendProgressMap); + } + + } + + })); + + Map mapForTheWeek = new LinkedHashMap<>(); + + // First some k-v pairs for week number,week start date, week end + // date + mapForTheWeek.put("week", weekNumber); + mapForTheWeek.put("start_date", + startingDayOfWeek.format(DateTimeFormatter.ISO_DATE)); + mapForTheWeek.put("end_date", + endingDayOfWeek.format(DateTimeFormatter.ISO_DATE)); + + // Lets calculate the compliance for the week. We simply get the + // compliance for the last day of the week + + complianceRunningValue = calculateWeeklyCompliance(trendListForTheWeek); + mapForTheWeek.put(COMPLIANCE_PERCENTAGE, complianceRunningValue); + trendListForTheWeek.forEach(ruleTrendProgressMap -> { + // We don't need _id in the response + ruleTrendProgressMap.remove("_id"); + }); + + // Store a 'copy' of the weeks array list instead of the original, + // as we will clear the original and reuse it for the next + // iteration. Lets call this by the key 'compliance_info' + mapForTheWeek.put("compliance_info", + new ArrayList>(trendListForTheWeek)); + + if (!trendListForTheWeek.isEmpty()) { + allWeeksDataList.add(mapForTheWeek); + + totalNumberRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList( + TOTAL, trendListForTheWeek); + compliantRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList( + COMPLAINT, trendListForTheWeek); + noncompliantRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList( + NON_COMPLIANT, trendListForTheWeek); + + // Maintain a max instance number for the quarter that is being + // processed. + long maxInstancesRunningValue = (long) getMaxValueNumericDataFromAWeeklyDataList( + TOTAL, trendListForTheWeek); + if (maxInstancesRunningValue > maxInstancesForTheCompleteDateRange) { + maxInstancesForTheCompleteDateRange = maxInstancesRunningValue; + } + + } + + // Now, lets get ready for the iteration for next week + trendListForTheWeek.clear(); + startingDayOfWeek = startingDayOfWeek.plusDays(7); + endingDayOfWeek = endingDayOfWeek.plusDays(7); + + // If week ending date bypasses the quarter end date, lets rewind + // back to quarter end date. The quarter end date will be set as the + // week ending date. + } + + Map quarterlyDataMap = new LinkedHashMap<>(); + quarterlyDataMap.put("ag", assetGroup); + quarterlyDataMap.put("start_date", + startDate.format(DateTimeFormatter.ISO_DATE)); + quarterlyDataMap.put("end_date", + endDate.format(DateTimeFormatter.ISO_DATE)); + quarterlyDataMap.put("max", maxInstancesForTheCompleteDateRange); + quarterlyDataMap.put(TOTAL, totalNumberRunningValue); + quarterlyDataMap.put(COMPLAINT, compliantRunningValue); + quarterlyDataMap.put(NON_COMPLIANT, noncompliantRunningValue); + quarterlyDataMap.put(COMPLIANCE_PERCENTAGE, complianceRunningValue); + + quarterlyDataMap.put("compliance_trend", allWeeksDataList); + + return quarterlyDataMap; + + } + + /** + * Gets the latest days numeric data from A weekly data list. + * + * @param dataKeyName the data key name + * @param ruleTrendProgressListForTheWeek the rule trend progress list for the week + * @return the latest days numeric data from A weekly data list + */ + private double getLatestDaysNumericDataFromAWeeklyDataList( + String dataKeyName, + List> ruleTrendProgressListForTheWeek) { + + int index = ruleTrendProgressListForTheWeek.size() - 1; + + // We take the latest days data, provided its a non-zero value + while (index >= 0) { + Object obj = ruleTrendProgressListForTheWeek.get(index).get( + dataKeyName); + if (null != obj && Double.valueOf(obj.toString()) != 0) { + return Double.valueOf(obj.toString()); + } + index--; + } + + return 0; + } + + /** + * Gets the max value numeric data from A weekly data list. + * + * @param dataKeyName the data key name + * @param trendProgressListForTheWeek the trend progress list for the week + * @return the max value numeric data from A weekly data list + */ + private double getMaxValueNumericDataFromAWeeklyDataList( + String dataKeyName, + List> trendProgressListForTheWeek) { + + double maxValue = 0; + int index = trendProgressListForTheWeek.size() - 1; + + while (index >= 0) { + Object obj = trendProgressListForTheWeek.get(index) + .get(dataKeyName); + if (null != obj && Double.valueOf(obj.toString()) != 0 + && (Double.valueOf(obj.toString()) > maxValue)) { + maxValue = Double.valueOf(obj.toString()); + } + index--; + } + + return maxValue; + } + + /** + * Calculate weekly compliance. + * + * @param trendProgressListForTheWeek the trend progress list for the week + * @return the double + */ + private double calculateWeeklyCompliance( + List> trendProgressListForTheWeek) { + + int index = trendProgressListForTheWeek.size() - 1; + while (index >= 0) { + Object percentObj = trendProgressListForTheWeek.get(index).get( + COMPLIANCE_PERCENT); + if (null != percentObj + && Double.valueOf(percentObj.toString()) != 0) { + return Double.valueOf(percentObj.toString()); + } + index--; + } + return HUNDRED; + + } + + /* (non-Javadoc) + * @see com.tmobile.pacman.api.compliance.service.IssueTrendService#getTrendIssues(java.lang.String, java.time.LocalDate, java.time.LocalDate, java.util.Map, java.lang.String) + */ + @Override + public Map getTrendIssues(String assetGroup, + LocalDate from, LocalDate to, Map filter, + String domain) throws ServiceException { + + Map parentMap = new HashMap<>(); + parentMap.put("ag", assetGroup); + + String ttypes = complianceRepository.getTargetTypeForAG(assetGroup, + filter.get(DOMAIN)); + List> ruleDetails = null; + if (!Strings.isNullOrEmpty(ttypes)) { + try{ + ruleDetails = complianceRepository + .getRuleIdWithDisplayNameQuery(ttypes); + }catch(DataException e){ + throw new ServiceException(e); + } + } + + Set ruleSev = new HashSet<>(); + List> ruleSevCatDetails; + ruleSevCatDetails = complianceService + .getRuleSevCatDetails(ruleDetails); + + Map ruleSevDetails = ruleSevCatDetails.parallelStream() + .collect( + Collectors.toMap(r -> r.get(RULEID).toString(), + r -> r.get(SEVERITY), + (oldvalue, newValue) -> newValue)); + ruleSevDetails.entrySet().parallelStream() + .forEach(entry -> ruleSev.add(entry.getValue().toString())); + + List> issueInfoList; + try { + issueInfoList = repository.getTrendIssues( + assetGroup, from, to, filter, ruleSev); + } catch (DataException e) { + throw new ServiceException(e); + } + if (!issueInfoList.isEmpty()) { + issueInfoList.parallelStream().forEach( + issuemap -> { + issuemap.remove("_id"); + double total = Double.parseDouble(issuemap.get(TOTAL) + .toString()); + if (total == 0) { + for (Map.Entry issue : issuemap + .entrySet()) { + if (!"date".equals(issue.getKey())) { + issuemap.put(issue.getKey(), 0); + } + } + } else { + + for (Map.Entry issue : issuemap + .entrySet()) { + if (issuemap.get(issue.getKey()) == null) + issuemap.put(issue.getKey(), 0); + } + } + + }); + // Sort the list by the date in ascending order + Comparator> comp = (m1, m2) -> LocalDate.parse( + m1.get("date").toString(), DateTimeFormatter.ISO_DATE) + .compareTo( + LocalDate.parse(m2.get("date").toString(), + DateTimeFormatter.ISO_DATE)); + + Collections.sort(issueInfoList, comp); + + useRealTimeDataForLatestDate(issueInfoList, assetGroup, "issues", + null, domain); + + parentMap.put("issues_info", issueInfoList); + } + return parentMap; + } + +} diff --git a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/service/VulnerabilityService.java b/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/service/VulnerabilityService.java deleted file mode 100644 index 4dd80dfc1..000000000 --- a/api/pacman-api-compliance/src/main/java/com/tmobile/pacman/api/compliance/service/VulnerabilityService.java +++ /dev/null @@ -1,1895 +0,0 @@ -/******************************************************************************* - * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - ******************************************************************************/ -package com.tmobile.pacman.api.compliance.service; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Strings; -import com.tmobile.pacman.api.commons.Constants; -import com.tmobile.pacman.api.commons.exception.DataException; -import com.tmobile.pacman.api.commons.exception.ServiceException; -import com.tmobile.pacman.api.commons.utils.CommonUtils; -import com.tmobile.pacman.api.compliance.client.AssetServiceClient; -import com.tmobile.pacman.api.compliance.domain.AssetApi; -import com.tmobile.pacman.api.compliance.domain.AssetApiData; -import com.tmobile.pacman.api.compliance.domain.AssetCount; -import com.tmobile.pacman.api.compliance.domain.AssetCountByAppEnvDTO; -import com.tmobile.pacman.api.compliance.domain.AssetCountDTO; -import com.tmobile.pacman.api.compliance.domain.AssetCountData; -import com.tmobile.pacman.api.compliance.domain.Request; -import com.tmobile.pacman.api.compliance.domain.TrendNote; -import com.tmobile.pacman.api.compliance.repository.VulnerabilityRepository; -import com.tmobile.pacman.api.compliance.repository.VulnerabilityTrendGenerator; - -/** - * The Class VulnerabilityService. - */ -@Service -public class VulnerabilityService implements Constants { - - /** The vulnerability repository. */ - @Autowired - private VulnerabilityRepository vulnerabilityRepository; - - /** The vuln trend generator. */ - @Autowired - VulnerabilityTrendGenerator vulnTrendGenerator; - - @Autowired - ComplianceService complianceService; - - /** The vuln types. */ - @Value("${vulnerability.types}") - private String vulnTypes; - - /** The vuln summary severity. */ - @Value("${vulnerability.summary.severity}") - private String vulnSummarySeverity; - - /** The asset service client. */ - @Autowired - private AssetServiceClient assetServiceClient; - - /** The Constant logger. */ - private static final Log logger = LogFactory.getLog(VulnerabilityService.class); - - - - /** - * Gets the vulnerabilities details. - * - * @param assetGroup the asset group - * @param filter the filter - * @return the vulnerabilities details - * @throws Exception the exception - */ - public List> getVulnerabilitiesDetails( - String assetGroup, Map filter) throws Exception { - List> vulnerabilitiesDetails = new ArrayList<>(); - try { - List vulnTargetTypes = getVulnTargetTypes(assetGroup); - if (!vulnTargetTypes.isEmpty()) { - for (String parentType : vulnTargetTypes) { - Map vulnAssetsAffected = vulnerabilityRepository - .getAssetsAffectedCount(assetGroup, filter, - parentType); - List> vulnerabilitiesData = formVulnerabilitiesData( - vulnerabilityRepository.getAllVulnerabilities(new ArrayList( - vulnAssetsAffected.keySet())), vulnAssetsAffected); - List> vulnerabilitiesDetailsTemp = new ArrayList<>(); - if(vulnerabilitiesDetails.isEmpty()) { - vulnerabilitiesDetails = new ArrayList<>(vulnerabilitiesData); - } else { - for(Map vulnData : vulnerabilitiesData) { - boolean qidMatched = false; - for(Map vulnDetail : vulnerabilitiesDetails) { - if(vulnData.get("qid").equals(vulnDetail.get("qid"))) { - vulnDetail.put(ASSETS_AFFECTED, Long.valueOf(vulnDetail.get(ASSETS_AFFECTED).toString()) - + Long.valueOf(vulnData.get(ASSETS_AFFECTED).toString())); - qidMatched = true; - break; - } - } - if(!qidMatched) { - vulnerabilitiesDetailsTemp.add(vulnData); - } - } - vulnerabilitiesDetails.addAll(vulnerabilitiesDetailsTemp); - } - } - } - - } catch (Exception e) { - logger.error("Error in getVulnerabilitiesDetails ", e); - throw e; - } - - return vulnerabilitiesDetails; - } - - /** - * Gets the vulnerability summary. - * - * @param assetGroup the asset group - * @return the vulnerability summary - * @throws ServiceException the service exception - */ - @SuppressWarnings({ "unchecked" }) - public Map getVulnerabilitySummary(String assetGroup,String reqSeverity) - throws ServiceException { - - List vulnTargetTypes = getVulnTargetTypes(assetGroup); - - Map vulnerabilitySummary = new HashMap<>(); - List> severityInfo = new ArrayList<>(); - if (vulnTargetTypes.isEmpty()) { - for (int i = 3; i <= 5; i++) { - Map sevInfo = new HashMap<>(); - sevInfo.put(SEVERITY, "S" + i); - sevInfo.put(SEVEITY_LEVEL, i); - sevInfo.put(COUNT, 0); - sevInfo.put("appCount", 0); - sevInfo.put("hostCount", 0); - sevInfo.put(UNIQUE_VULN_COUNT, 0); - sevInfo.put(VULN_COUNT, 0); - severityInfo.add(sevInfo); - } - vulnerabilitySummary.put(SEV_INFO, severityInfo); - vulnerabilitySummary.put(VULNEREBILITIES, 0); - vulnerabilitySummary.put("hosts", 0); - vulnerabilitySummary.put(TOTAL_VULN_ASSETS, 0); - vulnerabilitySummary.put("compliantpercent", 100); - vulnerabilitySummary.put("assetsWithVulns", 0); - return vulnerabilitySummary; - - } else { - - Map> queryResults = new HashMap<>(); - - long totalQualysCount = 0; - long vulnerableAssetCount = 0; - long totalAssetCount = 0; - - ExecutorService executor = Executors.newFixedThreadPool(3); - executor.execute(() -> { - queryResults.put("uniqueHost",vulnerabilityRepository.getUniqueHost(assetGroup,reqSeverity)); - }); - executor.execute(() -> { - queryResults.put("VulnInfo",vulnerabilityRepository.getVulnInfo(assetGroup,reqSeverity)); - - }); - executor.execute(() -> { - queryResults.put("uniqueApp",vulnerabilityRepository.getUniqueApp(assetGroup)); - }); - executor.shutdown(); - while (!executor.isTerminated()) { - } - - Map uniqueHost = queryResults.get("uniqueHost"); - Map vulnInfo = queryResults.get("VulnInfo"); - Map uniqueApp = queryResults.get("uniqueApp"); - - List sevList = Arrays.asList(reqSeverity.split(",")); - List summarySevList = Arrays.asList(vulnSummarySeverity.split(",")); - Map sevVulnInfo ; - Map vulnInfoMap ; - - List> sevVulnfoList = new ArrayList<>(); - vulnerabilitySummary.put(SEV_INFO, sevVulnfoList); - - for(String sev : sevList){ - sevVulnInfo = new HashMap<>(); - sevVulnInfo.put(SEVEITY_LEVEL, Integer.valueOf(sev)); - sevVulnInfo.put(SEVERITY, "S"+sev); - sevVulnInfo.put("hostCount", uniqueHost.get(sev)==null?0:uniqueHost.get(sev)); - if(summarySevList.contains(sev)){ - vulnerableAssetCount += Long.valueOf(sevVulnInfo.get("hostCount").toString()); - } - vulnInfoMap = (Map) vulnInfo.get(sev); - if(vulnInfoMap!=null){ - sevVulnInfo.put(COUNT,vulnInfoMap.get(VULN_COUNT)); - sevVulnInfo.put(VULN_COUNT,vulnInfoMap.get(VULN_COUNT)); - sevVulnInfo.put(UNIQUE_VULN_COUNT,vulnInfoMap.get(UNIQUE_VULN_COUNT)); - }else{ - sevVulnInfo.put(COUNT,0); - sevVulnInfo.put(VULN_COUNT,0); - sevVulnInfo.put(UNIQUE_VULN_COUNT,0); - } - sevVulnInfo.put("appCount", uniqueApp.get(sev)==null?0:uniqueApp.get(sev)); - sevVulnfoList.add(sevVulnInfo); - } - vulnerabilitySummary.put(UNIQUE_VULN_COUNT, sevVulnfoList.stream().mapToLong(sevData-> Long.valueOf(sevData.get(UNIQUE_VULN_COUNT).toString())).sum()); - vulnerabilitySummary.put("assetsWithVulns", uniqueHost.get(TOTAL)==null?0:uniqueHost.get(TOTAL)); - vulnerabilitySummary.put( VULNEREBILITIES,vulnInfo.get(TOTAL)==null?0:vulnInfo.get(TOTAL)); - - if(sevList.stream().filter(summarySevList::contains).count() != summarySevList.size()){ - vulnerableAssetCount = Long.valueOf(vulnerabilityRepository.getUniqueHost(assetGroup,vulnSummarySeverity).get(TOTAL).toString()); - } - - for (String vulnType : vulnTargetTypes) { - try{ - if(vulnType.equals(EC2)) { - Request request = new Request(); - request.setAg(assetGroup); - Map filter = new HashMap<>(); - filter.put("domain", "Infra & Platforms"); - filter.put("ruleId.keyword","PacMan_Ec2InstanceScannedByQualys_version-1_Ec2-instance-scanned-by-qualys-API_ec2"); - request.setFilter(filter ); - Map response = complianceService.getRulecompliance(request).getResponse().get(0); - totalAssetCount += Long.valueOf(response.get("assetsScanned").toString()); - totalQualysCount += Long.valueOf(response.get("passed").toString()); - } else { - AssetCount totalAssets = assetServiceClient - .getTotalAssetsCount(assetGroup, vulnType, null); - AssetCountData data = totalAssets.getData(); - AssetCountByAppEnvDTO[] assetcount = data.getAssetcount(); - Long totalAssetsCount = 0l; - for (AssetCountByAppEnvDTO assetCount_Count : assetcount) { - if (assetCount_Count.getType().equalsIgnoreCase(vulnType)) { - totalAssetsCount = Long.parseLong(assetCount_Count - .getCount()); - } - } - totalAssetCount += totalAssetsCount; - totalQualysCount += vulnerabilityRepository.getTotalQualysHostCount(assetGroup, vulnType); - } - }catch(ServiceException | DataException e){ - throw new ServiceException(); - } - } - - try { - - vulnerabilitySummary.put("hosts", totalAssetCount); - if (totalQualysCount > totalAssetCount) { - totalQualysCount = totalAssetCount; - } - - long totalVulnerableAssets = totalAssetCount - totalQualysCount - + vulnerableAssetCount; - if (totalVulnerableAssets > totalAssetCount) { - totalVulnerableAssets = totalAssetCount; - } - vulnerabilitySummary.put(TOTAL_VULN_ASSETS, - totalVulnerableAssets); - - float compliantCount = (float)totalAssetCount - totalVulnerableAssets; - float compliantpercent = 100; - if (totalAssetCount > 0) { - compliantpercent = (compliantCount / totalAssetCount) * 100; - } - DecimalFormat df = new DecimalFormat("#.00"); - vulnerabilitySummary.put("compliantpercent", - Math.floor(Float.valueOf(df.format(compliantpercent)))); - vulnerabilitySummary.put("hostsScanned", totalQualysCount); - vulnerabilitySummary.put("hostsNotScanned", totalAssetCount-totalQualysCount); - if(totalAssetCount > 0) { - vulnerabilitySummary.put("hostsScanCoverage", Math.floor( (1.0*totalQualysCount/totalAssetCount)*100)); - } else { - vulnerabilitySummary.put("hostsScanCoverage", 0); - } - } catch (Exception e) { - logger.error(e); - throw new ServiceException(e); - } - if(vulnerabilitySummary.isEmpty()){ - throw new ServiceException(NO_DATA_FOUND); - } - return vulnerabilitySummary; - } - - } - - /** - * Gets the vulnerability by app and env. - * - * @param assetGroup the asset group - * @param filter the filter - * @param application the application - * @return the vulnerability by app and env - * @throws Exception the exception - */ - public List> getVulnerabilityByAppAndEnv( - String assetGroup, String filter, String application) - throws Exception { - - List> vulnApplications = new ArrayList<>(); - List vulnTargetTypes = getVulnTargetTypes(assetGroup); - - if (!vulnTargetTypes.isEmpty()) { - for (String parentType : vulnTargetTypes) { - vulnApplications.addAll(vulnerabilityRepository - .getVulnerabilyAcrossAppAndEnv(assetGroup, filter, - application, parentType, null)); - } - } - - return vulnApplications; - } - - /** - * Gets the vulnerability trend. - * - * @param assetGroup the asset group - * @param filter the filter - * @param from the from - * @param to the to - * @return the vulnerability trend - * @throws Exception the exception - */ - public List> getVulnerabilityTrend(String assetGroup, - Map filter, Date from, Date to) throws Exception { - return vulnerabilityRepository.getVulnerabilityTrend(assetGroup, - filter, from, to); - } - - /** - * Gets the vulnerability trend. - * - * @param assetGroup the asset group - * @param severity the severity - * @param from the from - * @return the vulnerability trend with open new count - * @throws Exception the exception - */ - public List> getVulnerabilityNewOpenTrend(String assetGroup, - String severity, Date from) throws Exception { - return vulnTrendGenerator.generateTrend(assetGroup, - severity,from); - } - - - /** - * Gets the vulnerabilities distribution. - * - * @param assetGroup the asset group - * @return the vulnerabilities distribution - * @throws Exception the exception - */ - public List> getVulnerabilitiesDistribution( - String assetGroup) throws Exception { - - List> vulnDistributions = new ArrayList<>(); - List vulnTargetTypes = getVulnTargetTypes(assetGroup); - if (!vulnTargetTypes.isEmpty()) { - for (String parentType : vulnTargetTypes) { - vulnDistributions - .addAll(vulnerabilityRepository - .getVulnerabilitiesDistribution(assetGroup, - parentType)); - } - } - return vulnDistributions; - } - - /** - * Filter matching collection elements. - * - * @param masterDetailList the master detail list - * @param searchText the search text - * @param b the b - * @return the object - * @throws ServiceException the ServiceException - */ - public Object filterMatchingCollectionElements( - List> masterDetailList, String searchText, - boolean b) throws ServiceException { - return CommonUtils.filterMatchingCollectionElements(masterDetailList, - searchText, true); - } - - /** - * Gets the vulnerabilitysummary by resource id. - * - * @param instanceId the instance id - * @return the vulnerabilitysummary by resource id - */ - public Map getVulnerabilitysummaryByResourceId( - String instanceId) { - return vulnerabilityRepository - .getVulnerabilitysummaryByResourceId(instanceId); - } - - /** - * Gets the vulnerability details by resource id. - * - * @param instanceId the instance id - * @return the vulnerability details by resource id - */ - public List> getVulnerabilityDetailsByResourceId( - String instanceId) { - - List> vulnerabilitiesDetails = new ArrayList<>(); - try { - List> vulnerabilitiesDetailsList = vulnerabilityRepository - .getVulnerabilityDetailsByResourceId(instanceId); - vulnerabilitiesDetails = formVulnerabilitiesData( - vulnerabilitiesDetailsList, new HashMap()); - } catch (Exception e) { - logger.error("Error in getVulnerabilitiesDetails ", e); - throw e; - } - - return vulnerabilitiesDetails; - } - - /** - * Form vulnerabilities data. - * - * @param vulnerabilitiesDetails the vulnerabilities details - * @param vulnAssetsAffected the vuln assets affected - * @return the list - */ - private List> formVulnerabilitiesData( - List> vulnerabilitiesDetails, - Map vulnAssetsAffected) { - - List> vulnerabilitiesDetailsList = new ArrayList<>(); - - vulnerabilitiesDetails - .parallelStream() - .forEach( - vulnObject -> { - Map vulnObj = new LinkedHashMap<>(); - vulnObj.put(TITLE, vulnObject.get(TITLE).toString()); - vulnObj.put( - SEVERITY, - "S" - + Double.valueOf( - vulnObject.get( - SEVEITY_LEVEL) - .toString()) - .intValue()); - if (!CollectionUtils.isEmpty(vulnAssetsAffected)) { - vulnObj.put(ASSETS_AFFECTED, vulnAssetsAffected - .get(String.valueOf(vulnObject.get( - "qid").toString()))); - } - vulnObj.put( - "qid", - Double.valueOf( - vulnObject.get("qid").toString()) - .longValue()); - vulnObj.put(CATEGORY, vulnObject.get(CATEGORY) - .toString()); - vulnObj.put(VULN_TYPE, vulnObject.get(VULN_TYPE) - .toString()); - if (vulnObject.containsKey(PATCHABLE)) { - vulnObj.put(PATCHABLE, "1".equals(vulnObject - .get(PATCHABLE).toString()) ? true - : false); - } - vulnObj.put( - SEVEITY_LEVEL, - Double.valueOf( - vulnObject.get(SEVEITY_LEVEL) - .toString()).intValue()); - synchronized (vulnerabilitiesDetailsList) { - vulnerabilitiesDetailsList.add(vulnObj); - } - }); - - if (!CollectionUtils.isEmpty(vulnAssetsAffected)) { - return vulnerabilitiesDetailsList - .stream() - .sorted((h1, h2) -> (int) (Double.parseDouble(h2.get( - ASSETS_AFFECTED).toString()) - (Double - .parseDouble(h1.get(ASSETS_AFFECTED).toString())))) - .sorted((h1, h2) -> (int) (Double.parseDouble(h2.get( - SEVEITY_LEVEL).toString()) - (Double.parseDouble(h1 - .get(SEVEITY_LEVEL).toString())))) - .collect(Collectors.toList()); - } else { - return vulnerabilitiesDetailsList - .stream() - .sorted((h1, h2) -> (int) (Double.parseDouble(h2.get( - SEVEITY_LEVEL).toString()) - (Double.parseDouble(h1 - .get(SEVEITY_LEVEL).toString())))) - .collect(Collectors.toList()); - } - - } - - /** - * Gets the target types. - * - * @param assetGroup the asset group - * @return the target types - */ - private String getTargetTypes(String assetGroup) { - String tTypesTemp; - String ttypes = null; - AssetApi assetApi = assetServiceClient.getTargetTypeList(assetGroup, - null); - AssetApiData data = assetApi.getData(); - AssetCountDTO[] targetTypes = data.getTargettypes(); - for (AssetCountDTO name : targetTypes) { - if (!Strings.isNullOrEmpty(name.getType())) { - tTypesTemp = new StringBuilder().append('\'') - .append(name.getType()).append('\'').toString(); - if (Strings.isNullOrEmpty(ttypes)) { - ttypes = tTypesTemp; - } else { - ttypes = new StringBuilder(ttypes).append(",").append(tTypesTemp).toString(); - } - } - } - return ttypes; - } - - /** - * Gets the vulnerability distribution summary. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the vulnerability distribution summary - */ - public List> getVulnerabilityDistributionSummary( - String assetGroup, String severity) { - - List> distributionSummary = new ArrayList<>(); - - Map> appDetails = getDistributionSummary(assetGroup, severity); - - Map directApp = new ConcurrentHashMap<>(); - Map vpApp = new ConcurrentHashMap<>(); - formDirectorAndVPByApp(directApp, vpApp); - - if (StringUtils.isEmpty(severity)) { - for (int i = 3; i <= 5; i++) { - distributionSummary.add(formDistributionSummary(appDetails, - directApp, vpApp, String.valueOf(i))); - } - } else - distributionSummary.add(formDistributionSummary(appDetails, - directApp, vpApp, severity)); - - return distributionSummary; - } - - /** - * Form distribution summary. - * - * @param appDetails the app details - * @param directApp the direct app - * @param vpApp the vp app - * @param severity the severity - * @return the map - */ - private Map formDistributionSummary( - Map> appDetails, - Map directApp, Map vpApp, - String severity) { - - List> vpData = new ArrayList<>(); - List> directorData = new ArrayList<>(); - List> appData = new ArrayList<>(); - - int total = 0; - - for (Entry> entry : appDetails.entrySet()) { - String appName = entry.getKey(); - Map sev = entry.getValue(); - - Map appTemp = new HashMap<>(); - appTemp.put("name", appName); - appTemp.put(COUNT, sev.get("S" + severity)); - total += Integer.valueOf(sev.get("S" + severity).toString()); - appData.add(appTemp); - if (!directorData.isEmpty()) { - String director; - if (StringUtils.isEmpty(directApp.get(appName))) - director = UNKNOWN; - else - director = directApp.get(appName); - - boolean directorExists = false; - for (Map existingDirectorData : directorData) { - if (director.equals(existingDirectorData.get("name"))) { - existingDirectorData.put( - COUNT, - Integer.valueOf(existingDirectorData.get(COUNT) - .toString()) - + Integer.valueOf(sev.get( - "S" + severity).toString())); - directorExists = true; - break; - } - } - if (!directorExists) { - Map directorTemp = new HashMap<>(); - directorTemp.put("name", director); - directorTemp.put(COUNT, sev.get("S" + severity)); - directorData.add(directorTemp); - } - } else { - Map directorTemp = new HashMap<>(); - if (StringUtils.isEmpty(directApp.get(appName))) { - directorTemp.put("name", UNKNOWN); - } else - directorTemp.put("name", directApp.get(appName)); - directorTemp.put(COUNT, sev.get("S" + severity)); - directorData.add(directorTemp); - } - - if (!vpData.isEmpty()) { - String vp; - if (StringUtils.isEmpty(vpApp.get(appName))) - vp = UNKNOWN; - else - vp = vpApp.get(appName); - - boolean vpExists = false; - for (Map existingVpData : vpData) { - if (vp.equals(existingVpData.get("name"))) { - existingVpData.put( - COUNT, - Integer.valueOf(existingVpData.get(COUNT) - .toString()) - + Integer.valueOf(sev.get( - "S" + severity).toString())); - vpExists = true; - break; - } - } - if (!vpExists) { - Map vpTemp = new HashMap<>(); - vpTemp.put("name", vp); - vpTemp.put(COUNT, sev.get("S" + severity)); - vpData.add(vpTemp); - } - } else { - Map vpTemp = new HashMap<>(); - if (StringUtils.isEmpty(vpApp.get(appName))) { - vpTemp.put("name", UNKNOWN); - } else - vpTemp.put("name", vpApp.get(appName)); - vpTemp.put(COUNT, sev.get("S" + severity)); - vpData.add(vpTemp); - } - } - - Map vpInfo = new LinkedHashMap<>(); - vpInfo.put("type", "VP"); - vpInfo.put("data", vpData); - Map directorInfo = new LinkedHashMap<>(); - directorInfo.put("type", "Director"); - directorInfo.put("data", directorData); - Map appInfo = new LinkedHashMap<>(); - appInfo.put("type", "Application"); - appInfo.put("data", appData); - - List> distributionList = new ArrayList<>(); - distributionList.add(vpInfo); - distributionList.add(directorInfo); - distributionList.add(appInfo); - - Map severityMap = new HashMap<>(); - severityMap.put(SEVERITY, Integer.valueOf(severity)); - severityMap.put("distribution", distributionList); - severityMap.put("total", total); - return severityMap; - } - - /** - * Gets the aging summary. - * - * @param assetGroup the asset group - * @return the aging summary - */ - public List> getAgingSummary(String assetGroup) { - return vulnerabilityRepository.getAgingSummary(assetGroup); - } - - /** - * Gets the aging distribution summary. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the aging distribution summary - */ - @SuppressWarnings("unchecked") - public List> getAgingDistributionSummary( - String assetGroup, String severity) { - - List> distributionSummary = new ArrayList<>(); - List> vulnApplications = new ArrayList<>(); - List vulnTargetTypes = getVulnTargetTypes(assetGroup); - if (!vulnTargetTypes.isEmpty()) { - for (String parentType : vulnTargetTypes) { - try { - vulnApplications.addAll(vulnerabilityRepository - .getAgingByApplication(assetGroup, parentType, - severity)); - } catch (Exception e) { - logger.error(e); - } - } - } - /* Parallel Stream and so concurrent hashmap */ - Map directApp = new ConcurrentHashMap<>(); - Map vpApp = new ConcurrentHashMap<>(); - try { - vulnerabilityRepository - .fetchExecDirectorApps() - .parallelStream() - .forEach( - app -> { - directApp.put(app.get(APP_TAG).toString(), app - .get("director").toString()); - vpApp.put(app.get(APP_TAG).toString(), - app.get("executiveSponsor").toString()); - }); - - } catch (Exception e) { - logger.error(e); - } - - Map appDetails = new ConcurrentHashMap<>(); - vulnApplications - .parallelStream() - .forEach( - vulnApps -> { - List> severityInfo = (List>) vulnApps - .get(SEV_INFO); - Map> sevDetails = new HashMap<>(); - for (Map sevInfo : severityInfo) { - Map days = new HashMap<>(); - days.put("days", sevInfo.get("days")); - days.put(COUNT, sevInfo.get(COUNT)); - sevDetails.put( - sevInfo.get(SEVERITY).toString(), days); - } - appDetails.put(vulnApps.get("application") - .toString(), sevDetails); - }); - if (StringUtils.isEmpty(severity)) { - for (int i = 3; i <= 5; i++) { - distributionSummary.add(formAgingDistributionSummary( - appDetails, directApp, vpApp, String.valueOf(i))); - } - } else - distributionSummary.add(formAgingDistributionSummary(appDetails, - directApp, vpApp, severity)); - - return distributionSummary; - } - - /** - * Form aging distribution summary. - * - * @param appDetails the app details - * @param directApp the direct app - * @param vpApp the vp app - * @param severity the severity - * @return the map - */ - @SuppressWarnings("unchecked") - private Map formAgingDistributionSummary( - Map appDetails, Map directApp, - Map vpApp, String severity) { - - List> vpData = new ArrayList<>(); - List> directorData = new ArrayList<>(); - List> appData = new ArrayList<>(); - - ObjectMapper oMapper = new ObjectMapper(); - - for (Entry entry : appDetails.entrySet()) { - String appName = entry.getKey(); - Map sev = oMapper.convertValue(entry.getValue(), - Map.class); - Map appTemp = new HashMap<>(); - appTemp.put("name", appName); - Map sevInfo = oMapper.convertValue( - sev.get("S" + severity), Map.class); - if (sevInfo.get(COUNT).toString().equals(ZERO) - || sevInfo.get(COUNT).toString().equals(DOUBLE_ZERO)) { - appTemp.put("days", 0); - } else { - appTemp.put("days", Math.floor(Double.valueOf(sevInfo.get( - "days").toString()) - / Double.valueOf(sevInfo.get(COUNT).toString()))); - } - - appData.add(appTemp); - if (!directorData.isEmpty()) { - String director; - if (StringUtils.isEmpty(directApp.get(appName))) - director = UNKNOWN; - else - director = directApp.get(appName); - - boolean directorExists = false; - for (Map existingDirectorData : directorData) { - if (director.equals(existingDirectorData.get("name"))) { - existingDirectorData.put( - "days", - Double.valueOf(existingDirectorData.get("days") - .toString()) - + Double.valueOf(sevInfo.get("days") - .toString())); - existingDirectorData.put( - COUNT, - Double.valueOf(existingDirectorData.get(COUNT) - .toString()) - + Double.valueOf(sevInfo.get(COUNT) - .toString())); - directorExists = true; - break; - } - } - if (!directorExists) { - Map directorTemp = new HashMap<>(); - directorTemp.put("name", director); - directorTemp.put("days", sevInfo.get("days")); - directorTemp.put(COUNT, sevInfo.get(COUNT)); - directorData.add(directorTemp); - } - } else { - Map directorTemp = new HashMap<>(); - if (StringUtils.isEmpty(directApp.get(appName))) { - directorTemp.put("name", UNKNOWN); - } else - directorTemp.put("name", directApp.get(appName)); - directorTemp.put("days", sevInfo.get("days")); - directorTemp.put(COUNT, sevInfo.get(COUNT)); - directorData.add(directorTemp); - } - - if (!vpData.isEmpty()) { - String vp; - if (StringUtils.isEmpty(vpApp.get(appName))) - vp = UNKNOWN; - else - vp = vpApp.get(appName); - - boolean vpExists = false; - for (Map existingVpData : vpData) { - if (vp.equals(existingVpData.get("name"))) { - existingVpData.put( - "days", - Double.valueOf(existingVpData.get("days") - .toString()) - + Double.valueOf(sevInfo.get("days") - .toString())); - existingVpData.put( - COUNT, - Double.valueOf(existingVpData.get(COUNT) - .toString()) - + Double.valueOf(sevInfo.get(COUNT) - .toString())); - vpExists = true; - break; - } - } - if (!vpExists) { - Map vpTemp = new HashMap<>(); - vpTemp.put("name", vp); - vpTemp.put("days", sevInfo.get("days")); - vpTemp.put(COUNT, sevInfo.get(COUNT)); - vpData.add(vpTemp); - } - } else { - Map vpTemp = new HashMap<>(); - if (StringUtils.isEmpty(vpApp.get(appName))) { - vpTemp.put("name", UNKNOWN); - } else - vpTemp.put("name", vpApp.get(appName)); - vpTemp.put("days", sevInfo.get("days")); - vpTemp.put(COUNT, sevInfo.get(COUNT)); - vpData.add(vpTemp); - } - } - - directorData.parallelStream().forEach( - director -> { - if (director.get(COUNT).toString().equals(ZERO) - || director.get(COUNT).toString() - .equals(DOUBLE_ZERO)) { - director.put("days", 0); - } else { - director.put("days", - Math.floor(Double.valueOf(director.get("days") - .toString()) - / Double.valueOf(director.get(COUNT) - .toString()))); - } - director.remove(COUNT); - }); - vpData.parallelStream().forEach( - vp -> { - if (vp.get(COUNT).toString().equals(ZERO) - || vp.get(COUNT).toString().equals(DOUBLE_ZERO)) { - vp.put("days", 0); - } else { - vp.put("days", Math.floor(Double.valueOf(vp.get("days") - .toString()) - / Double.valueOf(vp.get(COUNT).toString()))); - } - vp.remove(COUNT); - }); - - Map vpInfo = new LinkedHashMap<>(); - vpInfo.put("type", "VP"); - vpInfo.put("data", vpData); - Map directorInfo = new LinkedHashMap<>(); - directorInfo.put("type", "Director"); - directorInfo.put("data", directorData); - Map appInfo = new LinkedHashMap<>(); - appInfo.put("type", "Application"); - appInfo.put("data", appData); - - List> distributionList = new ArrayList<>(); - distributionList.add(vpInfo); - distributionList.add(directorInfo); - distributionList.add(appInfo); - - Map severityMap = new HashMap<>(); - severityMap.put(SEVERITY, Integer.valueOf(severity)); - severityMap.put("distribution", distributionList); - return severityMap; - } - - /** - * Gets the vulnerability by qid. - * - * @param qid the qid - * @return the vulnerability by qid - */ - public List> getVulnerabilityByQid(String qid) { - - List> vulnByCategories = new ArrayList<>(); - Map vulnKbData = vulnerabilityRepository - .getVulnerabilityByQid(qid); - vulnByCategories.add(formGeneralCategory(vulnKbData)); - vulnByCategories.add(formDetailsCategory(vulnKbData)); - vulnByCategories.add(formSoftwareCategory(vulnKbData)); - vulnByCategories.add(formImpactCategory(vulnKbData)); - vulnByCategories.add(formThreatCategory(vulnKbData)); - vulnByCategories.add(formSolutionCategory(vulnKbData)); - vulnByCategories.add(formExploitabilityCategory(vulnKbData)); - vulnByCategories.add(formAssociatedMalware(vulnKbData)); - return vulnByCategories; - } - - /** - * Form general category. - * - * @param vulnKbData the vuln kb data - * @return the map - */ - @SuppressWarnings("unchecked") - private Map formGeneralCategory( - Map vulnKbData) { - - ObjectMapper oMapper = new ObjectMapper(); - - Map category = new LinkedHashMap<>(); - category.put("name", "General Information"); - Map attributes = new LinkedHashMap<>(); - attributes.put("QID", - null == vulnKbData.get("qid") ? "" : vulnKbData.get("qid")); - attributes.put("Title", - null == vulnKbData.get(TITLE) ? "" : vulnKbData.get(TITLE)); - attributes.put( - "Severity Level", - null == vulnKbData.get(SEVEITY_LEVEL) ? "" : vulnKbData - .get(SEVEITY_LEVEL)); - attributes.put( - "Vulnerability Type", - null == vulnKbData.get(VULN_TYPE) ? "" : vulnKbData - .get(VULN_TYPE)); - attributes.put("Category", null == vulnKbData.get(CATEGORY) ? "" - : vulnKbData.get(CATEGORY)); - - Map discovery = oMapper.convertValue( - vulnKbData.get("discovery"), Map.class); - - if (discovery != null) { - attributes - .put("Authentication", - fetchAttributes(discovery, "authtypelist", - "authtype", true)); - } - - attributes.put("Service Modified", - null == vulnKbData.get("lastservicemodificationdatetime") ? "" - : vulnKbData.get("lastservicemodificationdatetime")); - attributes.put( - "Published", - null == vulnKbData.get("publisheddatetime") ? "" : vulnKbData - .get("publisheddatetime")); - - category.put(ATTRIBUTES, attributes); - return category; - } - - /** - * Form details category. - * - * @param vulnKbData the vuln kb data - * @return the map - */ - @SuppressWarnings("unchecked") - private Map formDetailsCategory( - Map vulnKbData) { - ObjectMapper oMapper = new ObjectMapper(); - Map category = new HashMap<>(); - category.put("name", "Details"); - Map attributes = new LinkedHashMap<>(); - if (Arrays.asList( - fetchAttributes(vulnKbData, "discovery", "additionalinfo", - false).toString().split("\\s*,\\s*")).contains( - "Patch Available")) { - attributes.put("Patch Availble", "Yes"); - } else - attributes.put("Patch Availble", "No"); - - attributes.put("CVE ID", - fetchAttributes(vulnKbData, "cvelist", "cve", true)); - attributes.put( - "Vendor Reference", - fetchAttributes(vulnKbData, "vendorreferencelist", - "vendorreference", true)); - attributes.put("Bugtraq ID", - fetchAttributes(vulnKbData, "bugtraqlist", "bugtraq", true)); - - if (null != vulnKbData.get("pciflag")) { - attributes.put("PCI Flag", Double.valueOf(vulnKbData.get("pciflag") - .toString()) == 0 ? false : true); - } - - attributes.put("PCI Reasons", - fetchAttributes(vulnKbData, "pcireasons", "pcireason", true)); - if (null != vulnKbData.get("supportedmodules")) { - attributes.put("Supported Modules", - vulnKbData.get("supportedmodules")); - } - - Map cvss = oMapper.convertValue(vulnKbData.get("cvss"), - Map.class); - Map cvss3 = oMapper.convertValue( - vulnKbData.get("cvssv3"), Map.class); - if (cvss != null) { - attributes.put("CVSS Base", cvss.get("base")); - attributes.put("CVSS Temporal", cvss.get("temporal")); - attributes.put("CVSS Access Vector", - fetchAttributes(cvss, "access", "vector", false)); - } - if (cvss3 != null) { - attributes.put("CVSS3 Base", cvss3.get("base")); - attributes.put("CVSS3 Temporal", cvss3.get("temporal")); - } - - category.put(ATTRIBUTES, attributes); - return category; - } - - /** - * Form software category. - * - * @param vulnKbData the vuln kb data - * @return the map - */ - private Map formSoftwareCategory( - Map vulnKbData) { - - Map category = new HashMap<>(); - category.put("name", "Software"); - category.put(ATTRIBUTES, - fetchAttributes(vulnKbData, "softwarelist", "software", true)); - return category; - } - - /** - * Form threat category. - * - * @param vulnKbData the vuln kb data - * @return the map - */ - private Map formThreatCategory( - Map vulnKbData) { - - Map category = new HashMap<>(); - category.put("name", "Threat"); - category.put(ATTRIBUTES, null == vulnKbData.get("diagnosis") ? "" - : vulnKbData.get("diagnosis")); - return category; - } - - /** - * Form impact category. - * - * @param vulnKbData the vuln kb data - * @return the map - */ - private Map formImpactCategory( - Map vulnKbData) { - - Map category = new HashMap<>(); - category.put("name", "Impact"); - category.put(ATTRIBUTES, null == vulnKbData.get("consequence") ? "" - : vulnKbData.get("consequence")); - return category; - } - - /** - * Form solution category. - * - * @param vulnKbData the vuln kb data - * @return the map - */ - private Map formSolutionCategory( - Map vulnKbData) { - - Map category = new HashMap<>(); - category.put("name", "Solution"); - category.put(ATTRIBUTES, null == vulnKbData.get("solution") ? "" - : vulnKbData.get("solution")); - return category; - } - - /** - * Form exploitability category. - * - * @param vulnKbData the vuln kb data - * @return the map - */ - @SuppressWarnings("unchecked") - private Map formExploitabilityCategory( - Map vulnKbData) { - - ObjectMapper oMapper = new ObjectMapper(); - List> attributes = new ArrayList<>(); - Map category = new HashMap<>(); - category.put("name", "Exploitability"); - - Map correlation = oMapper.convertValue( - vulnKbData.get("correlation"), Map.class); - if (correlation != null && !correlation.isEmpty()) { - List> exploits = (List>) fetchAttributes( - correlation, "exploits", "expltsrc", true); - for (Map exploitTemp : exploits) { - Map exploit = new HashMap<>(); - exploit.put(SRC_NAME, exploitTemp.get(SRC_NAME)); - exploit.put( - "exploits", - fetchAttributes(exploitTemp, "expltlist", "explt", true)); - attributes.add(exploit); - } - } - category.put(ATTRIBUTES, attributes); - return category; - } - - /** - * Form associated malware. - * - * @param vulnKbData the vuln kb data - * @return the map - */ - @SuppressWarnings("unchecked") - private Map formAssociatedMalware( - Map vulnKbData) { - ObjectMapper oMapper = new ObjectMapper(); - List> attributes = new ArrayList<>(); - Map category = new HashMap<>(); - category.put("name", "Malware"); - - Map correlation = oMapper.convertValue( - vulnKbData.get("correlation"), Map.class); - if (correlation != null && !correlation.isEmpty()) { - List> exploits = (List>) fetchAttributes( - correlation, "malware", "mwsrc", true); - for (Map exploitTemp : exploits) { - Map exploit = new HashMap<>(); - exploit.put(SRC_NAME, exploitTemp.get(SRC_NAME)); - exploit.put("malwares", - fetchAttributes(exploitTemp, "mwlist", "mwinfo", true)); - attributes.add(exploit); - } - } - category.put(ATTRIBUTES, attributes); - return category; - } - - /** - * Fetch attributes. - * - * @param vulnKbData the vuln kb data - * @param parent the parent - * @param child the child - * @param isList the is list - * @return the object - */ - @SuppressWarnings("unchecked") - private Object fetchAttributes(Map vulnKbData, - String parent, String child, boolean isList) { - Map parentMap = new ObjectMapper().convertValue( - vulnKbData.get(parent), Map.class); - Object childObj; - if (parentMap != null) { - childObj = parentMap.get(child); - if (childObj != null) - return childObj; - } - - if (isList) { - return new ArrayList<>(); - } else { - return ""; - } - } - - /** - * Gets the distribution summary. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the distribution summary - */ - @SuppressWarnings("unchecked") - private Map> getDistributionSummary(String assetGroup, String severity) { - - List> vulnApplications = new ArrayList<>(); - List vulnTargetTypes = getVulnTargetTypes(assetGroup); - if (!vulnTargetTypes.isEmpty()) { - for (String parentType : vulnTargetTypes) { - try { - vulnApplications.addAll(vulnerabilityRepository - .getVulnerabilyAcrossAppAndEnv(assetGroup, - "tags.Application.keyword", "", parentType, - severity)); - } catch (Exception e) { - logger.error("Exception in getting getDistributionSummary ",e); - } - } - } - - Map> appDetails = new ConcurrentHashMap<>(); - vulnApplications - .parallelStream() - .forEach( - vulnApps -> { - List> severityInfo = (List>) vulnApps - .get(SEV_INFO); - Map sevDetails = new HashMap<>(); - severityInfo.forEach(sevInfo -> { - sevDetails.put( - sevInfo.get(SEVERITY).toString(), - sevInfo.get(COUNT)); - }); - appDetails.put(vulnApps.get("application") - .toString(), sevDetails); - }); - return appDetails; - } - - /** - * Form director and VP by app. - * - * @param directApp the direct app - * @param vpApp the vp app - */ - private void formDirectorAndVPByApp(Map directApp, Map vpApp) { - try { - vulnerabilityRepository - .fetchExecDirectorApps() - .parallelStream() - .forEach( - app -> { - if(null != directApp) { - directApp.put(app.get(APP_TAG).toString(), app - .get("director").toString()); - } - if(null != vpApp) { - vpApp.put(app.get(APP_TAG).toString(), - app.get("executiveSponsor").toString()); - } - }); - - } catch (Exception e) { - logger.error(e); - } - } - - /** - * Gets the highest lowest performers. - * - * @param assetGroup the asset group - * @param severity the severity - * @param type the type - * @return the highest lowest performers - */ - @SuppressWarnings("unchecked") - public Map getHighestLowestPerformers(String assetGroup, String severity,String type) { - - Map appDetails = new HashMap<>(); - List vulnTargetTypes = getVulnTargetTypes(assetGroup); - - if(StringUtils.isBlank(severity)) { - severity=SEVERITY_LEVELS; - } - Map perfData = new HashMap<>(); - - if("org".equalsIgnoreCase(type)){ - if (!vulnTargetTypes.isEmpty()) { - for (String parentType : vulnTargetTypes) { - try { - Map appDetailsTemp = vulnerabilityRepository.getAppsBySeverity(assetGroup, parentType, severity); - if(appDetails.isEmpty()) { - appDetails = new HashMap<>(appDetailsTemp); - } else { - for(Entry appDetailTemp : appDetailsTemp.entrySet()) { - boolean appExists = false; - for(Entry appDetail : appDetails.entrySet()) { - if(appDetail.getKey().equals(appDetailTemp.getKey())){ - appDetails.put(appDetail.getKey(),appDetail.getValue()+appDetailTemp.getValue()); - appExists = true; - break; - } - } - if(!appExists) { - appDetails.put(appDetailTemp.getKey(),appDetailTemp.getValue()); - } - } - } - } catch (Exception e) { - logger.error("Exception in getHighestLowestPeformers ",e); - } - } - } - - Map directApp = new ConcurrentHashMap<>(); - formDirectorAndVPByApp(directApp, null); - - - for (Entry entry : appDetails.entrySet()) { - - String appName = entry.getKey(); - - if (!perfData.isEmpty()) { - String director; - if (StringUtils.isEmpty(directApp.get(appName))) - director = UNKNOWN; - else - director = directApp.get(appName).trim(); - - boolean directorExists = false; - for (Entry existingDirectorData : perfData.entrySet()) { - if (director.equals(existingDirectorData.getKey())) { - perfData.put( - director,existingDirectorData.getValue()+ Integer.valueOf(entry.getValue().toString())); - directorExists = true; - break; - } - } - if (!directorExists) { - perfData.put(director, Integer.valueOf(entry.getValue().toString())); - } - } else { - if(StringUtils.isEmpty(directApp.get(appName))) { - perfData.put(UNKNOWN, Integer.valueOf(entry.getValue().toString())); - } else - perfData.put(directApp.get(appName),Integer.valueOf(entry.getValue().toString())); - } - } - - }else if(APPLICATION.equalsIgnoreCase(type)){ - try{ - List> vulnApplications = getVulnerabilityByAppAndEnv( - assetGroup, TAGS_APPS, ""); - for(Map appInfo:vulnApplications) { - String app = appInfo.get(APPS).toString(); - List> sevInfo = (List>)appInfo.get(SEV_INFO); - perfData.put(app, getVulnInstanceCount(sevInfo,severity)); - } - }catch(Exception e){ - - } - - }else if(ENVIRONMENT.equalsIgnoreCase(type)){ - - try{ - List> vulnEnvmnts = getVulnerabilityByAppAndEnv( - assetGroup, TAGS_ENV, ""); - for(Map envInfo:vulnEnvmnts) { - String env = envInfo.get(ENV).toString(); - List> sevInfo = (List>)envInfo.get(SEV_INFO); - perfData.put(env, getVulnInstanceCount(sevInfo,severity)); - } - }catch(Exception e){ - - } - } - - return perfData.entrySet().stream() - .sorted(Map.Entry.comparingByValue()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, - (oldValue, newValue) -> oldValue, LinkedHashMap::new)); - } - - /** - * Gets the vuln instance count. - * - * @param sevInfoList the sev info list - * @param severity the severity - * @return the vuln instance count - */ - private int getVulnInstanceCount(List> sevInfoList, String severity){ - List sevList = Arrays.asList(severity.split(",")); - return sevInfoList.stream().filter(sevInfo-> sevList.contains(sevInfo.get("severitylevel").toString())).mapToInt(sevInfo-> Double.valueOf(sevInfo.get("vulnInstanceCount").toString()).intValue()).sum(); - } - - /** - * Gets the vuln target types. - * - * @param assetGroup the asset group - * @return the vuln target types - */ - private List getVulnTargetTypes(String assetGroup) { - - String validTargetTypes = getTargetTypes(assetGroup); - List vulnTargetTypes = new ArrayList<>(); - for (String vulnType : vulnTypes.split(",")) { - if (!StringUtils.isEmpty(validTargetTypes) - && validTargetTypes.contains(vulnType.trim())) { - vulnTargetTypes.add(vulnType); - } - } - return vulnTargetTypes; - } - - /** - * Gets the distribution summary by infra type. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the distribution summary by infra type - * @throws ServiceException the service exception - */ - public List> getDistributionSummaryByInfraType(String assetGroup, String severity) throws ServiceException { - - List> distributionList = new ArrayList<>(); - - List vulnTargetTypes = getVulnTargetTypes(assetGroup); - if(StringUtils.isBlank(severity)) { - severity=SEVERITY_LEVELS; - } - long totalVulnCount = 0; - for (String vulnType : vulnTargetTypes) { - Map info = new HashMap<>(); - try{ - info = vulnerabilityRepository. - getDistributionSummaryByInfraType(assetGroup, severity, vulnType); - }catch(Exception e){ - logger.error("Error in getDistributionSummaryByInfraType ", e); - throw new ServiceException(); - } - - totalVulnCount += Long.valueOf(info.get(VULNEREBILITIES).toString()); - - if(vulnType.equals(EC2)) { - info.put(CATEGORY, "Cloud"); - } else { - info.put(CATEGORY, "On-Prem"); - } - distributionList.add(info); - } - - double contribution = HUNDRED; - for(int i=0;i info = distributionList.get(i); - double contributionPercent = Math.floor((Double.valueOf(info.get(VULNEREBILITIES).toString())/totalVulnCount)*HUNDRED); - if(i== distributionList.size()-1){ - info.put(CONTRIBUTION, contribution); - }else{ - info.put(CONTRIBUTION, contributionPercent); - contribution = contribution-contributionPercent; - } - } - return distributionList; - } - - /** - * Gets the distribution summary by env. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the distribution summary by env - * @throws ServiceException the service exception - */ - public List> getDistributionSummaryByEnv(String assetGroup, String severity) throws ServiceException { - - List> distributionList = new ArrayList<>(); - if(StringUtils.isBlank(severity)) { - severity=SEVERITY_LEVELS; - } - - long totalVulnCount = 0; - - Map prodInfo = new HashMap<>(); - prodInfo.put(TOTAL_VULN_ASSETS, 0); - prodInfo.put(VULNEREBILITIES, 0); - prodInfo.put(UNIQUE_VULN_COUNT, 0); - - Map nonProdInfo = new HashMap<>(); - nonProdInfo.put(TOTAL_VULN_ASSETS, 0); - nonProdInfo.put(VULNEREBILITIES, 0); - nonProdInfo.put(UNIQUE_VULN_COUNT, 0); - try { - Map prodInfoTemp = vulnerabilityRepository.getProdInfoByEnv(assetGroup, severity); - Map nonProdInfoTemp = vulnerabilityRepository.getNonProdInfoByEnv(assetGroup, severity); - - totalVulnCount += prodInfoTemp.get(VULNEREBILITIES)+nonProdInfoTemp.get(VULNEREBILITIES); - - for (Entry entry : prodInfo.entrySet()) { - prodInfo.put(entry.getKey(), Long.valueOf(entry.getValue().toString())+prodInfoTemp.get(entry.getKey())); - } - - for (Entry entry : nonProdInfo.entrySet()) { - nonProdInfo.put(entry.getKey(), Long.valueOf(entry.getValue().toString())+nonProdInfoTemp.get(entry.getKey())); - } - } catch (Exception e) { - throw new ServiceException(e); - } - prodInfo.put(CATEGORY,"Prod"); - distributionList.add(prodInfo); - nonProdInfo.put(CATEGORY,"Non-Prod"); - distributionList.add(nonProdInfo); - - double contribution = HUNDRED; - for(int i=0;i info = distributionList.get(i); - if(totalVulnCount > 0) { - double contributionPercent = Math.floor((Double.valueOf(info.get(VULNEREBILITIES).toString())/totalVulnCount)*HUNDRED); - if(i== distributionList.size()-1){ - info.put(CONTRIBUTION, contribution); - }else{ - info.put(CONTRIBUTION, contributionPercent); - contribution = contribution-contributionPercent; - } - } else { - info.put(CONTRIBUTION, 0); - } - } - return distributionList; - } - - /** - * Gets the distribution summary by vuln type. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the distribution summary by vuln type - * @throws DataException the data exception - */ - public List> getDistributionSummaryByVulnType(String assetGroup, String severity) throws DataException { - if(StringUtils.isBlank(severity)) { - severity=SEVERITY_LEVELS; - } - return vulnerabilityRepository.getDistributionSummaryByVulnType(assetGroup, severity); - } - - /** - * Gets the remediation actions summary. - * - * @param assetGroup the asset group - * @param severity the severity - * @return the remediation actions summary - * @throws DataException the data exception - */ - public List> getRemediationActionsSummary(String assetGroup, String severity) throws DataException { - - List> remediationList = new ArrayList<>(); - if(StringUtils.isBlank(severity)) { - severity=SEVERITY_LEVELS; - } - - List> eolActions = vulnerabilityRepository.getDataFromPacmanRDS("SELECT matchingString,subAction FROM cf_RemediationCriteria WHERE " - + "action='Remove/Replace EOL Software'"); - List> stopRemoveActions = vulnerabilityRepository.getDataFromPacmanRDS("SELECT matchingString,subAction FROM cf_RemediationCriteria WHERE " - + "action='Stop Service/Remove Software'"); - List> swConfigChangeActions = vulnerabilityRepository.getDataFromPacmanRDS("SELECT matchingString,subAction FROM cf_RemediationCriteria WHERE " - + "action='Software Configuration Change'"); - List> swUpdateActions = vulnerabilityRepository.getDataFromPacmanRDS("SELECT matchingString,subAction FROM cf_RemediationCriteria WHERE " - + "action='Software Update'"); - - String softwareConfig = getAllMatchingString(swConfigChangeActions); - String softwareUpdate = getAllMatchingString(swUpdateActions); - - Map osPatching = new HashMap<>(); - osPatching.put(ACTION, "OS Patching"); - osPatching.put(DESCRIPTION, "Apply the patches released by the operating system provider"); - osPatching.put(CONTRIBUTION, 0); - Map eolSoftware = new HashMap<>(); - eolSoftware.put(ACTION, "Remove/Replace EOL Software"); - eolSoftware.put(CONTRIBUTION, 0); - eolSoftware.put(DESCRIPTION, "Remove or replace below listed End of Life Software versions"); - Map noSolution = new HashMap<>(); - noSolution.put(ACTION, "No Solution Available"); - noSolution.put(CONTRIBUTION, 0); - noSolution.put(DESCRIPTION, "Vulnerabilities with no published solution yet"); - Map stopRemove = new HashMap<>(); - stopRemove.put(ACTION, "Stop Service/Remove Software"); - stopRemove.put(CONTRIBUTION, 0); - stopRemove.put(DESCRIPTION, "Stop unimportant vulnerable services, remove malicious softwares from the hosts"); - Map swConfigChange = new HashMap<>(); - swConfigChange.put(ACTION, "Software Configuration Change"); - swConfigChange.put(CONTRIBUTION, 0); - swConfigChange.put(DESCRIPTION, "Fix the configurations of the below listed softwares. Some default configurations like default admin username and password should be replaced with a stronger one"); - Map swUpdate = new HashMap<>(); - swUpdate.put(ACTION, "Software Update"); - swUpdate.put(CONTRIBUTION, 0); - swUpdate.put(DESCRIPTION, "Update the below listed softwares to their latest version or apply patches released by the software provider "); - - List> eolSubActions = new ArrayList<>(); - List> stopRemoveSubActions = new ArrayList<>(); - List> swConfigChangeSubActions = new ArrayList<>(); - List> swUpdateSubActions = new ArrayList<>(); - - Map unclassified = new HashMap<>(); - unclassified.put(ACTION, "Unclassified"); - unclassified.put(DESCRIPTION, "These vulnerabilities are not classified yet. Refer the vulnerability description to fix the vulnerability"); - unclassified.put(CONTRIBUTION, 0); - - Map qids = vulnerabilityRepository.getAllQidByAG(assetGroup, severity); - Long total = qids.entrySet().stream().mapToLong(entry-> Long.valueOf(entry.getValue().toString())).sum(); - for (String qidTitleClass: qids.keySet()) { - String qid = qidTitleClass.split("~")[0]; - String vulnTitle = qidTitleClass.split("~")[1].toLowerCase(); - String classification = qidTitleClass.split("~")[2]; - if("OS".equalsIgnoreCase(classification)){ - osPatching.put(CONTRIBUTION, Long.valueOf(osPatching.get(CONTRIBUTION).toString())+Long.valueOf(qids.get(qidTitleClass).toString())); - }else if(vulnTitle.contains("EOL/Obsolete".toLowerCase())) { - eolSoftware.put(CONTRIBUTION, Long.valueOf(eolSoftware.get(CONTRIBUTION).toString())+Long.valueOf(qids.get(qidTitleClass).toString())); - formSubActionList(eolActions,eolSubActions,vulnTitle,Long.valueOf(qids.get(qidTitleClass).toString())); - } else if("11925".equals(qid) || "370914".equals(qid)) { - noSolution.put(CONTRIBUTION, Long.valueOf(noSolution.get(CONTRIBUTION).toString())+Long.valueOf(qids.get(qidTitleClass).toString())); - } else if(vulnTitle.contains("Java Debug Wire Protocol".toLowerCase())) { - stopRemove.put(CONTRIBUTION, Long.valueOf(stopRemove.get(CONTRIBUTION).toString())+Long.valueOf(qids.get(qidTitleClass).toString())); - formSubActionList(stopRemoveActions,stopRemoveSubActions,vulnTitle,Long.valueOf(qids.get(qidTitleClass).toString())); - } else if(checkVulnTitle(vulnTitle,softwareConfig)) { - swConfigChange.put(CONTRIBUTION, Long.valueOf(swConfigChange.get(CONTRIBUTION).toString())+Long.valueOf(qids.get(qidTitleClass).toString())); - formSubActionList(swConfigChangeActions,swConfigChangeSubActions,vulnTitle,Long.valueOf(qids.get(qidTitleClass).toString())); - } else if(checkVulnTitle(vulnTitle,softwareUpdate)) { - swUpdate.put(CONTRIBUTION, Long.valueOf(swUpdate.get(CONTRIBUTION).toString())+Long.valueOf(qids.get(qidTitleClass).toString())); - formSubActionList(swUpdateActions,swUpdateSubActions,vulnTitle,Long.valueOf(qids.get(qidTitleClass).toString())); - } else { - unclassified.put(CONTRIBUTION, Long.valueOf(unclassified.get(CONTRIBUTION).toString())+Long.valueOf(qids.get(qidTitleClass).toString())); - } - } - - calculateContributionPercentage(eolSubActions,Long.valueOf(eolSoftware.get(CONTRIBUTION).toString())); - calculateContributionPercentage(stopRemoveSubActions,Long.valueOf(stopRemove.get(CONTRIBUTION).toString())); - calculateContributionPercentage(swConfigChangeSubActions,Long.valueOf(swConfigChange.get(CONTRIBUTION).toString())); - calculateContributionPercentage(swUpdateSubActions,Long.valueOf(swUpdate.get(CONTRIBUTION).toString())); - - eolSoftware.put(SUB_ACTIONS, eolSubActions); - stopRemove.put(SUB_ACTIONS, stopRemoveSubActions); - swConfigChange.put(SUB_ACTIONS, swConfigChangeSubActions); - swUpdate.put(SUB_ACTIONS, swUpdateSubActions); - - remediationList.add(osPatching); - remediationList.add(eolSoftware); - remediationList.add(noSolution); - remediationList.add(stopRemove); - remediationList.add(swConfigChange); - remediationList.add(swUpdate); - remediationList.add(unclassified); - - calculateContributionPercentage(remediationList,total); - return remediationList; - } - - /** - * Check vuln title. - * - * @param vulnTitle the vuln title - * @param values the values - * @return true, if successful - */ - private boolean checkVulnTitle(String vulnTitle, String values) { - for(String value : values.split(",")) { - if(vulnTitle.contains(value)) { - return true; - } - } - return false; - } - - /** - * Form sub action list. - * - * @param actions the actions - * @param subActions the sub actions - * @param vulnTitle the vuln title - * @param contribution the contribution - */ - private void formSubActionList(List> actions, List> subActions,String vulnTitle, long contribution) { - boolean titleMatched = false; - for(Map action : actions) { - if(vulnTitle.contains(action.get(MATCHING_STRING).toString().toLowerCase())) { - titleMatched = true; - formSubAction(subActions, action.get("subAction").toString(), action.get(MATCHING_STRING).toString(), contribution); - break; - } - } - if(!titleMatched) { - formSubAction(subActions, "Others", "Others", contribution); - } - } - - /** - * Form sub action. - * - * @param subActions the sub actions - * @param subActionTitle the sub action title - * @param subActiondescr the sub actiondescr - * @param contribution the contribution - */ - private void formSubAction(List> subActions, String subActionTitle, String subActiondescr, Long contribution) { - if(subActions.isEmpty()) { - Map subAction = new HashMap<>(); - subAction.put(ACTION, subActionTitle); - subAction.put(DESCRIPTION, subActiondescr); - subAction.put(CONTRIBUTION, contribution); - subActions.add(subAction); - } else { - boolean subActionExists = false; - for(Map subAction : subActions) { - if(subActionTitle.equals(subAction.get(ACTION).toString())) { - subActionExists = true; - subAction.put(CONTRIBUTION, Long.valueOf(subAction.get(CONTRIBUTION).toString())+contribution); - break; - } - } - if(!subActionExists) { - Map subAction = new HashMap<>(); - subAction.put(ACTION, subActionTitle); - subAction.put(DESCRIPTION, subActiondescr); - subAction.put(CONTRIBUTION, contribution); - subActions.add(subAction); - } - } - } - - /** - * Gets the all matching string. - * - * @param actions the actions - * @return the all matching string - */ - private String getAllMatchingString(List> actions) { - List matchingStrings = new ArrayList<>(); - for(Map action : actions) { - matchingStrings.add(action.get(MATCHING_STRING).toString().toLowerCase()); - } - return StringUtils.join(matchingStrings, ","); - } - - /** - * Calculate contribution percentage. - * - * @param contributionList the contribution list - * @param total the total - */ - private void calculateContributionPercentage(List> contributionList, long total) { - DecimalFormat df = new DecimalFormat("###.##"); - ListIterator> it = contributionList.listIterator(); - String contributionPercent; - while(it.hasNext()){ - Map bucket = it.next(); - Long contribution = Long.valueOf(bucket.get(CONTRIBUTION).toString()); - if(contribution==0){ - it.remove(); - }else{ - contributionPercent = df.format((contribution*HUNDRED)/total); - if("0".equals(contributionPercent)){ - it.remove(); - }else{ - bucket.put(CONTRIBUTION, Float.valueOf(contributionPercent)); - } - } - } - } - - /** - * Creates the trend annotation. - * - * @param request the request - * @return true, if successful - * @throws JsonProcessingException the json processing exception - */ - public boolean createTrendAnnotation(TrendNote request) throws JsonProcessingException { - return vulnerabilityRepository.createTrendAnnotation(request); - } - - /** - * Gets the trend annotations. - * - * @param assetGroup the asset group - * @param from the from - * @return the trend annotations - * @throws DataException the data exception - */ - public List> getTrendAnnotations(String assetGroup, Date from) throws DataException { - - List> globalAnnotations = new ArrayList<>(); - List> assetGroupAnnotations = new ArrayList<>(); - List> annotations = vulnerabilityRepository.getTrendAnnotations(assetGroup,from); - - annotations.parallelStream().forEach(annotation -> { - if(StringUtils.isEmpty(annotation.get("ag").toString())) { - synchronized (globalAnnotations) { - globalAnnotations.add(annotation); - } - } else { - synchronized (assetGroupAnnotations) { - assetGroupAnnotations.add(annotation); - } - } - }); - - Map gloablMap = new HashMap<>(); - gloablMap.put("type", "Global"); - gloablMap.put("data", globalAnnotations); - - Map agMap = new HashMap<>(); - agMap.put("type", "AssetGroup"); - agMap.put("data", assetGroupAnnotations); - - List> noteList = new ArrayList<>(); - noteList.add(agMap); - noteList.add(gloablMap); - - return noteList; - } - - /** - * Delete trend annotation. - * - * @param noteId the note id - * @return true, if successful - */ - public boolean deleteTrendAnnotation(String noteId) { - return vulnerabilityRepository.deleteTrendAnnotation(noteId); - } -} diff --git a/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/controller/ComplianceControllerTest.java b/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/controller/ComplianceControllerTest.java index a0d66062e..f5a0d0fc6 100644 --- a/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/controller/ComplianceControllerTest.java +++ b/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/controller/ComplianceControllerTest.java @@ -43,7 +43,6 @@ import com.tmobile.pacman.api.compliance.domain.PolicyViolationDetails; import com.tmobile.pacman.api.compliance.domain.RevokeIssuesException; import com.tmobile.pacman.api.compliance.service.ComplianceService; -import com.tmobile.pacman.api.compliance.service.VulnerabilityService; import com.tmobile.pacman.api.compliance.util.CommonTestUtil; @RunWith(MockitoJUnitRunner.class) @@ -55,9 +54,6 @@ public class ComplianceControllerTest { @Mock ComplianceService complianceService; - @Mock - VulnerabilityService vulnerabilityService; - @Test public void getIssuesTest() throws Exception { when(complianceService.getIssues(anyObject())).thenReturn(CommonTestUtil.getResponseWithOrder()); @@ -111,18 +107,6 @@ public void getTaggingTest() throws Exception { assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); } - @Test - public void getVulnerabilitiesTest() throws Exception { - when(vulnerabilityService.getVulnerabilitySummary(anyString(),anyString())).thenReturn(CommonTestUtil.getMapObject()); - assertThat(complianceController.getVulnerabilities("ag"), is(notNullValue())); - assertThat(complianceController.getVulnerabilities(""), is(notNullValue())); - - when(vulnerabilityService.getVulnerabilitySummary(anyString(),anyString())).thenThrow(new ServiceException()); - when(complianceService.formatException(anyObject())).thenReturn(ResponseUtils.buildFailureResponse(new ServiceException())); - ResponseEntity responseObj = complianceController.getVulnerabilities("ag"); - assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - @Test public void getCertificatesTest() throws Exception { when(complianceService.getCertificates(anyString())).thenReturn(CommonTestUtil.getMapLong()); diff --git a/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/controller/VulnerabilityControllerTest.java b/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/controller/VulnerabilityControllerTest.java deleted file mode 100644 index 53c151c0c..000000000 --- a/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/controller/VulnerabilityControllerTest.java +++ /dev/null @@ -1,428 +0,0 @@ -/******************************************************************************* - * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - ******************************************************************************/ -package com.tmobile.pacman.api.compliance.controller; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.powermock.api.mockito.PowerMockito.when; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.powermock.modules.junit4.PowerMockRunner; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -import com.tmobile.pacman.api.commons.exception.DataException; -import com.tmobile.pacman.api.commons.exception.ServiceException; -import com.tmobile.pacman.api.compliance.domain.Request; -import com.tmobile.pacman.api.compliance.domain.TrendRequest; -import com.tmobile.pacman.api.compliance.service.VulnerabilityService; - -@RunWith(PowerMockRunner.class) -public class VulnerabilityControllerTest { - - @InjectMocks - VulnerabilityController vulnerabilityController; - - @Mock - VulnerabilityService vulnerabilityService; - - @Test - public void getVulnerabilitiesDetailsTest() throws Exception { - - List> vulnDetails = new ArrayList<>(); - - Request request = new Request(); - request.setAg("ag"); - request.setFrom(0); - - when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(new ArrayList<>()); - - assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.OK); - - request.setFilter(new HashMap<>()); - vulnDetails.add(new HashMap<>()); - vulnDetails.add(new HashMap<>()); - - when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(vulnDetails); - - assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.OK); - - request.setSize(1); - when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(vulnDetails); - - assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.OK); - - request.setSize(3); - when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(vulnDetails); - - assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulnerabilitiesDetailsTest_Failure() throws Exception { - - Request request = new Request(); - - assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - request.setAg("ag"); - request.setFrom(-1); - - assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - request.setFrom(2); - List> vulnDetails = new ArrayList>(); - vulnDetails.add(new HashMap<>()); - - when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(vulnDetails); - - assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getCertificatesDetailsTest_Exception() throws Exception { - - Request request = new Request(); - request.setAg("ag"); - request.setFrom(0); - - when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenThrow(new ServiceException()); - assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getVulnerabilitysummaryTest() throws Exception { - - when(vulnerabilityService.getVulnerabilitySummary(anyString(),anyString())).thenReturn(new HashMap<>()); - assertTrue(vulnerabilityController.getVulnerabilitysummary("ag","3,4,5").getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulnerabilitysummaryTest_Exception() throws Exception { - - assertTrue(vulnerabilityController.getVulnerabilitysummary("","3,4,5").getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - when(vulnerabilityService.getVulnerabilitySummary(anyString(),anyString())).thenThrow(new ServiceException()); - assertTrue(vulnerabilityController.getVulnerabilitysummary("ag","3,4,5").getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getVulnerabilityByApplicationsTest() throws Exception { - - when(vulnerabilityService.getVulnerabilityByAppAndEnv(anyString(), anyString(), anyString())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilityByApplications("ag").getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulnerabilityByApplicationsTest_Exception() throws Exception { - - assertTrue(vulnerabilityController.getVulnerabilityByApplications("").getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - when(vulnerabilityService.getVulnerabilityByAppAndEnv(anyString(), anyString(), anyString())).thenThrow(new Exception()); - assertTrue(vulnerabilityController.getVulnerabilityByApplications("ag").getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getVulnerabilitiesTrendTest() throws Exception { - - TrendRequest request = new TrendRequest(); - request.setAg("ag"); - - when(vulnerabilityService.getVulnerabilityTrend(anyString(),anyObject(),anyObject(),anyObject())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.OK); - - request.setFrom(new Date()); - when(vulnerabilityService.getVulnerabilityTrend(anyString(),anyObject(),anyObject(),anyObject())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.OK); - - request.setTo(new Date()); - when(vulnerabilityService.getVulnerabilityTrend(anyString(),anyObject(),anyObject(),anyObject())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.OK); - - request = new TrendRequest(); - request.setAg("ag"); - request.setTo(new Date()); - when(vulnerabilityService.getVulnerabilityTrend(anyString(),anyObject(),anyObject(),anyObject())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulnerabilitiesTrendTest_Exception() throws Exception { - - TrendRequest request = new TrendRequest(); - assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - request.setAg("ag"); - when(vulnerabilityService.getVulnerabilityTrend(anyString(),anyObject(),anyObject(),anyObject())).thenThrow(new ServiceException()); - assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getVulnerabilityByEnvironmentTest() throws Exception { - - when(vulnerabilityService.getVulnerabilityByAppAndEnv(anyString(), anyString(), anyString())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilityByEnvironment("ag","app").getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulnerabilityByEnvironmentTest_Exception() throws Exception { - - assertTrue(vulnerabilityController.getVulnerabilityByEnvironment("",null).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - when(vulnerabilityService.getVulnerabilityByAppAndEnv(anyString(), anyString(), anyString())).thenThrow(new Exception()); - assertTrue(vulnerabilityController.getVulnerabilityByEnvironment("ag","app").getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getVulnerabilityDistributionTest() throws Exception { - - when(vulnerabilityService.getVulnerabilitiesDistribution(anyString())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilityDistribution("ag").getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulnerabilityDistributionTest_Exception() throws Exception { - - assertTrue(vulnerabilityController.getVulnerabilityDistribution("").getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - when(vulnerabilityService.getVulnerabilitiesDistribution(anyString())).thenThrow(new ServiceException()); - assertTrue(vulnerabilityController.getVulnerabilityDistribution("ag").getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getVulnerabilitysummaryByResourceIdTest() throws Exception { - - when(vulnerabilityService.getVulnerabilitysummaryByResourceId(anyString())).thenReturn(new HashMap<>()); - assertTrue(vulnerabilityController.getVulnerabilitysummaryByResourceId("ag").getStatusCode() == HttpStatus.OK); - } - - /* @Test - public void getVulnerabilitysummaryByResourceIdTest_Exception() throws Exception { - - when(vulnerabilityService.getVulnerabilitysummaryByResourceId(anyString())).thenThrow(new Exception()); - assertTrue(vulnerabilityController.getVulnerabilitysummaryByResourceId("ag").getStatusCode() == HttpStatus.EXPECTATION_FAILED); - }*/ - - @Test - public void getVulnerabilityDetailsByResourceIdTest() throws Exception { - - when(vulnerabilityService.getVulnerabilityDetailsByResourceId(anyString())).thenReturn(new ArrayList<>()); - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(new ArrayList<>()); - - assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id","search",0,0).getStatusCode() == HttpStatus.OK); - assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id","search",null,null).getStatusCode() == HttpStatus.OK); - - List> resourceDetails = new ArrayList<>(); - resourceDetails.add(new HashMap<>()); - resourceDetails.add(new HashMap<>()); - - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(resourceDetails); - assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id","search",0,0).getStatusCode() == HttpStatus.OK); - - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(resourceDetails); - assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id","search",0,1).getStatusCode() == HttpStatus.OK); - - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(resourceDetails); - assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id","search",0,3).getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulnerabilityDetailsByResourceIdTest_Exception() throws Exception { - - List> resourceDetails = new ArrayList<>(); - resourceDetails.add(new HashMap<>()); - - when(vulnerabilityService.getVulnerabilityDetailsByResourceId(anyString())).thenReturn(new ArrayList<>()); - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenReturn(resourceDetails); - assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id","search",2,3).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - when(vulnerabilityService.filterMatchingCollectionElements(anyObject(),anyString(),anyBoolean())).thenThrow(new ServiceException()); - assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id","search",0,1).getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getVulnerabilityDistributionSummaryTest() throws Exception { - - when(vulnerabilityService.getVulnerabilityDistributionSummary(anyString(),anyString())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilityDistributionSummary("ag","sev").getStatusCode() == HttpStatus.OK); - } - - @Test - public void getAgingDistributionSummaryTest() throws Exception { - - when(vulnerabilityService.getAgingDistributionSummary(anyString(),anyString())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getAgingDistributionSummary("ag","sev").getStatusCode() == HttpStatus.OK); - } - - @Test - public void getAgingSummaryTest() throws Exception { - - when(vulnerabilityService.getAgingSummary(anyString())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getAgingSummary("ag").getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulnerabilityByQidTest() throws Exception { - - when(vulnerabilityService.getVulnerabilityByQid(anyString())).thenReturn(new ArrayList<>()); - assertTrue(vulnerabilityController.getVulnerabilityByQid("qid").getStatusCode() == HttpStatus.OK); - } - - @Test - public void getDistributionSummaryByVulnTypeTest() throws Exception { - - when(vulnerabilityService.getDistributionSummaryByVulnType(anyString(), anyString())).thenReturn(new ArrayList<>()); - - ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByVulnType("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.OK); - } - - @Test - public void getDistributionSummaryByVulnTypeTest_Exception() throws Exception { - - when(vulnerabilityService.getDistributionSummaryByVulnType(anyString(), anyString())).thenThrow(new DataException()); - - ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByVulnType("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getDistributionSummaryByInfraTypeTest() throws Exception { - - when(vulnerabilityService.getDistributionSummaryByInfraType(anyString(), anyString())).thenReturn(new ArrayList<>()); - - ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByInfraType("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.OK); - } - - @Test - public void getDistributionSummaryByInfraTypeTest_Exception() throws Exception { - - when(vulnerabilityService.getDistributionSummaryByInfraType(anyString(), anyString())).thenThrow(new ServiceException()); - - ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByInfraType("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getDistributionSummaryByEnvTest() throws Exception { - - when(vulnerabilityService.getDistributionSummaryByEnv(anyString(), anyString())).thenReturn(new ArrayList<>()); - - ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByEnv("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.OK); - } - - @Test - public void getDistributionSummaryByEnvTest_Exception() throws Exception { - - when(vulnerabilityService.getDistributionSummaryByEnv(anyString(), anyString())).thenThrow(new ServiceException()); - - ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByEnv("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getRemediationActionsSummaryTest() throws Exception { - - when(vulnerabilityService.getRemediationActionsSummary(anyString(), anyString())).thenReturn(new ArrayList<>()); - - ResponseEntity responseObj = vulnerabilityController.getRemediationActionsSummary("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.OK); - } - - @Test - public void getRemediationActionsSummaryTest_Exception() throws Exception { - - when(vulnerabilityService.getRemediationActionsSummary(anyString(), anyString())).thenThrow(new DataException()); - - ResponseEntity responseObj = vulnerabilityController.getRemediationActionsSummary("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } - - @Test - public void getHighestLowestPeformersTest() throws Exception { - - Map directorData = new HashMap<>(); - directorData.put("dir1", 1); - directorData.put("dir2", 2); - - when(vulnerabilityService.getHighestLowestPerformers(anyString(), anyString(),anyString())).thenReturn(directorData); - - ResponseEntity responseObj = vulnerabilityController.getHighestLowestPerformers("ag","3"); - assertTrue(responseObj.getStatusCode() == HttpStatus.OK); - - } - - @Test - public void getVulerabilityTrendTest() throws Exception { - - TrendRequest request = new TrendRequest(); - request.setAg("ag"); - - when(vulnerabilityService.getVulnerabilityNewOpenTrend(anyString(),anyString(),anyObject())).thenReturn(new ArrayList<>()); - - ResponseEntity responseObj = vulnerabilityController.getVulnerabilityTrend(request); - assertTrue(responseObj.getStatusCode() == HttpStatus.OK); - - request.setFrom(new Date()); - request.setFilter(new HashMap<>()); - - when(vulnerabilityService.getVulnerabilityNewOpenTrend(anyString(),anyString(),anyObject())).thenReturn(new ArrayList<>()); - - ResponseEntity response1Obj = vulnerabilityController.getVulnerabilityTrend(request); - assertTrue(response1Obj.getStatusCode() == HttpStatus.OK); - - Map filter = new HashMap<>(); - filter.put("severity","3"); - request.setFilter(filter); - when(vulnerabilityService.getVulnerabilityNewOpenTrend(anyString(),anyString(),anyObject())).thenReturn(new ArrayList<>()); - - ResponseEntity response2Obj = vulnerabilityController.getVulnerabilityTrend(request); - assertTrue(response2Obj.getStatusCode() == HttpStatus.OK); - } - - @Test - public void getVulerabilityTrendTest_Failure() throws Exception { - - TrendRequest request = new TrendRequest(); - - ResponseEntity responseObj = vulnerabilityController.getVulnerabilityTrend(request); - assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); - - request.setAg("ag"); - when(vulnerabilityService.getVulnerabilityNewOpenTrend(anyString(),anyString(),anyObject())).thenThrow(new Exception()); - - ResponseEntity response1 = vulnerabilityController.getVulnerabilityTrend(request); - assertTrue(response1.getStatusCode() == HttpStatus.EXPECTATION_FAILED); - } -} diff --git a/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/service/IssueTrendServiceImplTest.java b/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/service/IssueTrendServiceImplTest.java index b396303dc..e3a721a5c 100644 --- a/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/service/IssueTrendServiceImplTest.java +++ b/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/service/IssueTrendServiceImplTest.java @@ -56,9 +56,6 @@ public class IssueTrendServiceImplTest { @Mock private ComplianceService complianceService; - @Mock - private VulnerabilityService vulnService; - Request request = new Request(); UntaggedTargetTypeRequest untaggedTargetTypeRequest = new UntaggedTargetTypeRequest(); @@ -99,11 +96,6 @@ public void getTrendProgressTest() throws Exception { when(complianceService.getCertificates(anyString())) .thenReturn(taggingInfoMap); - when(vulnService.getVulnerabilitySummary(anyString(),anyString())) - .thenReturn(vulnInfoMap); - complianceService - .getRulecompliance(request); - when(complianceService.getRulecompliance(anyObject())) .thenReturn(CommonTestUtil.getResponseWithOrder()); diff --git a/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/service/VulnerabilityServiceTest.java b/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/service/VulnerabilityServiceTest.java deleted file mode 100644 index 5dbe6ce1a..000000000 --- a/api/pacman-api-compliance/src/test/java/com/tmobile/pacman/api/compliance/service/VulnerabilityServiceTest.java +++ /dev/null @@ -1,713 +0,0 @@ -/******************************************************************************* - * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - ******************************************************************************/ -package com.tmobile.pacman.api.compliance.service; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.powermock.api.mockito.PowerMockito.when; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.powermock.modules.junit4.PowerMockRunner; -import org.springframework.test.util.ReflectionTestUtils; - -import com.tmobile.pacman.api.commons.Constants; -import com.tmobile.pacman.api.commons.exception.DataException; -import com.tmobile.pacman.api.commons.exception.ServiceException; -import com.tmobile.pacman.api.commons.utils.CommonUtils; -import com.tmobile.pacman.api.compliance.client.AssetServiceClient; -import com.tmobile.pacman.api.compliance.domain.AssetApi; -import com.tmobile.pacman.api.compliance.domain.AssetApiData; -import com.tmobile.pacman.api.compliance.domain.AssetCount; -import com.tmobile.pacman.api.compliance.domain.AssetCountByAppEnvDTO; -import com.tmobile.pacman.api.compliance.domain.AssetCountDTO; -import com.tmobile.pacman.api.compliance.domain.AssetCountData; -import com.tmobile.pacman.api.compliance.domain.Request; -import com.tmobile.pacman.api.compliance.domain.ResponseWithOrder; -import com.tmobile.pacman.api.compliance.repository.VulnerabilityRepository; -import com.tmobile.pacman.api.compliance.repository.VulnerabilityTrendGenerator; - -@RunWith(PowerMockRunner.class) -public class VulnerabilityServiceTest { - - @InjectMocks - VulnerabilityService vulnerabilityService; - - @Mock - VulnerabilityRepository vulnerabilityRepository; - - @Mock - AssetServiceClient assetServiceClient; - - @Mock - VulnerabilityTrendGenerator vulnTrendGenerator; - - @Mock - ComplianceService complianceService; - - @Mock - CommonUtils commonUtils; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - AssetApi assetApi = new AssetApi(); - AssetApiData data = new AssetApiData(); - - List ttypes = new ArrayList<>(); - AssetCountDTO tt = new AssetCountDTO(); - tt.setType("ec2"); - ttypes.add(tt); - tt = new AssetCountDTO(); - tt.setType("onpremserver"); - ttypes.add(tt); - - data.setTargettypes(ttypes.toArray(new AssetCountDTO[ttypes.size()])); - assetApi.setData(data); - - when(assetServiceClient.getTargetTypeList(anyString(),anyObject())).thenReturn(assetApi); - } - - @Test - public void getVulnerabilitiesDetailsTest() throws Exception { - - List> vulnerabilitiesData = new ArrayList<>(); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getAssetsAffectedCount(anyString(), - anyObject(), anyString())).thenReturn(new HashMap<>()); - when (vulnerabilityRepository.getAllVulnerabilities(anyObject())).thenReturn(vulnerabilitiesData); - assertThat(vulnerabilityService.getVulnerabilitiesDetails("ag", null), - is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); - assertThat(vulnerabilityService.getVulnerabilitiesDetails("ag", null), - is(notNullValue())); - - Map vuln = new HashMap<>(); - vuln.put("qid", "123"); - vuln.put("assetsAffected", 1); - vuln.put(Constants.TITLE, "test"); - vuln.put(Constants.SEVEITY_LEVEL, "3"); - vuln.put(Constants.CATEGORY, "test"); - vuln.put(Constants.VULN_TYPE, "type"); - vuln.put(Constants.PATCHABLE, "1"); - vulnerabilitiesData.add(vuln); - - vuln = new HashMap<>(); - vuln.put("qid", "456"); - vuln.put("assetsAffected", 2); - vuln.put(Constants.TITLE, "test"); - vuln.put(Constants.SEVEITY_LEVEL, "5"); - vuln.put(Constants.CATEGORY, "test"); - vuln.put(Constants.VULN_TYPE, "type"); - vuln.put(Constants.PATCHABLE, "0"); - vulnerabilitiesData.add(vuln); - - vuln = new HashMap<>(); - vuln.put("qid", "789"); - vuln.put("assetsAffected", 2); - vuln.put(Constants.TITLE, "test"); - vuln.put(Constants.SEVEITY_LEVEL, "5"); - vuln.put(Constants.CATEGORY, "test"); - vuln.put(Constants.VULN_TYPE, "type"); - vulnerabilitiesData.add(vuln); - - Map assetsAffected = new HashMap(); - assetsAffected.put("123", 10L); - assetsAffected.put("456", 10L); - assetsAffected.put("789", 10L); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); - when(vulnerabilityRepository.getAssetsAffectedCount(anyString(), - anyObject(), anyString())).thenReturn(assetsAffected); - when (vulnerabilityRepository.getAllVulnerabilities(anyObject())).thenReturn(vulnerabilitiesData); - assertThat(vulnerabilityService.getVulnerabilitiesDetails("ag", null), - is(notNullValue())); - } - - @Test - public void getVulnerabilitiesDetailsTest_Exception() throws Exception { - - when(vulnerabilityRepository.getAssetsAffectedCount(anyString(), - anyObject(), anyString())).thenReturn(new HashMap<>()); - when (vulnerabilityRepository.getAllVulnerabilities(anyObject())).thenThrow(new DataException()); - assertThatThrownBy( - () -> vulnerabilityService.getVulnerabilitiesDetails("ag", null)).isInstanceOf(Exception.class); - } - - @Test - public void getVulnerabilitySummaryTest() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); - assertThat(vulnerabilityService.getVulnerabilitySummary("ag","3,4,5"), - is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getUniqueHost(anyString(),anyString())).thenReturn(new HashMap()); - when(vulnerabilityRepository.getVulnInfo(anyString(),anyString())).thenReturn(new HashMap()); - when(vulnerabilityRepository.getUniqueApp(anyString())).thenReturn(new HashMap()); - - AssetCount totalAssets = new AssetCount(); - AssetCountData data = new AssetCountData(); - - AssetCountByAppEnvDTO assetCount_Count = new AssetCountByAppEnvDTO(); - assetCount_Count.setType("onpremserver"); - assetCount_Count.setCount("1"); - - List assetAppEnvDTOs = new ArrayList(); - assetAppEnvDTOs.add(assetCount_Count); - data.setAssetcount(assetAppEnvDTOs.toArray(new AssetCountByAppEnvDTO[assetAppEnvDTOs.size()])); - totalAssets.setData(data); - - ResponseWithOrder responseWithOrder = new ResponseWithOrder(); - List> response = new ArrayList<>(); - LinkedHashMap obj = new LinkedHashMap<>(); - obj.put("assetsScanned", 1); - obj.put("passed", 1); - response.add(obj); - responseWithOrder.setResponse(response ); - when(complianceService.getRulecompliance(any(Request.class))).thenReturn(responseWithOrder); - when(vulnerabilityRepository.getTotalQualysHostCount(anyString(), anyString())).thenReturn(1L); - ReflectionTestUtils.setField(vulnerabilityService, "vulnSummarySeverity", "3"); - - Map vulnSummary = new HashMap<>(); - List> severityInfo = new ArrayList<>(); - Map severity = new HashMap<>(); - severity.put(Constants.SEVEITY_LEVEL, 3); - severity.put(Constants.COUNT, 2); - severity.put(Constants.VULN_COUNT, 2); - severityInfo.add(severity); - severity = new HashMap<>(); - severity.put(Constants.SEVEITY_LEVEL, 4); - severity.put(Constants.COUNT, 2); - severity.put(Constants.VULN_COUNT, 2); - severityInfo.add(severity); - severity = new HashMap<>(); - severity.put(Constants.SEVEITY_LEVEL, 5); - severity.put(Constants.COUNT, 2); - severity.put(Constants.VULN_COUNT, 2); - severityInfo.add(severity); - - vulnSummary.put("severityInfo", severityInfo); - assertThat(vulnerabilityService.getVulnerabilitySummary("ag","3,4,5"),is(notNullValue())); - - - Map uniqueHost = new HashMap<>(); - uniqueHost.put("total", 10); - uniqueHost.put("3", 1); - Map vulnInfo = new HashMap<>(); - Map vulnInfoMap = new HashMap<>(); - vulnInfoMap.put(Constants.VULN_COUNT,2); - vulnInfoMap.put(Constants.UNIQUE_VULN_COUNT,2); - vulnInfo.put("total", 10); - vulnInfo.put("3", vulnInfoMap); - Map uniqueApp = new HashMap<>(); - uniqueApp.put("3", 1); - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); - when(vulnerabilityRepository.getUniqueHost(anyString(),anyString())).thenReturn(uniqueHost); - when(vulnerabilityRepository.getVulnInfo(anyString(),anyString())).thenReturn(vulnInfo); - when(vulnerabilityRepository.getUniqueApp(anyString())).thenReturn(uniqueApp); - - when(complianceService.getRulecompliance(any(Request.class))).thenReturn(responseWithOrder); - when(assetServiceClient.getTotalAssetsCount(anyString(), anyString(), anyString())).thenReturn(totalAssets); - when(vulnerabilityRepository.getTotalQualysHostCount(anyString(), anyString())).thenReturn(1L); - ReflectionTestUtils.setField(vulnerabilityService, "vulnSummarySeverity", "3"); - - vulnSummary.put("severityInfo", severityInfo); - - assertThat(vulnerabilityService.getVulnerabilitySummary("ag","3"),is(notNullValue())); - - } - - @Test - public void getVulnerabilitySummaryTest_Exception() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getUniqueHost(anyString(),anyString())).thenReturn(new HashMap()); - when(vulnerabilityRepository.getVulnInfo(anyString(),anyString())).thenReturn(new HashMap()); - when(vulnerabilityRepository.getUniqueApp(anyString())).thenReturn(new HashMap()); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnSummarySeverity", "3"); - when(complianceService.getRulecompliance(any(Request.class))).thenThrow(new ServiceException()); - assertThatThrownBy( - () -> vulnerabilityService.getVulnerabilitySummary("ag","3,4,5")).isInstanceOf(ServiceException.class); - } - - @Test - public void getVulnerabilityByAppAndEnvTest() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(anyString(), anyObject(), - anyString(), anyString(), anyString())).thenReturn(new ArrayList<>()); - assertThat(vulnerabilityService.getVulnerabilityByAppAndEnv("ag","filter","app"), - is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); - assertThat(vulnerabilityService.getVulnerabilityByAppAndEnv("ag","filter","app").size(), - is(0)); - } - - @Test - public void getVulnerabilityTrendTest() throws Exception { - - when(vulnerabilityRepository.getVulnerabilityTrend(anyString(), - anyObject(), anyObject(), anyObject())).thenReturn(new ArrayList<>()); - assertThat(vulnerabilityService.getVulnerabilityTrend("ag",null,new Date(),new Date()), - is(notNullValue())); - } - - @Test - public void getVulnerabilityNewOpenTrendTest() throws Exception { - - when(vulnTrendGenerator.generateTrend(anyString(), - anyString(), anyObject())).thenReturn(new ArrayList<>()); - assertThat(vulnerabilityService.getVulnerabilityNewOpenTrend("ag","sev",new Date()), - is(notNullValue())); - } - - @Test - public void getVulnerabilitiesDistributionTest() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getVulnerabilitiesDistribution(anyString(),anyString())).thenReturn(new ArrayList<>()); - assertThat(vulnerabilityService.getVulnerabilitiesDistribution("ag"), - is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); - assertThat(vulnerabilityService.getVulnerabilitiesDistribution("ag").size(), - is(0)); - } - - /*@SuppressWarnings("static-access") - @Test - public void filterMatchingCollectionElementsTest() throws Exception { - - when(commonUtils.filterMatchingCollectionElements(anyObject(), - anyObject(), anyObject())).thenReturn(new Object()); - assertThat(vulnerabilityService.filterMatchingCollectionElements(new ArrayList<>(),"sev",true), - is(notNullValue())); - }*/ - - @Test - public void getVulnerabilitysummaryByResourceIdTest() throws Exception { - - when(vulnerabilityRepository.getVulnerabilitysummaryByResourceId(anyString())).thenReturn(new HashMap<>()); - assertThat(vulnerabilityService.getVulnerabilitysummaryByResourceId("id"), - is(notNullValue())); - } - - @Test - public void getVulnerabilityDetailsByResourceIdTest() throws Exception { - - List> vulnerabilitiesData = new ArrayList<>(); - - Map vuln = new HashMap<>(); - vuln.put("qid", "123"); - vuln.put("assetsAffected", 1); - vuln.put(Constants.TITLE, "test"); - vuln.put(Constants.SEVEITY_LEVEL, "3"); - vuln.put(Constants.CATEGORY, "test"); - vuln.put(Constants.VULN_TYPE, "type"); - vuln.put(Constants.PATCHABLE, "1"); - vulnerabilitiesData.add(vuln); - - when(vulnerabilityRepository.getVulnerabilityDetailsByResourceId(anyString())).thenReturn(vulnerabilitiesData); - assertThat(vulnerabilityService.getVulnerabilityDetailsByResourceId("id"), - is(notNullValue())); - - vuln = new HashMap<>(); - vuln.put("qid", "123"); - vuln.put("assetsAffected", 1); - vulnerabilitiesData.add(vuln); - - when(vulnerabilityRepository.getVulnerabilityDetailsByResourceId(anyString())).thenReturn(vulnerabilitiesData); - assertThatThrownBy( - () -> vulnerabilityService.getVulnerabilityDetailsByResourceId("id")).isInstanceOf(Exception.class); - } - - @Test - public void getVulnerabilityDistributionSummaryTest() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(anyString(), - anyString(), anyString(), anyString(),anyString())).thenReturn(getApps()); - when(vulnerabilityRepository.fetchExecDirectorApps()).thenReturn(fetchExecDirectorApps()); - - assertThat(vulnerabilityService.getVulnerabilityDistributionSummary("ag","3"), - is(notNullValue())); - assertThat(vulnerabilityService.getVulnerabilityDistributionSummary("ag",null), - is(notNullValue())); - } - - @Test - public void getAgingSummaryTest() throws Exception { - - when(vulnerabilityRepository.getAgingSummary(anyString())).thenReturn(new ArrayList<>()); - assertThat(vulnerabilityService.getAgingSummary("ag"), - is(notNullValue())); - } - - @Test - public void getAgingDistributionSummaryTest() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getAgingByApplication(anyString(),anyString(),anyString())).thenReturn(getApps()); - when(vulnerabilityRepository.fetchExecDirectorApps()).thenReturn(fetchExecDirectorApps()); - - assertThat(vulnerabilityService.getAgingDistributionSummary("ag","3"), - is(notNullValue())); - assertThat(vulnerabilityService.getAgingDistributionSummary("ag",null), - is(notNullValue())); - } - - @Test - public void getAgingDistributionSummaryTest_Exception() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); - when(vulnerabilityRepository.getAgingByApplication(anyString(),anyString(),anyString())).thenReturn(getApps()); - when(vulnerabilityRepository.fetchExecDirectorApps()).thenReturn(fetchExecDirectorApps()); - assertThat(vulnerabilityService.getAgingDistributionSummary("ag","3"), - is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getAgingByApplication(anyString(),anyString(),anyString())).thenThrow(new Exception()); - assertThat(vulnerabilityService.getAgingDistributionSummary("ag","3"), - is(notNullValue())); - } - - @Test - public void getVulnerabilityByQidTest() throws Exception { - - Map vuln = new HashMap<>(); - vuln.put("qid", "123"); - vuln.put("vulntype", "type"); - vuln.put("severitylevel", "3"); - vuln.put("title", "test"); - vuln.put("category", ""); - vuln.put("lastservicemodificationdatetime", "1234"); - vuln.put("publisheddatetime", "123"); - vuln.put("patchable", "1"); - Map softwarelist = new HashMap<>(); - List> softwares = new ArrayList<>(); - Map innerMap = new HashMap<>(); - innerMap.put("test","test"); - innerMap.put("test", "test"); - softwares.add(innerMap); - softwarelist.put("software", softwares); - vuln.put("softwarelist", softwarelist); - Map vendorreferencelist = new HashMap<>(); - List> vendorreference = new ArrayList<>(); - vendorreference.add(innerMap); - vendorreferencelist.put("vendorreference", vendorreference); - vuln.put("vendorreferencelist", vendorreferencelist); - vuln.put("diagnosis", "test"); - vuln.put("consequence", "test"); - vuln.put("solution", "test"); - Map bugtraqlist = new HashMap<>(); - List> bugtraq = new ArrayList<>(); - bugtraq.add(innerMap); - bugtraqlist.put("vendorreference", bugtraq); - vuln.put("bugtraqlist", bugtraqlist); - vuln.put("pciflag", 0); - Map pcireasons = new HashMap<>(); - List> pcireason = new ArrayList<>(); - pcireason.add(innerMap); - pcireasons.put("pcireason", pcireason); - vuln.put("pcireasons", pcireasons); - Map authtypelist = new HashMap<>(); - List> authtype = new ArrayList<>(); - authtype.add(innerMap); - authtypelist.put("authtype", authtype); - Map discovery = new HashMap<>(); - discovery.put("authtypelist", authtypelist); - discovery.put("additionalinfo", "Patch Available"); - vuln.put("discovery", discovery); - vuln.put("supportedmodules", "test"); - Map cvelist = new HashMap<>(); - List> cve = new ArrayList<>(); - cve.add(innerMap); - cvelist.put("cve", cve); - vuln.put("cvelist", cvelist); - Map cvss = new HashMap<>(); - cvss.put("base", 1); - cvss.put("temporal", 1); - vuln.put("cvssv3", cvss); - Map access = new HashMap<>(); - access.put("vector", 1); - cvss.put("access", access); - vuln.put("cvss", cvss); - - when(vulnerabilityRepository.getVulnerabilityByQid(anyString())).thenReturn(vuln); - assertThat(vulnerabilityService.getVulnerabilityByQid("id"), - is(notNullValue())); - - vuln = new HashMap<>(); - discovery = new HashMap<>(); - discovery.put("additionalinfo", "test"); - vuln.put("discovery", discovery); - vuln.put("pciflag", 1); - - when(vulnerabilityRepository.getVulnerabilityByQid(anyString())).thenReturn(vuln); - assertThat(vulnerabilityService.getVulnerabilityByQid("id"), - is(notNullValue())); - - when(vulnerabilityRepository.getVulnerabilityByQid(anyString())).thenReturn(new HashMap<>()); - assertThat(vulnerabilityService.getVulnerabilityByQid("id"), - is(notNullValue())); - } - - @Test - public void getHighestLowestPerformersTest() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); - when(vulnerabilityRepository.fetchExecDirectorApps()).thenReturn(fetchExecDirectorApps()); - assertThat(vulnerabilityService.getHighestLowestPerformers("ag", null,"org"),is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getAppsBySeverity(anyString(),anyString(),anyString())).thenReturn(new HashMap<>()); - when(vulnerabilityRepository.fetchExecDirectorApps()).thenReturn(fetchExecDirectorApps()); - assertThat(vulnerabilityService.getHighestLowestPerformers("ag", null,"org"),is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); - Map apps = new HashMap<>(); - apps.put("app1", 1L); - apps.put("app2", 2L); - apps.put("app3", 3L); - when(vulnerabilityRepository.getAppsBySeverity(anyString(),anyString(),anyString())).thenReturn(apps); - when(vulnerabilityRepository.fetchExecDirectorApps()).thenReturn(fetchExecDirectorApps()); - assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3","org"),is(notNullValue())); - - } - - @Test - public void getHighestLowestPerformersTest_Exception() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getAppsBySeverity(anyString(),anyString(),anyString())).thenThrow(new Exception()); - assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3","org"),is(notNullValue())); - } - - @Test - public void getDistributionSummaryByInfraTypeTest() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); - Map infraInfo = new HashMap<>(); - infraInfo.put(Constants.TOTAL_VULN_ASSETS,1); - infraInfo.put(Constants.VULNEREBILITIES,1); - infraInfo.put(Constants.UNIQUE_VULN_COUNT,1); - when(vulnerabilityRepository.getDistributionSummaryByInfraType(anyString(),anyString(),anyString())).thenReturn(infraInfo); - assertThat(vulnerabilityService.getDistributionSummaryByInfraType("ag",null),is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getDistributionSummaryByInfraType(anyString(),anyString(),anyString())).thenReturn(infraInfo); - assertThat(vulnerabilityService.getDistributionSummaryByInfraType("ag","3"),is(notNullValue())); - } - - @Test - public void getDistributionSummaryByInfraTypeTest_Exception() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - when(vulnerabilityRepository.getDistributionSummaryByInfraType(anyString(),anyString(),anyString())).thenThrow(new DataException()); - assertThatThrownBy( - () -> vulnerabilityService.getDistributionSummaryByInfraType("ag","3")).isInstanceOf(ServiceException.class); - } - - @Test - public void getDistributionSummaryByEnvTest() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); - - Map prodInfo = new HashMap<>(); - prodInfo.put("totalVulnerableAssets", 1L); - prodInfo.put(Constants.VULNEREBILITIES, 1L); - prodInfo.put("uniqueVulnCount", 1L); - - Map nonProdInfo = new HashMap<>(); - nonProdInfo.put("totalVulnerableAssets", 1L); - nonProdInfo.put(Constants.VULNEREBILITIES, 1L); - nonProdInfo.put("uniqueVulnCount", 1L); - - when(vulnerabilityRepository.getProdInfoByEnv(anyString(), anyString())).thenReturn(prodInfo); - when(vulnerabilityRepository.getNonProdInfoByEnv(anyString(), anyString())).thenReturn(nonProdInfo); - assertThat(vulnerabilityService.getDistributionSummaryByEnv("ag","3"),is(notNullValue())); - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - - prodInfo = new HashMap<>(); - prodInfo.put("totalVulnerableAssets", 0L); - prodInfo.put(Constants.VULNEREBILITIES, 0L); - prodInfo.put("uniqueVulnCount", 0L); - - nonProdInfo = new HashMap<>(); - nonProdInfo.put("totalVulnerableAssets", 0L); - nonProdInfo.put(Constants.VULNEREBILITIES, 0L); - nonProdInfo.put("uniqueVulnCount", 0L); - - when(vulnerabilityRepository.getProdInfoByEnv(anyString(), anyString())).thenReturn(prodInfo); - when(vulnerabilityRepository.getNonProdInfoByEnv(anyString(), anyString())).thenReturn(nonProdInfo); - assertThat(vulnerabilityService.getDistributionSummaryByEnv("ag",""),is(notNullValue())); - } - - @Test - public void getDistributionSummaryByEnvTest_Exception() throws Exception { - - ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); - Map prodInfo = new HashMap<>(); - prodInfo.put("totalVulnerableAssets", 1L); - prodInfo.put("uniqueVulnCount", 1L); - - Map nonProdInfo = new HashMap<>(); - nonProdInfo.put("totalVulnerableAssets", 1L); - nonProdInfo.put(Constants.VULNEREBILITIES, 1L); - nonProdInfo.put("uniqueVulnCount", 1L); - - when(vulnerabilityRepository.getProdInfoByEnv(anyString(), anyString())).thenReturn(prodInfo); - when(vulnerabilityRepository.getNonProdInfoByEnv(anyString(), anyString())).thenReturn(nonProdInfo); - assertThatThrownBy( - () -> vulnerabilityService.getDistributionSummaryByEnv("ag","3")).isInstanceOf(ServiceException.class); - } - - @Test - public void getDistributionSummaryByVulnTypeTest() throws Exception { - - when(vulnerabilityRepository.getDistributionSummaryByVulnType(anyString(), anyString())).thenReturn(new ArrayList<>()); - assertThat(vulnerabilityService.getDistributionSummaryByVulnType("ag","3"),is(notNullValue())); - - when(vulnerabilityRepository.getDistributionSummaryByVulnType(anyString(), anyString())).thenReturn(new ArrayList<>()); - assertThat(vulnerabilityService.getDistributionSummaryByVulnType("ag",null),is(notNullValue())); - } - - @Test - public void getRemediationActionsSummaryTest() throws Exception { - - Map qids = new HashMap<>(); - qids.put("123~title~OS", 2); - qids.put("123~EOL/Obsolete~Infra", 2); - qids.put("11925~title~Infra", 2); - qids.put("370914~title~Infra", 2); - qids.put("123~Java Debug Wire Protocol~Infra", 0); - qids.put("123~Java JMX Server Insecure Configuration~Infra", 2); - qids.put("123~Java~Infra", 2); - qids.put("123~title~Infra", 2); - when(vulnerabilityRepository.getAllQidByAG(anyString(),anyString())).thenReturn(qids); - assertThat(vulnerabilityService.getRemediationActionsSummary("ag",null),is(notNullValue())); - } - - private List> getApps() { - - List> vulnApplications = new ArrayList<>(); - List> severityInfo = new ArrayList<>(); - - Map severity = new HashMap<>(); - severity.put(Constants.SEVERITY, "S3"); - severity.put(Constants.COUNT, 3); - severity.put("days", 3); - severityInfo.add(severity); - severity = new HashMap<>(); - severity.put(Constants.SEVERITY, "S4"); - severity.put(Constants.COUNT, 4); - severity.put("days", 4); - severityInfo.add(severity); - severity = new HashMap<>(); - severity.put(Constants.SEVERITY, "S5"); - severity.put(Constants.COUNT, 5); - severity.put("days", 5); - severityInfo.add(severity); - - Map vulnApp = new HashMap<>(); - vulnApp.put("application", "app1"); - vulnApp.put(Constants.SEV_INFO, severityInfo); - vulnApplications.add(vulnApp); - - vulnApp = new HashMap<>(); - vulnApp.put("application", "app2"); - vulnApp.put(Constants.SEV_INFO, severityInfo); - vulnApplications.add(vulnApp); - - vulnApp = new HashMap<>(); - vulnApp.put("application", "app3"); - vulnApp.put(Constants.SEV_INFO, severityInfo); - vulnApplications.add(vulnApp); - return vulnApplications; - } - - private List> fetchExecDirectorApps() { - - List> apps = new ArrayList<>(); - Map app = new HashMap<>(); - app.put("appTag", "app1"); - app.put("director", "director1"); - app.put("executiveSponsor", "executiveSponsor1"); - apps.add(app); - - app = new HashMap<>(); - app.put("appTag", "app1"); - app.put("director", "director3"); - app.put("executiveSponsor", "executiveSponsor3"); - apps.add(app); - - app = new HashMap<>(); - app.put("appTag", "app2"); - app.put("director", "director2"); - app.put("executiveSponsor", "executiveSponsor2"); - apps.add(app); - - app = new HashMap<>(); - app.put("appTag", "app2"); - app.put("director", ""); - app.put("executiveSponsor", ""); - apps.add(app); - - app = new HashMap<>(); - app.put("appTag", "app3"); - app.put("director", "director3"); - app.put("executiveSponsor", "executiveSponsor3"); - apps.add(app); - - app = new HashMap<>(); - app.put("appTag", "app3"); - app.put("director", "director1"); - app.put("executiveSponsor", "executiveSponsor1"); - apps.add(app); - - app = new HashMap<>(); - app.put("appTag", "app3"); - app.put("director", null); - app.put("executiveSponsor", null); - apps.add(app); - - return apps; - } - -} diff --git a/api/pacman-api-config/src/main/resources/shared/api.yml b/api/pacman-api-config/src/main/resources/shared/api.yml index c1f8cfa87..e8ba71d86 100644 --- a/api/pacman-api-config/src/main/resources/shared/api.yml +++ b/api/pacman-api-config/src/main/resources/shared/api.yml @@ -8,6 +8,8 @@ service: devstandards: ${PACMAN_HOST_NAME}/api/devstandards auth: ${PACMAN_HOST_NAME}/api/auth admin: ${PACMAN_HOST_NAME}/api/admin + notifications: ${PACMAN_HOST_NAME}/api/notifications + vulnerability: ${PACMAN_HOST_NAME}/api/vulnerability endpoints: refresh: @@ -27,7 +29,7 @@ application: domains: all monitoring: - contextRootNames: asset,compliance,statistics,auth,admin + contextRootNames: asset,compliance,statistics,auth,admin,notifications,vulnerability auth: active: db @@ -66,6 +68,9 @@ api: - name: Notification Service url: ${PACMAN_HOST_NAME:http://localhost:8080}/api/notifications/v2/api-docs version: 2.0 + - name: Vulnerability Service + url: ${PACMAN_HOST_NAME:http://localhost:8080}/api/vulnerability/v2/api-docs + version: 2.0 tagging: mandatoryTags: Application,Environment diff --git a/api/pacman-api-config/src/main/resources/shared/vulnerability-service.yml b/api/pacman-api-config/src/main/resources/shared/vulnerability-service.yml new file mode 100644 index 000000000..9a4ea45fb --- /dev/null +++ b/api/pacman-api-config/src/main/resources/shared/vulnerability-service.yml @@ -0,0 +1,3 @@ +server: + servlet: + context-path: /api/vulnerability \ No newline at end of file diff --git a/api/pacman-api-vulnerability/pom.xml b/api/pacman-api-vulnerability/pom.xml new file mode 100644 index 000000000..aa225242d --- /dev/null +++ b/api/pacman-api-vulnerability/pom.xml @@ -0,0 +1,265 @@ + + + 4.0.0 + + com.tmobile.pacman + vulnerability-service + 1.0-SNAPSHOT + jar + vulnerability-service + + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + 2.0.2 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.8 + 1.6.4 + -Xdoclint:none + + + + + + org.springframework.cloud + spring-cloud-dependencies + Finchley.RELEASE + pom + import + + + de.codecentric + spring-boot-admin-dependencies + ${spring-boot-admin.version} + pom + import + + + + + + + com.tmobile.cloud + api-commons + 1.0.1-SNAPSHOT + + + + io.springfox + springfox-swagger2 + 2.7.0 + + + + io.springfox + springfox-swagger-ui + 2.7.0 + + + + io.springfox + springfox-bean-validators + 2.7.0 + + + + de.codecentric + spring-boot-admin-starter-client + + + + org.springframework.cloud + spring-cloud-starter-security + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework.cloud + spring-cloud-openfeign-core + + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.jayway.jsonpath + json-path + 2.2.0 + test + + + + org.elasticsearch.client + rest + 5.5.3 + + + + com.google.code.gson + gson + 2.8.1 + + + + com.google.guava + guava + 19.0 + + + + org.elasticsearch + elasticsearch + 5.6.2 + + + + org.elasticsearch.client + transport + 5.6.2 + + + + org.apache.logging.log4j + log4j-api + 2.9.0 + + + org.apache.logging.log4j + log4j-core + 2.9.0 + + + + org.springframework.boot + spring-boot-starter-cache + + + + com.github.ben-manes.caffeine + caffeine + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.javassist + javassist + 3.18.1-GA + + + + commons-validator + commons-validator + 1.4.0 + + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + + org.mockito + mockito-core + 1.10.19 + test + + + + ch.qos.logback.contrib + logback-jackson + 0.1.5 + + + + ch.qos.logback.contrib + logback-json-classic + 0.1.5 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + build-info + + + + + pacman-api-vulnerability + true + + + + org.jacoco + jacoco-maven-plugin + 0.7.6.201602180812 + + + + prepare-agent + + + + report + test + + report + + + + + + + + diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/RefreshScopeConfig.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/RefreshScopeConfig.java new file mode 100644 index 000000000..37b8b4725 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/RefreshScopeConfig.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/* + * + */ +package com.tmobile.pacman.api.vulnerability; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Configuration; + + +/** + * The Class RefreshScopeConfig. + */ +@Configuration +public class RefreshScopeConfig implements BeanFactoryPostProcessor { + + /* (non-Javadoc) + * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) + */ + + /** + * Overriding to update the test scope. + * + */ + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) { + for (String beanName : factory.getBeanDefinitionNames()) { + BeanDefinition beanDef = factory.getBeanDefinition(beanName); + if (beanDef.getBeanClassName() != null && beanDef.getBeanClassName().startsWith("com.tmobile.pacman")) { + beanDef.setScope("refresh"); + } + } + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/VulnerabilityApplication.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/VulnerabilityApplication.java new file mode 100644 index 000000000..6f54d6446 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/VulnerabilityApplication.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/* + * + */ +package com.tmobile.pacman.api.vulnerability; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; + + +/** + * The Class VulnerabilityApplication. + */ +@SpringBootApplication +@EnableFeignClients +@Configuration +@EnableResourceServer +@ComponentScan(basePackages = "com.tmobile.pacman") +public class VulnerabilityApplication { + + /** + * The main method. + * + * @param args the arguments + */ + public static void main(String[] args) { + SpringApplication.run(VulnerabilityApplication.class, args); + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/client/AssetServiceClient.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/client/AssetServiceClient.java new file mode 100644 index 000000000..e5ed738a2 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/client/AssetServiceClient.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Oct 26, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.client; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import com.tmobile.pacman.api.vulnerability.domain.AssetApi; +import com.tmobile.pacman.api.vulnerability.domain.AssetCount; + +/** + * The Interface AssetServiceClient. + */ +//@FeignClient(name = "assetclient", url = "${service.url.asset}") +@FeignClient(name = "assetclient", url = "https://stg.pacbot.t-mobile.com/api/asset") +public interface AssetServiceClient { + + /** + * Gets the total assets count. + * + * @param assetGroup the asset group + * @param targetType the target type + * @param domain the domain + * @return AssetCount + */ + @RequestMapping(method = RequestMethod.GET, value = "/v1/count") + AssetCount getTotalAssetsCount(@RequestParam("ag") String assetGroup, + @RequestParam("type") String targetType, + @RequestParam("domain") String domain); + + /** + * Gets the applications list. + * + * @param assetGroup the asset group + * @param domain the domain + * @return AssetApi + */ + @RequestMapping(method = RequestMethod.GET, value = "/v1/list/application") + AssetApi getApplicationsList(@RequestParam("ag") String assetGroup, + @RequestParam("domain") String domain); + + /** + * Gets the environment list. + * + * @param assetGroup the asset group + * @param application the application + * @param domain the domain + * @return AssetApi + */ + @RequestMapping(method = RequestMethod.GET, value = "/v1/list/environment") + AssetApi getEnvironmentList(@RequestParam("ag") String assetGroup, + @RequestParam("application") String application, + @RequestParam("domain") String domain); + + /** + * Gets the target type list. + * + * @param assetGroup the asset group + * @param domain the domain + * @return AssetApi + */ + @RequestMapping(method = RequestMethod.GET, value = "/v1/list/targettype") + AssetApi getTargetTypeList(@RequestParam("ag") String assetGroup, + @RequestParam("domain") String domain); + + /** + * Gets the total assets count by application. + * + * @param assetGroup the asset group + * @param targetType the target type + * @return AssetCount + */ + @RequestMapping(method = RequestMethod.GET, value = "/v1/count/byapplication") + AssetCount getTotalAssetsCountByApplication( + @RequestParam("ag") String assetGroup, + @RequestParam("type") String targetType); + + /** + * Gets the total assets count by environment. + * + * @param assetGroup the asset group + * @param application the application + * @param targetType the target type + * @return AssetCount + */ + @RequestMapping(method = RequestMethod.GET, value = "/v1/count/byenvironment") + AssetCount getTotalAssetsCountByEnvironment( + @RequestParam("ag") String assetGroup, + @RequestParam("application") String application, + @RequestParam("type") String targetType); + + /** + * Gets the asset group info. + * + * @param assetGroup the asset group + * @return AssetApi + */ + @RequestMapping(method = RequestMethod.GET, value = "/v1/assetgroup") + AssetApi getAssetGroupInfo(@RequestParam("ag") String assetGroup); + + /** + * Gets the target type list by domain. + * + * @param assetGroup the asset group + * @param domain the domain + * @return AssetApi + */ + @RequestMapping(method = RequestMethod.GET, value = "/v1/list/targettype") + AssetApi getTargetTypeListByDomain(@RequestParam("ag") String assetGroup, + @RequestParam("domain") String domain); +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/client/ComplianceServiceClient.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/client/ComplianceServiceClient.java new file mode 100644 index 000000000..f2c18a55c --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/client/ComplianceServiceClient.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.client; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import com.tmobile.pacman.api.vulnerability.domain.Request; + +//import com.tmobile.pacman.api.compliance.domain.Request; + +/** + * The Interface ComplianceServiceClient. + */ +//@FeignClient(name = "compliance", url = "${service.url.compliance}") +@FeignClient(name = "assetclient", url = "https://stg.pacbot.t-mobile.com/api/compliance") +public interface ComplianceServiceClient { + + /** + * Gets the total issues. + * + * @param assetGroup the asset group + * @return the total issues + */ + @RequestMapping(path = "/v1/noncompliancepolicy", method = RequestMethod.POST) + public ResponseEntity getNonCompliancePolicyByRule(@RequestBody(required = false) Request request); + +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/config/CrossOriginFilter.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/config/CrossOriginFilter.java new file mode 100644 index 000000000..9d212a292 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/config/CrossOriginFilter.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.config; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.stereotype.Component; + +/** + * Allows cross origin for testing swagger docs using swagger-ui from local file + * system + */ +@Component +public class CrossOriginFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + // Called by the web container to indicate to a filter that it is being + // placed into service. + // We do not want to do anything here. + } + + @Override + public void doFilter(ServletRequest req, ServletResponse resp, + FilterChain chain) throws IOException, ServletException { + HttpServletResponse response = (HttpServletResponse) resp; + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Allow-Methods", + "GET, POST, PATCH, PUT, DELETE, OPTIONS"); + response.setHeader("Access-Control-Max-Age", "3600"); + response.setHeader( + "Access-Control-Allow-Headers", + "Origin, Authorization, X-Requested-With, Content-Type, Accept, X-Auth-Token, X-CSRF-TOKEN"); + chain.doFilter(req, resp); + } + + @Override + public void destroy() { + + // Called by the web container to indicate to a filter that it is being + // taken out of service. + // We do not want to do anything here. + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/config/SpringSecurityConfig.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/config/SpringSecurityConfig.java new file mode 100644 index 000000000..5e153e3c3 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/config/SpringSecurityConfig.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import org.springframework.security.web.firewall.DefaultHttpFirewall; +import org.springframework.security.web.firewall.HttpFirewall; + +import feign.RequestInterceptor; +import feign.RequestTemplate; + +/** + * Sample SpringSecurty Config to Support / in "@pathparam". Http security is + * overriden to permit AL. + * + * @author U11087 + * + */ +@Configuration("WebSecurityConfig") +@EnableWebSecurity +public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + /** + * Constructor disables the default security settings + **/ + public SpringSecurityConfig() { + super(true); + } + + @Bean + public RequestInterceptor requestTokenBearerInterceptor() { + return new RequestInterceptor() { + @Override + public void apply(RequestTemplate requestTemplate) { + log.info("Is SecurityContextHolder.getContext() null ===========>"+(SecurityContextHolder.getContext() != null)); + if(SecurityContextHolder.getContext() != null) { + OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails(); + log.info("Token Value===========>"+details.getTokenValue()); + requestTemplate.header("Authorization", "bearer " + details.getTokenValue()); + } + } + }; + } + + /** + * Allow url encoded slash http firewall. + * + * @return the http firewall + */ + @Bean + public HttpFirewall allowUrlEncodedSlashHttpFirewall() { + DefaultHttpFirewall firewall = new DefaultHttpFirewall(); + firewall.setAllowUrlEncodedSlash(true); + return firewall; + } + + /* (non-Javadoc) + * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.WebSecurity) + */ + @Override + public void configure(WebSecurity web) throws Exception { + web.httpFirewall(allowUrlEncodedSlashHttpFirewall()); + web.ignoring().antMatchers("/public/**", "/swagger-ui.html", "/api.html", "/js/swagger-oauth.js", "/images/pacman_logo.svg", "/js/swagger.js", "/js/swagger-ui.js", "/images/favicon-32x32.png", "/images/favicon-16x16.png", "/images/favicon.ico", "/swagger-resources/**", "/v2/api-docs/**", "/webjars/**", + "/v1/auth/**", "/client-auth/**", "/user/login/**", "/auth/refresh/**", "/user/authorize/**"); + web.ignoring().antMatchers("/imgs/**"); + web.ignoring().antMatchers("/css/**"); + web.ignoring().antMatchers("/css/font/**"); + web.ignoring().antMatchers("/proxy*/**"); + web.ignoring().antMatchers("/hystrix/monitor/**"); + web.ignoring().antMatchers("/hystrix/**"); + web.ignoring().antMatchers("/actuator/**"); + web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**"); + } + + /* (non-Javadoc) + * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity) + */ + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable(); + http.anonymous().and().antMatcher("/user").authorizeRequests().antMatchers("/public/**").permitAll() + .antMatchers("/secure/**").authenticated(); + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/controller/VulnerabilityController.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/controller/VulnerabilityController.java new file mode 100644 index 000000000..ad97b0d7e --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/controller/VulnerabilityController.java @@ -0,0 +1,1033 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/* + * + */ +package com.tmobile.pacman.api.vulnerability.controller; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TimeZone; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.base.Strings; +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.exception.ServiceException; +import com.tmobile.pacman.api.commons.utils.ResponseUtils; +import com.tmobile.pacman.api.vulnerability.domain.CompliantTrendRequest; +import com.tmobile.pacman.api.vulnerability.domain.DitributionDTO; +import com.tmobile.pacman.api.vulnerability.domain.OutputDTO; +import com.tmobile.pacman.api.vulnerability.domain.Request; +import com.tmobile.pacman.api.vulnerability.domain.ResponseData; +import com.tmobile.pacman.api.vulnerability.domain.ResponseWithCount; +import com.tmobile.pacman.api.vulnerability.domain.TrendNote; +import com.tmobile.pacman.api.vulnerability.domain.TrendRequest; +import com.tmobile.pacman.api.vulnerability.domain.VulnerabilityRequest; +import com.tmobile.pacman.api.vulnerability.service.VulnerabilityService; + +import io.swagger.annotations.ApiOperation; + +/** + * The Class VulnerabilityController. + */ +/** + * @author U95007 + * + */ + +@RestController +@PreAuthorize("@securityService.hasPermission(authentication, 'ROLE_USER')") +@CacheConfig(cacheNames = { "trends", "trendsvuln" }) +@ConditionalOnProperty(name = "features.vulnerability.enabled") +public class VulnerabilityController implements Constants { + + /** The Constant LOGGER. */ + private static final Logger LOGGER = LoggerFactory.getLogger(VulnerabilityController.class); + + /** The vulnerability service. */ + @Autowired + private VulnerabilityService vulnerabilityService; + + /** + * Gets the vulnerabilities details. + * + * @param request + * the request + * @return ResponseEntity + */ + + @SuppressWarnings("unchecked") + @PostMapping(value = "/v1/vulnerabilities/detail") + public ResponseEntity getVulnerabilitiesDetails(@RequestBody(required = true) Request request) { + + ResponseWithCount response; + String assetGroup = request.getAg(); + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + + int from = request.getFrom(); + int size = request.getSize(); + if (from < 0) { + return ResponseUtils.buildFailureResponse(new Exception("From should not be a negative number")); + + } + + String searchText = request.getSearchtext(); + + Map filter = request.getFilter(); + if (filter == null) { + filter = new HashMap<>(); + } + + try { + + List> masterDetailList = vulnerabilityService.getVulnerabilitiesDetails(assetGroup, + filter); + + masterDetailList = (List>) vulnerabilityService + .filterMatchingCollectionElements(masterDetailList, searchText, true); + if (masterDetailList.isEmpty()) { + return ResponseUtils + .buildSucessResponse(new ResponseWithCount(new ArrayList>(), 0)); + } + + if (from >= masterDetailList.size()) { + return ResponseUtils.buildFailureResponse(new Exception("From exceeds the size of list")); + } + + int endIndex = 0; + + if ((from + size) > masterDetailList.size()) { + endIndex = masterDetailList.size(); + } else { + endIndex = from + size; + } + + if (endIndex == 0) { + endIndex = masterDetailList.size(); + } + + // from - inclusive, endIndex - exclusive + List> subDetailList = masterDetailList.subList(from, endIndex); + + response = new ResponseWithCount(subDetailList, masterDetailList.size()); + + } catch (Exception e) { + LOGGER.error(EXE_VULN, e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + + + + /** + * Gets the vulnerabilities details by application. + * + * @param request + * the request + * @return ResponseEntity + */ + + @ApiOperation(httpMethod = "POST", value = "Get the cartesian product of vulnerability occureneces of asset and details of assets in an asset group. Filter can receive 'severitylevel' as comma seperated." + + "'searchtext' can be used to get the sorted result based on the search text provided.Sample request: {\"ag\":\"pacman\",\"filter\":{\"severitylevel\":\"3,5\"},\"from\":0,\"searchtext\":\"\",\"size\":25}") + @PostMapping(value = "/v1/vulnerabilities/occurrences") + public ResponseEntity getVulnerabilitiesOccurrences(@RequestBody(required = true) Request request) { + + ResponseWithCount response = null; + String assetGroup = request.getAg(); + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + String searchText = request.getSearchtext(); + int from = request.getFrom(); + int size = request.getSize(); + List severitiesList = new ArrayList<>(); + List severities = new ArrayList<>(); + boolean allMatch = false; + severitiesList.add("3"); + severitiesList.add("4"); + severitiesList.add("5"); + + if (from < 0) { + return ResponseUtils.buildFailureResponse(new Exception("From should not be a negative number")); + } + Map filter = request.getFilter(); + Map severityfilter = new HashMap<>(); + String applicationFilter = null; + String environmentFilter = null; + for (Map.Entry entry : filter.entrySet()) { + if (entry.getKey().equals("severitylevel")) { + severityfilter.put(entry.getKey(), entry.getValue()); + } else if (entry.getKey().equals("tags.Application.keyword")) { + applicationFilter = entry.getValue(); + } else if (entry.getKey().equals("tags.Environment.keyword")) { + environmentFilter = entry.getValue(); + } + } + + if (severityfilter.size() > 0) { + + for (Map.Entry entry : severityfilter.entrySet()) { + severities = Arrays.asList(entry.getValue().split(",")); + } + allMatch = severities.stream().allMatch(severitiesList::contains); + if (allMatch != true) { + return ResponseUtils.buildFailureResponse(new Exception("Severity level can only be 3,4 or 5")); + } + + } + if (!(Optional.ofNullable(size).isPresent()) && size != 0) { + from = 0; + size = 0; + } + + try { + int vulnerabilityOccuranceListCount = vulnerabilityService.vulnerabilityAssetCount(assetGroup, + severityfilter, applicationFilter, environmentFilter, from, size); + List> vulnerabilityOccuranceList = vulnerabilityService + .getAllVulnerabilitiesDetailsByAssetGroup(assetGroup, severityfilter,applicationFilter, environmentFilter, searchText, from, size); + + if (vulnerabilityOccuranceList.isEmpty()) { + return ResponseUtils + .buildSucessResponse(new ResponseWithCount(new ArrayList>(), 0)); + } + + response = new ResponseWithCount(vulnerabilityOccuranceList, vulnerabilityOccuranceListCount); + + } catch (Exception e) { + LOGGER.error(EXE_VULN, e); + return ResponseUtils.buildFailureResponse(e); + } + + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerability summary. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/vulnerabilities/summary", method = RequestMethod.GET) + public ResponseEntity getVulnerabilitysummary(@RequestParam(name = "ag", required = true) String assetGroup, + @RequestParam(name = "severity", required = false) String severity) { + + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + if (Strings.isNullOrEmpty(severity)) { + severity = SEVERITY_LEVELS; + } + DitributionDTO response; + try { + response = new DitributionDTO(vulnerabilityService.getVulnerabilitySummary(assetGroup, severity)); + } catch (Exception e) { + LOGGER.error("Exception in getVulnerabilitysummary ", e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerability by applications. + * + * @param assetGroup + * the asset group + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/vulnerabilities/summarybyapplication", method = RequestMethod.GET) + public ResponseEntity getVulnerabilityByApplications(@RequestParam("ag") String assetGroup) { + + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + ResponseData response; + try { + response = new ResponseData( + vulnerabilityService.getVulnerabilityByAppAndEnv(assetGroup, "tags.Application.keyword", "")); + } catch (Exception e) { + LOGGER.error("Exception in vulnerabilitybyapplications ", e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerabilities trend. + * + * @param request + * the request + * @return ResponseEntity + */ + + @PostMapping(value = "/v1/vulnerabilities/trend") + public ResponseEntity getVulnerabilitiesTrend(@RequestBody(required = true) TrendRequest request) { + + Map response = new HashMap<>(); + String assetGroup = request.getAg(); + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + Date from = request.getFrom(); + Date to = request.getTo(); + Map filter = request.getFilter(); + try { + if (from == null && to == null) { + Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("UTC")); + to = cal.getTime(); + cal.add(Calendar.DATE, NEG_THIRTY); + from = cal.getTime(); + } + response.put("ag", assetGroup); + List> trendList = vulnerabilityService.getVulnerabilityTrend(assetGroup, filter, from, + to); + response.put("trend", trendList); + } catch (Exception e) { + LOGGER.error(EXE_VULN, e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerability by environment. + * + * @param assetGroup + * the asset group + * @param application + * the application + * @return ResponseEntity + */ + + @RequestMapping(path = "/v1/vulnerabilities/summarybyenvironment", method = RequestMethod.GET) + public ResponseEntity getVulnerabilityByEnvironment(@RequestParam("ag") String assetGroup, + @RequestParam(name = "application", required = false) String application) { + + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + ResponseData response; + try { + response = new ResponseData(vulnerabilityService.getVulnerabilityByAppAndEnv(assetGroup, + "tags.Environment.keyword", application)); + } catch (Exception e) { + LOGGER.error("Exception in vulnerabilitybyenvironment ", e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerability distribution. + * + * @param assetGroup + * the asset group + * @return ResponseEntity + */ + @RequestMapping(path = "/v1/vulnerabilities/distribution", method = RequestMethod.GET) + public ResponseEntity getVulnerabilityDistribution(@RequestParam("ag") String assetGroup) { + + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + ResponseData response; + try { + response = new ResponseData(vulnerabilityService.getVulnerabilitiesDistribution(assetGroup)); + } catch (Exception e) { + LOGGER.error("Exception in getVulnerabilityDistribution ", e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerabilitysummary by resource id. + * + * @param resourceId + * the resource id + * @return ResponseEntity + */ + @RequestMapping(path = "/v1/vulnerabilities/summary/{resourceId}", method = RequestMethod.GET) + public ResponseEntity getVulnerabilitysummaryByResourceId( + @PathVariable(name = "resourceId", required = true) String resourceId) { + + DitributionDTO response; + try { + response = new DitributionDTO(vulnerabilityService.getVulnerabilitysummaryByResourceId(resourceId)); + } catch (Exception e) { + LOGGER.error("Exception in getVulnerabilitysummary ", e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerability details by resource id. + * + * @param resourceId + * the resource id + * @param searchtext + * the searchtext + * @param from + * the from + * @param size + * the size + * @return ResponseEntity + */ + @SuppressWarnings("unchecked") + @RequestMapping(path = "/v1/vulnerabilities/detail/{resourceId}", method = RequestMethod.GET) + public ResponseEntity getVulnerabilityDetailsByResourceId( + @PathVariable(name = "resourceId", required = true) String resourceId, + @RequestParam(name = "searchtext", required = false) String searchtext, + @RequestParam(name = "from", required = false) Integer from, + @RequestParam(name = "size", required = false) Integer size) { + + Integer iFrom = from == null ? 0 : from; + Integer iSize = size == null ? 0 : size; + + ResponseWithCount response; + try { + List> masterDetailList = vulnerabilityService + .getVulnerabilityDetailsByResourceId(resourceId); + masterDetailList = (List>) vulnerabilityService + .filterMatchingCollectionElements(masterDetailList, searchtext, true); + if (masterDetailList.isEmpty()) { + return ResponseUtils + .buildSucessResponse(new ResponseWithCount(new ArrayList>(), 0)); + } + + if (iFrom >= masterDetailList.size()) { + return ResponseUtils.buildFailureResponse(new Exception("From exceeds the size of list")); + } + + int endIndex = 0; + + if (iSize == 0) { + iSize = masterDetailList.size(); + } + + if ((iFrom + iSize) > masterDetailList.size()) { + endIndex = masterDetailList.size(); + } else { + endIndex = iFrom + iSize; + } + + List> subDetailList = masterDetailList.subList(iFrom, endIndex); + + response = new ResponseWithCount(subDetailList, masterDetailList.size()); + + } catch (Exception e) { + LOGGER.error(EXE_VULN, e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerability distribution summary. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return ResponseEntity + */ + @RequestMapping(path = "/v1/vulnerabilities/distributionsummary", method = RequestMethod.GET) + public ResponseEntity getVulnerabilityDistributionSummary(@RequestParam("ag") String assetGroup, + @RequestParam(name = "severity", required = false) String severity) { + + try { + return ResponseUtils.buildSucessResponse( + vulnerabilityService.getVulnerabilityDistributionSummary(assetGroup, severity)); + } catch (Exception e) { + return ResponseUtils.buildFailureResponse(e); + } + } + + /** + * Gets the aging distribution summary. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return ResponseEntity + */ + @RequestMapping(path = "/v1/vulnerabilities/aging/distributionsummary", method = RequestMethod.GET) + public ResponseEntity getAgingDistributionSummary(@RequestParam("ag") String assetGroup, + @RequestParam(name = "severity", required = false) String severity) { + try { + return ResponseUtils + .buildSucessResponse(vulnerabilityService.getAgingDistributionSummary(assetGroup, severity)); + } catch (Exception e) { + return ResponseUtils.buildFailureResponse(e); + } + } + + /** + * Gets the aging summary. + * + * @param assetGroup + * the asset group + * @return ResponseEntity + */ + @RequestMapping(path = "/v1/vulnerabilities/aging/summary", method = RequestMethod.GET) + public ResponseEntity getAgingSummary(@RequestParam("ag") String assetGroup) { + return ResponseUtils.buildSucessResponse(vulnerabilityService.getAgingSummary(assetGroup)); + } + + /** + * Gets the vulnerability by qid. + * + * @param qid + * the qid + * @return ResponseEntity + */ + @RequestMapping(path = "/v1/vulnerabilities/qids", method = RequestMethod.GET) + public ResponseEntity getVulnerabilityByQid(@RequestParam("qid") String qid) { + return ResponseUtils.buildSucessResponse(vulnerabilityService.getVulnerabilityByQid(qid)); + } + + /** + * Gets the distribution summary by vuln type. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the distribution summary by vuln type + */ + @RequestMapping(path = "/v1/vulnerabilities/distribution-vulntype", method = RequestMethod.GET) + public ResponseEntity getDistributionSummaryByVulnType(@RequestParam("ag") String assetGroup, + @RequestParam(name = "severity", required = false) String severity) { + + Map response = new HashMap<>(); + + List> distributionList = new ArrayList<>(); + try { + distributionList = vulnerabilityService.getDistributionSummaryByVulnType(assetGroup, severity); + } catch (DataException e) { + LOGGER.error("Error in getDistributionSummaryByVulnType", e); + return ResponseUtils.buildFailureResponse(e); + } + + response.put(DISTRIBUTION, distributionList); + return ResponseUtils.buildSucessResponse(response); + + } + + /** + * Gets the distribution summary by infra type. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the distribution summary by infra type + */ + @RequestMapping(path = "/v1/vulnerabilities/distribution-infra", method = RequestMethod.GET) + public ResponseEntity getDistributionSummaryByInfraType(@RequestParam("ag") String assetGroup, + @RequestParam(name = "severity", required = false) String severity) { + Map response = new HashMap<>(); + + List> distributionList = new ArrayList<>(); + try { + distributionList = vulnerabilityService.getDistributionSummaryByInfraType(assetGroup, severity); + } catch (ServiceException e) { + LOGGER.error("Error in getDistributionSummaryByInfraType", e); + return ResponseUtils.buildFailureResponse(e); + } + response.put(DISTRIBUTION, distributionList); + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the distribution summary by env. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the distribution summary by env + */ + @RequestMapping(path = "/v1/vulnerabilities/distribution-env", method = RequestMethod.GET) + public ResponseEntity getDistributionSummaryByEnv(@RequestParam("ag") String assetGroup, + @RequestParam(name = "severity", required = false) String severity) { + Map response = new HashMap<>(); + + List> distributionList = new ArrayList<>(); + try { + distributionList = vulnerabilityService.getDistributionSummaryByEnv(assetGroup, severity); + } catch (ServiceException e) { + LOGGER.error("Error in getDistributionSummaryByEnv", e); + return ResponseUtils.buildFailureResponse(e); + } + response.put(DISTRIBUTION, distributionList); + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the remediation actions summary. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the remediation actions summary + */ + @RequestMapping(path = "/v1/vulnerabilities/remediations/summary", method = RequestMethod.GET) + public ResponseEntity getRemediationActionsSummary(@RequestParam("ag") String assetGroup, + @RequestParam(name = "severity", required = false) String severity) { + Map response = new HashMap<>(); + + List> remediationList = new ArrayList<>(); + try { + remediationList = vulnerabilityService.getRemediationActionsSummary(assetGroup, severity); + } catch (DataException e) { + LOGGER.error("Error in getRemediationActionsSummary", e); + return ResponseUtils.buildFailureResponse(e); + } + + response.put("actions", remediationList); + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the highest lowest performers. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the highest lowest performers + */ + @RequestMapping(path = "/v1/vulnerabilities/performers", method = RequestMethod.GET) + public ResponseEntity getHighestLowestPerformers(@RequestParam("ag") String assetGroup, + @RequestParam(name = "severity", required = false) String severity) { + Map response = new HashMap<>(); + + List> responseList = new ArrayList<>(); + Map directorData = vulnerabilityService.getHighestLowestPerformers(assetGroup, severity, + "org"); + Set keys = directorData.keySet(); + String[] keysArray = keys.toArray(new String[keys.size()]); + + if (keysArray.length >= 10) { + + Map info = new HashMap<>(); + info.put(CATEGORY, HIGHEST); + List> directorList = new ArrayList<>(); + + for (int i = 0; i < keysArray.length && i < Constants.FIVE; i++) { + Map director = new HashMap<>(); + director.put(keysArray[i], directorData.get(keysArray[i])); + directorList.add(director); + } + info.put(DIRECTORS, directorList); + responseList.add(info); + + info = new HashMap<>(); + info.put(CATEGORY, "Lowest"); + directorList = new ArrayList<>(); + + for (int i = keysArray.length - Constants.ONE; i > keysArray.length - Constants.SIX && i >= 0; i--) { + Map director = new HashMap<>(); + director.put(keysArray[i], directorData.get(keysArray[i])); + directorList.add(director); + } + info.put(DIRECTORS, directorList); + responseList.add(info); + } else { + + if (keysArray.length % 2 == 0) { + Map info = new HashMap<>(); + info.put(CATEGORY, HIGHEST); + List> directorList = new ArrayList<>(); + + for (int i = 0; i < keysArray.length / 2; i++) { + Map director = new HashMap<>(); + director.put(keysArray[i], directorData.get(keysArray[i])); + directorList.add(director); + } + info.put(DIRECTORS, directorList); + responseList.add(info); + + } else { + + Map info = new HashMap<>(); + info.put(CATEGORY, HIGHEST); + List> directorList = new ArrayList<>(); + + for (int i = 0; i < (keysArray.length / 2) + 1; i++) { + Map director = new HashMap<>(); + director.put(keysArray[i], directorData.get(keysArray[i])); + directorList.add(director); + } + info.put(DIRECTORS, directorList); + responseList.add(info); + } + + Map info = new HashMap<>(); + info.put(CATEGORY, "Lowest"); + List> directorList = new ArrayList<>(); + + for (int i = keysArray.length - Constants.ONE; i > keysArray.length / 2 && i >= 0; i--) { + Map director = new HashMap<>(); + director.put(keysArray[i], directorData.get(keysArray[i])); + directorList.add(director); + } + info.put(DIRECTORS, directorList); + responseList.add(info); + } + response.put("response", responseList); + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the highest lowest performers. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @param type + * the type + * @return the highest lowest performers + */ + @RequestMapping(path = "/v2/vulnerabilities/performers", method = RequestMethod.GET) + public ResponseEntity getHighestLowestPerformers(@RequestParam("ag") String assetGroup, + @RequestParam(name = "severity", required = false) String severity, + @RequestParam(name = "type") PerfType type) { + Map response = new HashMap<>(); + + Map perfData = vulnerabilityService.getHighestLowestPerformers(assetGroup, severity, + type.name()); + List> perfList = new ArrayList<>(); + Map info = new HashMap<>(); + String typeName = type.name(); + switch (typeName) { + case "org": + info.put(CATEGORY, "Director"); + break; + case "application": + info.put(CATEGORY, "Application"); + break; + case "environment": + info.put(CATEGORY, "Environment"); + break; + } + + if (perfData.size() > 1) { + Set keys = perfData.keySet(); + String[] keysArray = keys.toArray(new String[keys.size()]); + + for (int i = 0; i < keysArray.length; i++) { + Map director = new HashMap<>(); + director.put(keysArray[i], perfData.get(keysArray[i])); + perfList.add(director); + } + } + info.put("data", perfList); + + response.put("response", info); + return ResponseUtils.buildSucessResponse(response); + } + + /** + * Gets the vulnerability trend. + * + * @param header + * the header + * @param request + * the request + * @return the vulnerability trend + */ + @Cacheable(cacheNames = "trends", key = "#request.vulnCacheKey", unless = "#result.statusCodeValue!=200") + @RequestMapping(path = "/v1/vulnerabilities/trend/open-new", method = RequestMethod.POST) + public ResponseEntity getVulnerabilityTrend(@RequestBody(required = true) TrendRequest request) { + + String ag = request.getAg(); + if (Strings.isNullOrEmpty(ag)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + Date from = request.getFrom(); + if (from == null) { + Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("UTC")); + cal.add(Calendar.DATE, NEG_THIRTY); + from = cal.getTime(); + } + + Map filter = request.getFilter(); + String severity = SEVERITY_LEVELS; + if (filter != null) { + severity = filter.get("severity"); + if (severity == null) { + severity = SEVERITY_LEVELS; + } + } + + Map response = new HashMap<>(); + try { + List> trendList = vulnerabilityService.getVulnerabilityNewOpenTrend(ag, severity, from); + response.put("trend", trendList); + return ResponseUtils.buildSucessResponse(response); + } catch (Exception e) { + return ResponseUtils.buildFailureResponse(e); + } + + } + + /** + * Creates the trend annotation. + * + * @param request + * the request + * @return the response entity + */ + @RequestMapping(path = "/v1/vulnerabilities/trend/notes", method = RequestMethod.POST) + public ResponseEntity createTrendAnnotation(@RequestBody(required = true) TrendNote request) { + + try { + if (vulnerabilityService.createTrendAnnotation(request)) { + return ResponseUtils.buildSucessResponse("Annotation created"); + } else { + return ResponseUtils.buildFailureResponse(new Exception("Annotation creation failed")); + } + } catch (JsonProcessingException e) { + LOGGER.error("Error in createTrendAnnotation ", e); + return ResponseUtils.buildFailureResponse(e); + } + } + + /** + * Gets the trend annotations. + * + * @param assetGroup + * the asset group + * @param from + * the from + * @return the trend annotations + */ + @RequestMapping(path = "/v1/vulnerabilities/trend/notes", method = RequestMethod.GET) + public ResponseEntity getTrendAnnotations(@RequestParam("ag") String assetGroup, + @RequestParam(name = "from", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date from) { + + if (from == null) { + Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("UTC")); + cal.add(Calendar.DATE, NEG_THIRTY); + from = cal.getTime(); + } + Map notes = new HashMap<>(); + try { + notes.put("notes", vulnerabilityService.getTrendAnnotations(assetGroup, from)); + } catch (DataException e) { + LOGGER.error("Error in getTrendAnnotations ", e); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(notes); + + } + + /** + * Delete trend annotation. + * + * @param noteId + * the note id + * @return the response entity + */ + @RequestMapping(path = "/v1/vulnerabilities/trend/notes/{noteId}", method = RequestMethod.DELETE) + public ResponseEntity deleteTrendAnnotation(@PathVariable(name = "noteId", required = true) String noteId) { + + if (vulnerabilityService.deleteTrendAnnotation(noteId)) { + return ResponseUtils.buildSucessResponse("Annotation Deleted"); + } else { + return ResponseUtils.buildFailureResponse(new Exception("Annotation deletion failed")); + } + } + + /** + * Gets the vulnerability assets trend. + * + * @param request + * the request + * @return the vulnerability assets trend + */ + @Cacheable(cacheNames = "trendsvuln", key = "#request.vulnCacheKey", unless = "#result.statusCodeValue!=200") + @RequestMapping(path = "/v1/vulnerabilities/trend/total-affected", method = RequestMethod.POST) + public ResponseEntity getVulnerabilityAssetsTrend(@RequestBody(required = true) TrendRequest request) { + + String ag = request.getAg(); + if (Strings.isNullOrEmpty(ag)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + Date from = request.getFrom(); + if (from == null) { + Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("UTC")); + cal.add(Calendar.DATE, NEG_THIRTY); + from = cal.getTime(); + } + + Map filter = request.getFilter(); + String severity = SEVERITY_LEVELS; + if (filter != null) { + severity = filter.get("severity"); + if (severity == null) { + severity = SEVERITY_LEVELS; + } + } + + Map response = new HashMap<>(); + try { + response.put("trend", vulnerabilityService.getVulnerabilityAssetsTrend(ag, severity, from)); + } catch (DataException e) { + LOGGER.error("Error in getVulnerabilityAssetsTrend ", e); + return ResponseUtils.buildFailureResponse(e); + } + + return ResponseUtils.buildSucessResponse(response); + + } + + /** + * Gets the vulnerability summary by assets. + * + * @param assetGroup + * the asset group + * @return the vulnerability summary by assets + */ + @RequestMapping(path = "/v1/vulnerabilities/summarybyassets", method = RequestMethod.GET) + public ResponseEntity getVulnerabilitySummaryByAssets( + @RequestParam(name = "ag", required = true) String assetGroup) { + try { + return ResponseUtils.buildSucessResponse(vulnerabilityService.getVulnerabilitySummaryByAssets(assetGroup)); + } catch (ServiceException | DataException e) { + LOGGER.error("Error in getVulnerabilitySummaryByAssets ", e); + return ResponseUtils.buildFailureResponse(e); + } + } + + /** + * Gets the vulnerabilities.asssetGroup is mandatory. API returns count of + * totalVulnerabilities/totalAssets/totalVulnerabilites Assets + * + * @param assetGroup + * name of the asset group + * @return ResponseEntity + */ + // @Cacheable("trends") + @RequestMapping(path = "/v1/vulnerabilites", method = RequestMethod.GET) + public ResponseEntity getVulnerabilities(@RequestParam("ag") String assetGroup) { + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + OutputDTO output = null; + try { + Map vulnerabilities = new HashMap<>(); + Map vulnSummary = vulnerabilityService.getVulnerabilitySummary(assetGroup, SEVERITY_LEVELS); + vulnerabilities.put("vulnerabilities", Long.valueOf(vulnSummary.get("vulnerabilities").toString())); + vulnerabilities.put("hosts", Long.valueOf(vulnSummary.get("hosts").toString())); + vulnerabilities.put("totalVulnerableAssets", + Long.valueOf(vulnSummary.get("totalVulnerableAssets").toString())); + vulnSummary.remove("compliantpercent"); + output = new OutputDTO(vulnerabilities); + } catch (ServiceException e) { + return vulnerabilityService.formatException(e); + } + return ResponseUtils.buildSucessResponse(output); + } + + @RequestMapping(path = "/v1/trend/compliance/vulnerabilities", method = RequestMethod.POST) + public ResponseEntity getVulnTrend(@RequestBody(required = true) CompliantTrendRequest request) { + + Map response = new HashMap<>(); + String assetGroup = request.getAg(); + + Date input = request.getFrom(); + + if (input == null) { + Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("UTC")); + cal.add(Calendar.DATE, NEG_THIRTY); + input = cal.getTime(); + } + + Instant instant = input.toInstant(); + ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault()); + LocalDate fromDate = zdt.toLocalDate(); + LocalDate toDate = LocalDate.now(); + + if (Strings.isNullOrEmpty(assetGroup)) { + return ResponseUtils.buildFailureResponse(new Exception(ASSET_MANDATORY)); + } + + try { + Map ruleTrendProgressList = vulnerabilityService.getTrendProgress(assetGroup, null, + fromDate, toDate, "vulncompliance"); + response.put(RESPONSE, ruleTrendProgressList); + } catch (ServiceException e) { + LOGGER.error("Exception in getVulnTrend", e.getMessage()); + return ResponseUtils.buildFailureResponse(e); + } + return ResponseUtils.buildSucessResponse(response); + } + +} + +enum PerfType { + org, application, environment +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetApi.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetApi.java new file mode 100644 index 000000000..b3da1ed07 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetApi.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Oct 26, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; +/** + * The Class AssetApi. + */ +public class AssetApi { + private String message; + + private AssetApiData data; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public AssetApiData getData() { + return data; + } + + public void setData(AssetApiData data) { + this.data = data; + } + + @Override + public String toString() { + return "ClassPojo [message = " + message + ", data = " + data + "]"; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetApiData.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetApiData.java new file mode 100644 index 000000000..c9e36ab42 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetApiData.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Oct 26, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; +/** + * The Class AssetApiData. + */ +public class AssetApiData { + private String ag; + + private String datasource; + + private AssetCountDTO[] applications; + + private AssetCountDTO[] environments; + + private AssetCountDTO[] targettypes; + + public String getDatasource() { + return datasource; + } + + public void setDatasource(String datasource) { + this.datasource = datasource; + } + + public AssetCountDTO[] getTargettypes() { + return targettypes; + } + + public void setTargettypes(AssetCountDTO[] targettypes) { + this.targettypes = targettypes; + } + + public AssetCountDTO[] getEnvironments() { + return environments; + } + + public void setEnvironments(AssetCountDTO[] environments) { + this.environments = environments; + } + + public String getAg() { + return ag; + } + + public void setAg(String ag) { + this.ag = ag; + } + + public AssetCountDTO[] getApplications() { + return applications; + } + + public void setApplications(AssetCountDTO[] applications) { + this.applications = applications; + } + + @Override + public String toString() { + return "ClassPojo [applications = " + applications + ", ag = " + ag + + ", environments = " + environments + ", targettypes = " + + targettypes + ",datasource = " + datasource + "]"; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCount.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCount.java new file mode 100644 index 000000000..f08f7c151 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCount.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Oct 26, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; +/** + * The Class AssetCount. + */ +public class AssetCount { + private String message; + + private AssetCountData data; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public AssetCountData getData() { + return data; + } + + public void setData(AssetCountData data) { + this.data = data; + } + + @Override + public String toString() { + return "ClassPojo [message = " + message + ", data = " + data + "]"; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountByAppEnvDTO.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountByAppEnvDTO.java new file mode 100644 index 000000000..50b73cb91 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountByAppEnvDTO.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Nov 5, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; +/** + * The Class AssetCountByAppEnvDTO. + */ +public class AssetCountByAppEnvDTO { + private String count; + + private String type; + + private AssetCountEnvCount[] environments; + + private String application; + + public AssetCountEnvCount[] getEnvironments() { + return environments; + } + + public void setEnvironments(AssetCountEnvCount[] environments) { + this.environments = environments; + } + + public String getApplication() { + return application; + } + + public void setApplication(String application) { + this.application = application; + } + + public String getCount() { + return count; + } + + public void setCount(String count) { + this.count = count; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return "ClassPojo [count = " + count + ", type = " + type + + ", application = " + application + ",environments = " + + environments + "]"; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountDTO.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountDTO.java new file mode 100644 index 000000000..de757ec5a --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountDTO.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Nov 5, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; +/** + * The Class AssetCountDTO. + */ +public class AssetCountDTO { + + /** The name. */ + private String name; + + /** The type. */ + private String type; + + /** + * Gets the type. + * + * @return the type + */ + public String getType() { + return type; + } + + /** + * Sets the type. + * + * @param type the new type + */ + public void setType(String type) { + this.type = type; + } + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Sets the name. + * + * @param name the new name + */ + public void setName(String name) { + this.name = name; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ClassPojo [name = " + name + ",type = " + type + "]"; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountData.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountData.java new file mode 100644 index 000000000..f5d069c43 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountData.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Oct 26, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; +/** + * The Class AssetCountData. + */ +public class AssetCountData { + + /** The assetcount. */ + private AssetCountByAppEnvDTO[] assetcount; + + /** The ag. */ + private String ag; + + /** The type. */ + private String type; + + /** + * Gets the type. + * + * @return the type + */ + public String getType() { + return type; + } + + /** + * Sets the type. + * + * @param type the new type + */ + public void setType(String type) { + this.type = type; + } + + /** + * Gets the assetcount. + * + * @return the assetcount + */ + public AssetCountByAppEnvDTO[] getAssetcount() { + return assetcount; + } + + /** + * Sets the assetcount. + * + * @param assetcount the new assetcount + */ + public void setAssetcount(AssetCountByAppEnvDTO[] assetcount) { + this.assetcount = assetcount; + } + + /** + * Gets the ag. + * + * @return the ag + */ + public String getAg() { + return ag; + } + + /** + * Sets the ag. + * + * @param ag the new ag + */ + public void setAg(String ag) { + this.ag = ag; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ClassPojo [assetcount = " + assetcount + ", ag = " + ag + + ", type = " + type + "]"; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountEnvCount.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountEnvCount.java new file mode 100644 index 000000000..5600dd7d3 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/AssetCountEnvCount.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.domain; + +/** + * The Class AssetCountEnvCount. + */ +public class AssetCountEnvCount { + + /** The environment. */ + private String environment; + + /** The count. */ + private String count; + + /** + * Gets the environment. + * + * @return the environment + */ + public String getEnvironment() { + return environment; + } + + /** + * Sets the environment. + * + * @param environment the new environment + */ + public void setEnvironment(String environment) { + this.environment = environment; + } + + /** + * Gets the count. + * + * @return the count + */ + public String getCount() { + return count; + } + + /** + * Sets the count. + * + * @param count the new count + */ + public void setCount(String count) { + this.count = count; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ClassPojo [count = " + count + ",environment = " + environment + + "]"; + } + +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/CompliantTrendRequest.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/CompliantTrendRequest.java new file mode 100644 index 000000000..30cb64576 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/CompliantTrendRequest.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.Date; +import java.util.Map; +/** + * The Class CompliantTrendRequest. + */ +public class CompliantTrendRequest { + + /** The ag. */ + private String ag; + + /** The from. */ + private Date from; + + /** The filters. */ + private Map filters; + + /** + * Gets the filters. + * + * @return the filters + */ + public Map getFilters() { + return filters; + } + + /** + * Sets the filters. + * + * @param filters the filters + */ + public void setFilters(Map filters) { + this.filters = filters; + } + + /** + * Gets the ag. + * + * @return the ag + */ + public String getAg() { + return ag; + } + + /** + * Sets the ag. + * + * @param ag the new ag + */ + public void setAg(String ag) { + this.ag = ag; + } + + /** + * Gets the from. + * + * @return the from + */ + public Date getFrom() { + return from; + } + + /** + * Sets the from. + * + * @param from the new from + */ + public void setFrom(Date from) { + this.from = from; + } + +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/DitributionDTO.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/DitributionDTO.java new file mode 100644 index 000000000..ab25e4644 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/DitributionDTO.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Oct 23, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.Map; +/** + * The Class DitributionDTO. + */ +public class DitributionDTO { + + /** The response. */ + Map response; + + /** + * Instantiates a new ditribution DTO. + */ + public DitributionDTO() { + super(); + } + + /** + * Instantiates a new ditribution DTO. + * + * @param distribution the distribution + */ + public DitributionDTO(Map distribution) { + super(); + this.response = distribution; + } + + /** + * Gets the distribution. + * + * @return the distribution + */ + public Map getDistribution() { + return response; + } + + /** + * Sets the distribution. + * + * @param distribution the distribution + */ + public void setDistribution(Map distribution) { + this.response = distribution; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/OutputDTO.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/OutputDTO.java new file mode 100644 index 000000000..8445414ba --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/OutputDTO.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Oct 24, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.Map; +/** + * The Class OutputDTO. + */ +public class OutputDTO { + + /** The response. */ + Map response; + + /** + * Instantiates a new output DTO. + * + * @param output the output + */ + public OutputDTO(Map output) { + super(); + this.response = output; + } + + /** + * Instantiates a new output DTO. + */ + public OutputDTO() { + super(); + } + + /** + * Gets the output. + * + * @return the output + */ + public Map getOutput() { + return response; + } + + /** + * Sets the output. + * + * @param output the output + */ + public void setOutput(Map output) { + this.response = output; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/Request.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/Request.java new file mode 100644 index 000000000..18aefb327 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/Request.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Nov 17, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.base.Joiner; + + +/** + * The Class Request. + */ +public class Request { + + /** The searchtext. */ + private String searchtext = null; + + /** The from. */ + private int from; + + /** The size. */ + private int size; + + /** The filter. */ + private Map filter; + + /** The ag. */ + private String ag; + + /** + * Instantiates a new request. + * + * @param searchtext the searchtext + * @param from the from + * @param size the size + * @param filter the filter + * @param ag the ag + */ + public Request(String searchtext, int from, int size, + Map filter, String ag) { + super(); + this.searchtext = searchtext; + this.from = from; + this.size = size; + this.filter = filter; + this.ag = ag; + } + + /** + * Instantiates a new request. + */ + public Request() { + super(); + } + + /** + * this is used to cache the response. + * + * @return the key + */ + public String getKey() { + return ag + + searchtext + + Joiner.on("_") + .withKeyValueSeparator("-") + .join(filter == null ? new HashMap() + : filter) + from + "" + size; + } + + /** + * Gets the ag. + * + * @return the ag + */ + public String getAg() { + return ag; + } + + /** + * Sets the ag. + * + * @param ag the new ag + */ + public void setAg(String ag) { + this.ag = ag; + } + + /** + * Gets the searchtext. + * + * @return the searchtext + */ + public String getSearchtext() { + return searchtext; + } + + /** + * Sets the searchtext. + * + * @param searchtext the new searchtext + */ + public void setSearchtext(String searchtext) { + this.searchtext = searchtext; + } + + /** + * Gets the from. + * + * @return the from + */ + public int getFrom() { + return from; + } + + /** + * Sets the from. + * + * @param from the new from + */ + public void setFrom(int from) { + this.from = from; + } + + /** + * Gets the size. + * + * @return the size + */ + public int getSize() { + return size; + } + + /** + * Sets the size. + * + * @param size the new size + */ + public void setSize(int size) { + this.size = size; + } + + /** + * Gets the filter. + * + * @return the filter + */ + public Map getFilter() { + return filter; + } + + /** + * Sets the filter. + * + * @param filter the filter + */ + public void setFilter(Map filter) { + this.filter = filter; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ClassPojo [ag=" + ag + ", searchtext = " + searchtext + + ", from = " + from + ", filter = " + filter + ", size = " + + size + "]"; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseDTO.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseDTO.java new file mode 100644 index 000000000..c043ec4e7 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseDTO.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Nov 15, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.Map; +/** + * The Class ResponseDTO. + */ +public class ResponseDTO { + + /** The response. */ + Map response; + + /** + * Instantiates a new response DTO. + */ + public ResponseDTO() { + super(); + } + + /** + * Instantiates a new response DTO. + * + * @param response the response + */ + public ResponseDTO(Map response) { + super(); + this.response = response; + } + + /** + * Gets the response. + * + * @return the response + */ + public Map getResponse() { + return response; + } + + /** + * Sets the response. + * + * @param response the response + */ + public void setResponse(Map response) { + this.response = response; + } + +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseData.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseData.java new file mode 100644 index 000000000..2699d7c1f --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseData.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Nov 15, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.List; +import java.util.Map; + +/** + * The Class Response. + */ +public class ResponseData { + + /** The response. */ + List> response; + + /** + * Instantiates a new response. + */ + public ResponseData() { + super(); + } + + /** + * Instantiates a new response. + * + * @param response the response + */ + public ResponseData(List> response) { + super(); + this.response = response; + } + + /** + * Gets the response. + * + * @return the response + */ + public List> getResponse() { + return response; + } + + /** + * Sets the response. + * + * @param response the response + */ + public void setResponse(List> response) { + this.response = response; + } + +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseWithCount.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseWithCount.java new file mode 100644 index 000000000..c9d49f949 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseWithCount.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Nov 15, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.List; +import java.util.Map; + + +/** + * The Class ResponseWithCount. + */ +public class ResponseWithCount { + + /** The response. */ + List> response; + + /** The total. */ + long total; + + /** + * Instantiates a new response with count. + * + * @param response the response + * @param total the total + */ + public ResponseWithCount(List> response, long total) { + super(); + this.response = response; + this.total = total; + } + + /** + * Gets the total. + * + * @return the total + */ + public long getTotal() { + return total; + } + + /** + * Sets the total. + * + * @param total the new total + */ + public void setTotal(long total) { + this.total = total; + } + + /** + * Gets the response. + * + * @return the response + */ + public List> getResponse() { + return response; + } + + /** + * Sets the response. + * + * @param response the response + */ + public void setResponse(List> response) { + this.response = response; + } + +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseWithOrder.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseWithOrder.java new file mode 100644 index 000000000..5a70aa5d3 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/ResponseWithOrder.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :SGorle + Modified Date: Dec 9, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.LinkedHashMap; +import java.util.List; + +/** + * The DTO Class ResponseWithOrder for API response. + */ +public class ResponseWithOrder { + + /** The response. */ + List> response; + + /** The total. */ + long total; + + /** + * Instantiates a new response with order. + */ + public ResponseWithOrder() { + super(); + } + + /** + * Instantiates a new response with order. + * + * @param response the response + * @param total the total + */ + public ResponseWithOrder(List> response, + long total) { + super(); + this.response = response; + this.total = total; + } + + /** + * Gets the response. + * + * @return the response + */ + public List> getResponse() { + return response; + } + + /** + * Sets the response. + * + * @param response the response + */ + public void setResponse(List> response) { + this.response = response; + } + + /** + * Gets the total. + * + * @return the total + */ + public long getTotal() { + return total; + } + + /** + * Sets the total. + * + * @param total the new total + */ + public void setTotal(long total) { + this.total = total; + } + +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/TrendNote.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/TrendNote.java new file mode 100644 index 000000000..4adb8eba4 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/TrendNote.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.Date; +/** + * The Class TrendNote. + */ +public class TrendNote { + + /** The ag. */ + private String ag; + + /** The note. */ + private String note; + + /** The date. */ + private Date date; + + /** + * Gets the ag. + * + * @return the ag + */ + public String getAg() { + return ag; + } + + /** + * Sets the ag. + * + * @param ag the new ag + */ + public void setAg(String ag) { + this.ag = ag; + } + + /** + * Gets the note. + * + * @return the note + */ + public String getNote() { + return note; + } + + /** + * Sets the note. + * + * @param note the new note + */ + public void setNote(String note) { + this.note = note; + } + + /** + * Gets the date. + * + * @return the date + */ + public Date getDate() { + return date; + } + + /** + * Sets the date. + * + * @param date the new date + */ + public void setDate(Date date) { + this.date = date; + } + + +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/TrendRequest.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/TrendRequest.java new file mode 100644 index 000000000..aecce9091 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/TrendRequest.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +/** + * The Class TrendRequest. + */ +public class TrendRequest { + + /** The from. */ + private Date from; + + /** The to. */ + private Date to; + + /** The filter. */ + private Map filter; + + /** The ag. */ + private String ag; + + /** + * Gets the from. + * + * @return the from + */ + public Date getFrom() { + return from; + } + + /** + * Sets the from. + * + * @param from the new from + */ + public void setFrom(Date from) { + this.from = from; + } + + /** + * Gets the to. + * + * @return the to + */ + public Date getTo() { + return to; + } + + /** + * Sets the to. + * + * @param to the new to + */ + public void setTo(Date to) { + this.to = to; + } + + /** + * Gets the filter. + * + * @return the filter + */ + public Map getFilter() { + return filter; + } + + /** + * Sets the filter. + * + * @param filter the filter + */ + public void setFilter(Map filter) { + this.filter = filter; + } + + /** + * Gets the ag. + * + * @return the ag + */ + public String getAg() { + return ag; + } + + /** + * Sets the ag. + * + * @param ag the new ag + */ + public void setAg(String ag) { + this.ag = ag; + } + + /** + * Gets the vuln cache key. + * + * @return the vuln cache key + */ + public String getVulnCacheKey(){ + String severity = "3,4,5"; + if(filter!=null&& + (filter.get("severity")!=null)){ + severity =filter.get("severity"); + } + + return ag+ new SimpleDateFormat("yyyy-MM-dd").format(from)+severity; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/VulnerabilityRequest.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/VulnerabilityRequest.java new file mode 100644 index 000000000..f62f60766 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/domain/VulnerabilityRequest.java @@ -0,0 +1,212 @@ +package com.tmobile.pacman.api.vulnerability.domain; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.base.Joiner; + +public class VulnerabilityRequest { + /** The searchtext. */ + private String searchtext = null; + + /** The from. */ + private int from; + + /** The size. */ + private int size; + + /** The severity filter. */ + private Map severityFilter; + /** The application filter. */ + String applicationFilter; + /** The environment filter. */ + String environmentFilter; + + /** The ag. */ + private String ag; + + /** + * Instantiates a new request. + * + * @param searchtext + * the searchtext + * @param from + * the from + * @param size + * the size + * @param filter + * the filter + * @param ag + * the ag + */ + public VulnerabilityRequest(String searchtext, int from, int size, Map severityFilter, + String environmentFilter, String applicationFilter, String ag) { + super(); + this.searchtext = searchtext; + this.from = from; + this.size = size; + this.severityFilter = severityFilter; + this.applicationFilter = applicationFilter; + this.environmentFilter = environmentFilter; + this.ag = ag; + } + + /** + * Instantiates a new request. + */ + public VulnerabilityRequest() { + super(); + } + + /** + * this is used to cache the response. + * + * @return the key + */ + public String getKey() { + return ag + searchtext + Joiner.on("_").withKeyValueSeparator("-") + .join(severityFilter == null ? new HashMap() : severityFilter) + from + "" + size; + } + + /** + * Gets the ag. + * + * @return the ag + */ + public String getAg() { + return ag; + } + + /** + * Sets the ag. + * + * @param ag + * the new ag + */ + public void setAg(String ag) { + this.ag = ag; + } + + /** + * Gets the searchtext. + * + * @return the searchtext + */ + public String getSearchtext() { + return searchtext; + } + + /** + * Sets the searchtext. + * + * @param searchtext + * the new searchtext + */ + public void setSearchtext(String searchtext) { + this.searchtext = searchtext; + } + + /** + * Gets the from. + * + * @return the from + */ + public int getFrom() { + return from; + } + + /** + * Sets the from. + * + * @param from + * the new from + */ + public void setFrom(int from) { + this.from = from; + } + + /** + * Gets the size. + * + * @return the size + */ + public int getSize() { + return size; + } + + /** + * Sets the size. + * + * @param size + * the new size + */ + public void setSize(int size) { + this.size = size; + } + + /** + * Gets the filter. + * + * @return the filter + */ + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + + /** + * @return the severityFilter + */ + + /** + * @return the applicationFilter + */ + public String getApplicationFilter() { + return applicationFilter; + } + + /** + * @return the severityFilter + */ + public Map getSeverityFilter() { + return severityFilter; + } + + /** + * @param severityFilter + * the severityFilter to set + */ + public void setSeverityFilter(Map severityFilter) { + this.severityFilter = severityFilter; + } + + /** + * @return the environmentFilter + */ + public String getEnvironmentFilter() { + return environmentFilter; + } + + /** + * @param severityFilter + * the severityFilter to set + */ + + /** + * @param applicationFilter + * the applicationFilter to set + */ + public void setApplicationFilter(String applicationFilter) { + this.applicationFilter = applicationFilter; + } + + /** + * @param environmentFilter + * the environmentFilter to set + */ + public void setEnvironmentFilter(String environmentFilter) { + this.environmentFilter = environmentFilter; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityRepository.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityRepository.java new file mode 100644 index 000000000..e0ec70ad2 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityRepository.java @@ -0,0 +1,1913 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.repository; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.PostConstruct; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.entity.NStringEntity; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Repository; +import org.springframework.util.CollectionUtils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Strings; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.repo.ElasticSearchRepository; +import com.tmobile.pacman.api.commons.repo.PacmanRdsRepository; +import com.tmobile.pacman.api.commons.utils.CommonUtils; +import com.tmobile.pacman.api.commons.utils.PacHttpUtils; +import com.tmobile.pacman.api.vulnerability.domain.TrendNote; + +/** + * This is the Repository layer which makes call to ElasticSearch. + */ +@Repository +public class VulnerabilityRepository implements Constants { + + /** The es host. */ + @Value("${elastic-search.host}") + private String esHost; + + /** The es port. */ + @Value("${elastic-search.port}") + private int esPort; + + /** The update ES host. */ + @Value("${elastic-search.update-host}") + private String updateESHost; + + /** The update ES port. */ + @Value("${elastic-search.update-port}") + private int updateESPort; + + /** The Constant PROTOCOL. */ + private static final String PROTOCOL = "http"; + + /** The es url. */ + private String esUrl; + + /** The elastic search repository. */ + @Autowired + private ElasticSearchRepository elasticSearchRepository; + + /** The rds repository. */ + @Autowired + private PacmanRdsRepository rdsRepository; + + /** The Constant LOGGER. */ + private static final Log LOGGER = LogFactory.getLog(VulnerabilityRepository.class); + + /** The rest client. */ + private RestClient restClient; + + /** + * Initializes the esUrl. + */ + @PostConstruct + void init() { + esUrl = PROTOCOL + "://" + esHost + ":" + esPort; + } + + /** + * Gets the all vulnerabilities. + * + * @param vulnAssetsAffectedQids + * the vuln assets affected qids + * @return the all vulnerabilities + * @throws DataException + * the DataException + */ + public List> getAllVulnerabilities(List vulnAssetsAffectedQids) throws DataException { + + List> results = new ArrayList<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/qualys-kb/kb/_search"); + String responseJson = ""; + try { + for (int index = 0; index <= (vulnAssetsAffectedQids.size() / THOUSAND_TWENTY_FOUR); index++) { + int from = index * THOUSAND_TWENTY_FOUR; + int to = from + THOUSAND_TWENTY_FOUR; + if (vulnAssetsAffectedQids.size() < to) { + to = vulnAssetsAffectedQids.size(); + } + StringBuilder requestBody = new StringBuilder( + "{\"size\":10000,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"qid\":"); + requestBody.append(vulnAssetsAffectedQids.subList(from, to)); + requestBody.append("}}"); + requestBody.append("]}}}"); + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + elasticSearchRepository.processResponseAndSendTheScrollBack(responseJson, results); + } + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilityData", e); + throw new DataException(); + } + return results; + } + + public List> getAllVulnerabilitiesByAssetGroup(String assetGroup, String targetType, + Map mustFilter, List occurrenceFieldList, int from, int size, + Map mustTermsFilter) throws DataException { + List> results = new ArrayList<>(); + if (size != 0) { + try { + results = elasticSearchRepository.getSortedDataFromESBySize(assetGroup, targetType, mustFilter, null, + null, occurrenceFieldList, from, size, null, mustTermsFilter, null); + } catch (Exception e) { + LOGGER.error("Error in getAllVulnerabilitiesByAssetGroup", e); + throw new DataException(); + } + } else { + try { + /* + * results = vulnerabilityAssetsCount(assetGroup, targetType, mustFilter, + * occurrenceFieldList, from, size); + */ + results = elasticSearchRepository.getSortedDataFromES(assetGroup, targetType, mustFilter, null, null, + occurrenceFieldList, mustTermsFilter, null); + } catch (Exception e) { + LOGGER.error("Error in getAllVulnerabilitiesByAssetGroup", e); + throw new DataException(); + } + + } + for (Map map : results) { + map.remove("_routing"); + map.remove("_parent"); + map.remove("_id"); + } + return results; + } + + public List> getDetailsByResourceId(String assetGroup, Map mustFilter, + List resourceFieldList, Map mustTermFilter) throws DataException { + List> results = new ArrayList<>(); + try { + + results = elasticSearchRepository.getSortedDataFromES(assetGroup, null, mustFilter, null, null, + resourceFieldList, mustTermFilter, null); + + } catch (Exception e) { + LOGGER.error("Error in getDetailsByResourceId", e); + throw new DataException(); + } + + return results; + } + + /** + * Gets the assets affected count. + * + * @param assetGroup + * the asset group + * @param filter + * the filter + * @param parentType + * the parent type + * @return the assets affected count + */ + public Map getAssetsAffectedCount(String assetGroup, Map filter, String parentType) { + + Map assetsAffectedCount = new HashMap<>(); + Map filterForQuery = new HashMap<>(); + if (!CollectionUtils.isEmpty(filter)) { + filterForQuery = new HashMap<>(filter); + } + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(VULN_INFO); + urlToQuery.append("/").append(SEARCH); + String responseJson = ""; + try { + String severity = SEVERITY_LEVELS; + if (filterForQuery.containsKey(SEVEITY_LEVEL)) { + severity = filterForQuery.get(SEVEITY_LEVEL); + filterForQuery.remove(SEVEITY_LEVEL); + } + + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"terms\":{\"severitylevel\":["); + requestBody.append(severity + "]}},"); + requestBody.append("{\"has_parent\":{\"parent_type\":\"" + parentType + + "\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}}"); + + if (!filterForQuery.isEmpty()) { + requestBody.append(",{\"match\":"); + requestBody.append(new GsonBuilder().create().toJson(filterForQuery)); + requestBody.append("}"); + } + requestBody.append("]}}}}]}},\"aggs\":{\"qid\":{\"terms\":{\"field\":\"qid\",\"size\":"); + requestBody.append(ES_PAGE_SIZE); + requestBody.append("}}}}"); + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getAssetsAffectedCount from ES", e); + } + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray outerBuckets = aggsJson.getAsJsonObject("qid").getAsJsonArray(BUCKETS); + if (outerBuckets.size() > 0) { + for (int i = 0; i < outerBuckets.size(); i++) { + assetsAffectedCount.put( + String.valueOf(outerBuckets.get(i).getAsJsonObject().get("key").getAsLong()), + outerBuckets.get(i).getAsJsonObject().get(DOC_COUNT).getAsLong()); + } + } + } + return assetsAffectedCount; + } + + /** + * Gets the vulnerabily across app and env. + * + * @param assetGroup + * the asset group + * @param filter + * the filter + * @param application + * the application + * @param parentType + * the parent type + * @param severity + * the severity + * @return the vulnerabily across app and env + * @throws Exception + * the exception + */ + public List> getVulnerabilyAcrossAppAndEnv(String assetGroup, String filter, String application, + String parentType, String severity) throws Exception { + + List> vulnApplications = new ArrayList<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(parentType); + urlToQuery.append("/").append(SEARCH); + + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}}"); + if (StringUtils.isNotEmpty(application)) { + requestBody.append(",{\"match\":{\"tags.Application.keyword\":\""); + requestBody.append(application); + requestBody.append("\"}}"); + } + requestBody.append("]}},\"aggs\":{\"apps\":{\"terms\":{\"field\":\""); + requestBody.append(filter); + requestBody.append( + "\",\"size\":10000},\"aggs\":{\"vulns\":{\"children\":{\"type\":\"vulninfo\"},\"aggs\":{\"NAME\":{\"filters\":{\"filters\":{\""); + if (StringUtils.isNotEmpty(severity)) { + requestBody.append("S").append(severity); + requestBody.append("\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"term\":{\"severitylevel\":") + .append(severity).append("}}]}}"); + } else { + requestBody.append( + "S3\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":3}},{\"match\":{\"latest\":true}}]}},\"S4\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":4}},{\"match\":{\"latest\":true}}]}},\"S5\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":5}},{\"match\":{\"latest\":true}}]}}"); + } + requestBody.append("}}}}}}}}}"); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilyAcrossAppAndEnv from ES", e); + throw e; + } + JsonParser jsonParser = new JsonParser(); + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray outerBuckets = aggsJson.getAsJsonObject("apps").getAsJsonArray(BUCKETS); + if (outerBuckets.size() > 0) { + for (int i = 0; i < outerBuckets.size(); i++) { + String appName = outerBuckets.get(i).getAsJsonObject().get("key").getAsString(); + List> severityInfo = getSeverityInfo(outerBuckets.get(i).getAsJsonObject() + .getAsJsonObject(VULN).getAsJsonObject("NAME").getAsJsonObject(BUCKETS), severity); + Map applicationInfo = new HashMap<>(); + if (filter.equals(TAGS_APPS)) { + applicationInfo.put(APPS, appName); + } else { + applicationInfo.put("environment", appName); + } + applicationInfo.put("severityinfo", severityInfo); + if (StringUtils.isEmpty(severity)) { + applicationInfo.put(VULNEREBILITIES, + Integer.valueOf(severityInfo.get(0).get(COUNT).toString()) + + Integer.valueOf(severityInfo.get(1).get(COUNT).toString()) + + Integer.valueOf(severityInfo.get(2).get(COUNT).toString())); + } else { + applicationInfo.put(VULNEREBILITIES, Integer.valueOf(severityInfo.get(0).get(COUNT).toString())); + } + vulnApplications.add(applicationInfo); + } + } + return vulnApplications; + } + + /** + * Gets the vulnerability trend. + * + * @param assetGroup + * the asset group + * @param filter + * the filter + * @param from + * the from + * @param to + * the to + * @return the vulnerability trend + * @throws Exception + * the exception + */ + public List> getVulnerabilityTrend(String assetGroup, Map filter, Date from, + Date to) throws Exception { + List> vulnTrendList = new ArrayList<>(); + try { + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/assetgroup_stats/count_vuln/_search"); + StringBuilder request = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"ag.keyword\":" + "\"" + assetGroup + + "\"}}"); + + if (filter != null) { + Set filterkeys = filter.keySet(); + if (filterkeys.contains(TAGS_APPS)) { + request.append( + ",{ \"match\": {\"tags.Application.keyword\": " + "\"" + filter.get(TAGS_APPS) + "\"}}"); + } + if (filterkeys.contains("tags.Environment.keyword")) { + request.append(",{ \"match\": {\"tags.Environment.keyword\": " + "\"" + + filter.get("tags.Environment.keyword") + "\"}}"); + } + } + String gte = null; + String lte = null; + + if (from != null) { + gte = "\"gte\": \"" + new SimpleDateFormat("yyyy-MM-dd").format(from) + "\""; + } + if (to != null) { + lte = "\"lte\": \"" + new SimpleDateFormat("yyyy-MM-dd").format(to) + "\""; + } + + if (gte != null && lte != null) { + request.append(",{ \"range\": {\"date\": {" + gte + "," + lte + "}}}"); + } else if (gte != null) { + request.append(",{ \"range\": {\"date\": {" + gte + "}}}"); + } else { + request.append(",{ \"range\": {\"date\": {" + lte + "}}}"); + } + + request.append( + "]}},\"aggs\": {\"date\": {\"date_histogram\": {\"field\": \"date\",\"interval\": \"day\",\"format\": \"yyyy-MM-dd\"},\"aggs\": {\"vulns\": {\"sum\": {\"field\": \"count\"}}}}}}"); + + String responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), request.toString()); + JsonParser jsonParser = new JsonParser(); + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonArray buckets = resultJson.get(AGGREGATIONS).getAsJsonObject().get("date").getAsJsonObject() + .get(BUCKETS).getAsJsonArray(); + if (buckets.size() > 0) { + for (int i = 0; i < buckets.size(); i++) { + Map trend = new HashMap<>(); + JsonObject bucket = (JsonObject) buckets.get(i); + String date = bucket.get("key_as_string").getAsString(); + Long count = bucket.get(VULN).getAsJsonObject().get(VALUE).getAsLong(); + trend.put("date", date); + trend.put(COUNT, count); + vulnTrendList.add(trend); + } + } + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilityTrend from ES", e); + throw e; + } + + return vulnTrendList; + } + + /** + * Gets the vulnerabilities distribution. + * + * @param assetGroup + * the asset group + * @param parentType + * the parent type + * @return the vulnerabilities distribution + * @throws Exception + * the exception + */ + public List> getVulnerabilitiesDistribution(String assetGroup, String parentType) + throws Exception { + List> vulnDistributions = new ArrayList<>(); + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(parentType).append("/_search"); + String requestBody = "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}}]}},\"aggs\":{\"apps\":{\"terms\":{\"field\":\"tags.Application.keyword\",\"size\":1000}," + + "\"aggs\":{\"envs\":{\"terms\":{\"field\":\"tags.Environment.keyword\",\"size\":1000},\"aggs\":{\"vulns\":{\"children\":{\"type\":\"vulninfo\"}," + + "\"aggs\":{\"NAME\":{\"filters\":{\"filters\":{\"S3\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":3}},{\"match\":{\"latest\":true}}]}},\"S4\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":4}},{\"match\":{\"latest\":true}}]}},\"S5\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":5}},{\"match\":{\"latest\":true}}]}}}}}}}}}}}}}"; + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody); + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilitiesDistribution from ES", e); + throw e; + } + + JsonParser jsonParser = new JsonParser(); + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray outerBuckets = aggsJson.getAsJsonObject("apps").getAsJsonArray(BUCKETS); + if (outerBuckets.size() > 0) { + for (int i = 0; i < outerBuckets.size(); i++) { + Map applist = new HashMap<>(); + String appName = outerBuckets.get(i).getAsJsonObject().get("key").getAsString(); + JsonArray envs = outerBuckets.get(i).getAsJsonObject().getAsJsonObject("envs").getAsJsonArray(BUCKETS); + List> envDetails = new ArrayList<>(); + if (envs.size() > 0) { + for (int j = 0; j < envs.size(); j++) { + String envName = envs.get(j).getAsJsonObject().get("key").getAsString(); + List> severityInfo = getSeverityInfo(envs.get(j).getAsJsonObject() + .getAsJsonObject(VULN).getAsJsonObject("NAME").getAsJsonObject(BUCKETS), null); + Map envSeverity = new HashMap<>(); + envSeverity.put("environment", envName); + envSeverity.put(SEVERITY_INFO, severityInfo); + envSeverity.put(VULNEREBILITIES, + Integer.valueOf(severityInfo.get(0).get(COUNT).toString()) + + Integer.valueOf(severityInfo.get(ONE).get(COUNT).toString()) + + Integer.valueOf(severityInfo.get(TWO).get(COUNT).toString())); + envDetails.add(envSeverity); + } + } + applist.put(APPS, appName); + applist.put("applicationInfo", envDetails); + vulnDistributions.add(applist); + } + } + return vulnDistributions; + } + + /** + * Gets the vulnerabilitysummary by resource id. + * + * @param resourceId + * the resource id + * @return the vulnerabilitysummary by resource id + */ + public Map getVulnerabilitysummaryByResourceId(String resourceId) { + + Map vulnSummary = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl); + urlToQuery.append("/*");// any index + urlToQuery.append("/").append(VULN_INFO); + urlToQuery.append("/").append(SEARCH); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"match\":{\"_resourceid.keyword\":\""); + requestBody.append(resourceId); + requestBody.append( + "\"}},{\"terms\": {\"severitylevel\": [3,4,5]}}]}},\"aggs\":{\"NAME\":{\"filters\":{\"filters\":{\"S3\":{\"term\":{\"severitylevel\":\"3\"}},\"S4\":{\"term\":{\"severitylevel\":\"4\"}},\"S5\":{\"term\":{\"severitylevel\":\"5\"}}}}}}}"); + + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilitysummaryByResourceId from ES", e); + } + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject hitsJson = (JsonObject) jsonParser.parse(resultJson.get("hits").toString()); + vulnSummary.put(TOTAL, hitsJson.get(TOTAL).getAsLong()); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + try { + vulnSummary.put(SEVERITY_INFO, + getSeverityInfo(aggsJson.getAsJsonObject("NAME").getAsJsonObject(BUCKETS), null)); + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilitysummaryByResourceId ", e); + } + } + return vulnSummary; + + } + + /** + * Gets the vulnerability details by resource id. + * + * @param resourceId + * the resource id + * @return the vulnerability details by resource id + */ + public List> getVulnerabilityDetailsByResourceId(String resourceId) { + + List> results = new ArrayList<>(); + Long totalDocs = (Long) getVulnerabilitysummaryByResourceId(resourceId).get(TOTAL); + StringBuilder urlToQueryBuffer = new StringBuilder(esUrl); + urlToQueryBuffer.append("/*");// any index + urlToQueryBuffer.append("/").append(VULN_INFO); + urlToQueryBuffer.append("/").append(SEARCH).append(SCROLL).append(ES_PAGE_SCROLL_TTL); + + String urlToQuery = urlToQueryBuffer.toString(); + String urlToScroll = new StringBuilder(esUrl).append("/").append(SEARCH).append(SLASH_SCROLL).toString(); + + StringBuilder requestBody = new StringBuilder( + "{\"size\":10000,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"terms\":{\"severitylevel\":[3,4,5]}},{\"match\":{\"_resourceid.keyword\":\""); + requestBody.append(resourceId); + requestBody.append("\"}}]}}}"); + String request = requestBody.toString(); + String scrollId = null; + for (int index = 0; index <= (totalDocs / ES_PAGE_SIZE); index++) { + try { + if (!Strings.isNullOrEmpty(scrollId)) { + request = elasticSearchRepository.buildScrollRequest(scrollId, ES_PAGE_SCROLL_TTL); + urlToQuery = urlToScroll; + } + String responseDetails = PacHttpUtils.doHttpPost(urlToQuery, request); + scrollId = elasticSearchRepository.processResponseAndSendTheScrollBack(responseDetails, results); + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilityDetailsByResourceId", e); + } + } + return results; + } + + /** + * Gets the severity info. + * + * @param countBucket + * the count bucket + * @param severity + * the severity + * @return the severity info + */ + private List> getSeverityInfo(JsonObject countBucket, String severity) { + + List> severityInfo = new ArrayList<>(); + if (StringUtils.isEmpty(severity)) { + Map severity3 = new HashMap<>(); + severity3.put(SEVEITY_LEVEL, THREE); + severity3.put(SEVERITY, "S3"); + severity3.put(COUNT, countBucket.getAsJsonObject("S3").get(DOC_COUNT).getAsLong()); + severity3.put(VULN_COUNT, countBucket.getAsJsonObject("S3").get(DOC_COUNT).getAsLong()); + Map severity4 = new HashMap<>(); + severity4.put(SEVEITY_LEVEL, FOUR); + severity4.put(SEVERITY, "S4"); + severity4.put(COUNT, countBucket.getAsJsonObject("S4").get(DOC_COUNT).getAsLong()); + severity4.put(VULN_COUNT, countBucket.getAsJsonObject("S4").get(DOC_COUNT).getAsLong()); + Map severity5 = new HashMap<>(); + severity5.put(SEVEITY_LEVEL, FIVE); + severity5.put(COUNT, countBucket.getAsJsonObject("S5").get(DOC_COUNT).getAsLong()); + severity5.put(SEVERITY, "S5"); + severity5.put(VULN_COUNT, countBucket.getAsJsonObject("S5").get(DOC_COUNT).getAsLong()); + severityInfo.add(severity3); + severityInfo.add(severity4); + severityInfo.add(severity5); + } else { + Map severityMap = new HashMap<>(); + severityMap.put(SEVEITY_LEVEL, Integer.valueOf(severity)); + severityMap.put(COUNT, countBucket.getAsJsonObject("S" + severity).get(DOC_COUNT).getAsLong()); + severityMap.put(SEVERITY, "S" + severity); + severityMap.put(VULN_COUNT, countBucket.getAsJsonObject("S" + severity).get(DOC_COUNT).getAsLong()); + severityInfo.add(severityMap); + } + + return severityInfo; + } + + /** + * Fetch exec director apps. + * + * @return the list + * @throws Exception + * the exception + */ + @SuppressWarnings("deprecation") + public List> fetchExecDirectorApps() throws Exception { + Map mustFilter = new HashMap<>(); + mustFilter.put(Constants.LATEST, Constants.TRUE); + return elasticSearchRepository.getDataFromES("aws_apps", "apps", mustFilter, null, null, + Arrays.asList("appTag", "director", "executiveSponsor"), null); + + } + + /** + * Gets the unique host. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the unique host + */ + public Map getUniqueHost(String assetGroup, String severity) { + + Map uniqueHost = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(SEARCH); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"has_child\":{\"type\":\"vulninfo\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[" + + severity + "]}}]}}}}]}},\"aggs\":{\"vulninfo\":{\"children\":{\"type\":\"vulninfo\"}," + + "\"aggs\":{\"sev-filter\":{\"filter\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[" + + severity + "]}}]}}," + + "\"aggs\":{\"severity\":{\"terms\":{\"field\":\"severitylevel\",\"size\":5}," + + "\"aggs\":{\"unique-host\":{\"cardinality\":{\"field\":\"_resourceid.keyword\",\"precision_threshold\":40000}}}}}}}}}}"); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error(Constants.ERROR_UNIQUEHOST, e); + } + + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + + JsonObject hitsJson = (JsonObject) jsonParser.parse(resultJson.get(HITS).toString()); + long total = hitsJson.get(TOTAL).getAsLong(); + uniqueHost.put(TOTAL, total); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray buckets = aggsJson.getAsJsonObject(VULN_INFO).getAsJsonObject("sev-filter") + .getAsJsonObject(SEVERITY).getAsJsonArray(BUCKETS); + if (buckets.size() > 0) { + for (int i = 0; i < buckets.size(); i++) { + uniqueHost.put(buckets.get(i).getAsJsonObject().get("key").toString(), buckets.get(i) + .getAsJsonObject().get("unique-host").getAsJsonObject().get(VALUE).getAsLong()); + } + } + } + return uniqueHost; + } + + /** + * Gets the unique vuln. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the unique vuln + */ + public Map getVulnInfo(String assetGroup, String severity) { + + Map vulnInfo = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(SEARCH); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"has_child\":{\"type\":\"vulninfo\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[" + + severity + "]}}]}}}}]}},\"aggs\":{\"vulninfo\":{\"children\":{\"type\":\"vulninfo\"}," + + "\"aggs\":{\"sev-filter\":{\"filter\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[" + + severity + "]}}]}}," + + "\"aggs\":{\"severity\":{\"terms\":{\"field\":\"severitylevel\",\"size\":5}," + + "\"aggs\":{\"unique-qid\":{\"cardinality\":{\"script\":\"doc['qid'].toString().replace('.0','')\",\"precision_threshold\": 40000}}}}}}}}}}"); + + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error(Constants.ERROR_UNIQUEHOST, e); + } + + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + long total = aggsJson.getAsJsonObject(VULN_INFO).getAsJsonObject("sev-filter").get(DOC_COUNT).getAsLong(); + vulnInfo.put(TOTAL, total); + JsonArray buckets = aggsJson.getAsJsonObject(VULN_INFO).getAsJsonObject("sev-filter") + .getAsJsonObject(SEVERITY).getAsJsonArray(BUCKETS); + + Map sevInfo; + if (buckets.size() > 0) { + for (int i = 0; i < buckets.size(); i++) { + String sevKey = buckets.get(i).getAsJsonObject().get("key").toString(); + sevInfo = new HashMap<>(); + sevInfo.put(SEVERITY, sevKey); + sevInfo.put(UNIQUE_VULN_COUNT, + buckets.get(i).getAsJsonObject().get(UNIQUE_QID).getAsJsonObject().get(VALUE).getAsLong()); + sevInfo.put(VULN_COUNT, buckets.get(i).getAsJsonObject().get(DOC_COUNT).getAsLong()); + vulnInfo.put(sevKey, sevInfo); + } + } + } + return vulnInfo; + } + + /** + * Gets the unique app. + * + * @param assetGroup + * the asset group + * @return the unique app + */ + public Map getUniqueApp(String assetGroup) { + + Map uniqueApp = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append("_search?filter_path=aggregations"); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"match\":{\"_entity\":true}}]}}," + + "\"aggs\":{\"severity\":{\"filters\":{\"filters\":{" + + "\"S3\":{\"has_child\":{\"type\":\"vulninfo\",\"query\":{ \"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"match\":{\"severitylevel\":3}}]}}}}," + + "\"S4\":{\"has_child\":{\"type\":\"vulninfo\",\"query\":{ \"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"match\":{\"severitylevel\":4}}]}}}}," + + "\"S5\":{\"has_child\":{\"type\":\"vulninfo\",\"query\":{ \"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"match\":{\"severitylevel\":5}}]}}}}}}," + + "\"aggs\":{\"NAME\":{\"cardinality\":{\"field\":\"tags.Application.keyword\",\"precision_threshold\": 40000}}}}}}"); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error(Constants.ERROR_UNIQUEHOST, e); + } + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonObject buckets = aggsJson.getAsJsonObject(SEVERITY).getAsJsonObject(BUCKETS); + for (int i = 3; i <= 5; i++) { + uniqueApp.put(String.valueOf(i), + buckets.get("S" + i).getAsJsonObject().get("NAME").getAsJsonObject().get(VALUE).getAsLong()); + } + } + return uniqueApp; + } + + /** + * Gets the aging summary. + * + * @param assetGroup + * the asset group + * @return the aging summary + */ + public List> getAgingSummary(String assetGroup) { + + List> agingSummary = new ArrayList<>(); + Map avgAgingMap = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(VULN_INFO); + urlToQuery.append("/").append(SEARCH); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"terms\":{\"severitylevel\":[3,4,5]}}]}}," + + "\"aggs\":{\"severity\":{\"terms\":{\"field\":\"severitylevel\",\"size\":10},\"aggs\":{\"aging\":{\"avg\":{\"field\":\"_vulnage\"}}}}}}"); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getAgingSummary from ES", e); + } + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray buckets = aggsJson.getAsJsonObject(SEVERITY).getAsJsonArray(BUCKETS); + if (buckets.size() > 0) { + for (int i = 0; i < buckets.size(); i++) { + avgAgingMap.put(buckets.get(i).getAsJsonObject().get("key").toString(), Math.floor( + buckets.get(i).getAsJsonObject().get(AGING).getAsJsonObject().get(VALUE).getAsDouble())); + } + } + + avgAgingMap.forEach((severity, avg) -> { + Map sevInfo = new HashMap<>(); + sevInfo.put(SEVERITY, "S" + severity); + sevInfo.put("days", avg); + agingSummary.add(sevInfo); + }); + } + return agingSummary; + } + + /** + * Gets the aging by application. + * + * @param assetGroup + * the asset group + * @param parentType + * the parent type + * @param severity + * the severity + * @return the aging by application + * @throws Exception + * the exception + */ + public List> getAgingByApplication(String assetGroup, String parentType, String severity) + throws Exception { + + List> vulnApplications = new ArrayList<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(parentType); + urlToQuery.append("/").append(SEARCH); + + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}}]}},\"aggs\":{\"apps\":{\"terms\":{\"field\":\"tags.Application.keyword\",\"size\":10000}," + + "\"aggs\":{\"vulns\":{\"children\":{\"type\":\"vulninfo\"},\"aggs\":{\"NAME\":{\"filters\":{\"filters\":{\""); + if (StringUtils.isNotEmpty(severity)) { + requestBody.append("S").append(severity); + requestBody.append("\":{\"bool\":{\"must\":[ {\"match\":{\"latest\":true}},{\"term\":{\"severitylevel\":") + .append(severity).append("}}]}}"); + } else { + requestBody.append( + "S3\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":3}},{\"match\":{\"latest\":true}}]}},\"S4\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":4}},{\"match\":{\"latest\":true}}]}},\"S5\":{\"bool\":{\"must\":[{\"term\":{\"severitylevel\":5}},{\"match\":{\"latest\":true}}]}}"); + } + requestBody.append("}},\"aggs\":{\"aging\":{\"sum\":{\"field\":\"_vulnage\"}}}}}}}}}}"); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getAgingByApplication from ES", e); + throw e; + } + JsonParser jsonParser = new JsonParser(); + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray outerBuckets = aggsJson.getAsJsonObject("apps").getAsJsonArray(BUCKETS); + if (outerBuckets.size() > 0) { + for (int i = 0; i < outerBuckets.size(); i++) { + String appName = outerBuckets.get(i).getAsJsonObject().get("key").getAsString(); + List> agingInfo = getAgingInfo(outerBuckets.get(i).getAsJsonObject() + .getAsJsonObject(VULN).getAsJsonObject("NAME").getAsJsonObject(BUCKETS), severity); + Map applicationInfo = new HashMap<>(); + applicationInfo.put(APPS, appName); + applicationInfo.put("severityinfo", agingInfo); + vulnApplications.add(applicationInfo); + } + } + return vulnApplications; + } + + /** + * Gets the aging info. + * + * @param countBucket + * the count bucket + * @param severity + * the severity + * @return the aging info + * @throws DataException + * the data exception + */ + private List> getAgingInfo(JsonObject countBucket, String severity) throws DataException { + + List> severityInfo = new ArrayList<>(); + if (StringUtils.isEmpty(severity)) { + Map severity3 = new HashMap<>(); + severity3.put(SEVEITY_LEVEL, 3); + severity3.put(SEVERITY, "S3"); + if (countBucket.getAsJsonObject("S3").get(DOC_COUNT).toString().equals(ZERO)) { + severity3.put("days", 0); + severity3.put(COUNT, 0); + } else { + severity3.put(COUNT, countBucket.getAsJsonObject("S3").get(DOC_COUNT).getAsDouble()); + severity3.put("days", Math.floor( + countBucket.getAsJsonObject("S3").get(AGING).getAsJsonObject().get(VALUE).getAsDouble())); + } + Map severity4 = new HashMap<>(); + severity4.put(SEVEITY_LEVEL, 4); + severity4.put(SEVERITY, "S4"); + if (countBucket.getAsJsonObject("S4").get(DOC_COUNT).toString().equals(ZERO)) { + severity4.put("days", 0); + severity4.put(COUNT, 0); + } else { + severity4.put(COUNT, countBucket.getAsJsonObject("S4").get(DOC_COUNT).getAsDouble()); + severity4.put("days", + countBucket.getAsJsonObject("S4").get(AGING).getAsJsonObject().get(VALUE).getAsDouble()); + } + Map severity5 = new HashMap<>(); + severity5.put(SEVEITY_LEVEL, 5); + severity5.put(SEVERITY, "S5"); + if (countBucket.getAsJsonObject("S5").get(DOC_COUNT).toString().equals(ZERO)) { + severity5.put(COUNT, 0); + severity5.put("days", 0); + } else { + severity5.put(COUNT, countBucket.getAsJsonObject("S5").get(DOC_COUNT).getAsDouble()); + severity5.put("days", + countBucket.getAsJsonObject("S5").get(AGING).getAsJsonObject().get(VALUE).getAsDouble()); + } + severityInfo.add(severity3); + severityInfo.add(severity4); + severityInfo.add(severity5); + } else { + Map severityMap = new HashMap<>(); + severityMap.put(SEVEITY_LEVEL, Integer.valueOf(severity)); + severityMap.put(SEVERITY, "S" + severity); + if (countBucket.getAsJsonObject("S" + severity).get(DOC_COUNT).toString().equals(ZERO)) { + severityMap.put("days", 0); + severityMap.put(COUNT, 0); + } else { + severityMap.put(COUNT, countBucket.getAsJsonObject("S" + severity).get(DOC_COUNT).getAsDouble()); + severityMap.put("days", countBucket.getAsJsonObject("S" + severity).get(AGING).getAsJsonObject() + .get(VALUE).getAsDouble()); + } + severityInfo.add(severityMap); + } + + return severityInfo; + } + + /** + * Gets the total qualys host count. + * + * @param index + * the index + * @param vulnType + * the vuln type + * @return the total qualys host count + * @throws DataException + * the data exception + */ + public long getTotalQualysHostCount(String index, String vulnType) throws DataException { + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(index).append("/").append(vulnType) + .append("/").append(UNDERSCORE_COUNT); + StringBuilder requestBody = new StringBuilder( + "{\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"has_child\":{\"type\":\"qualysinfo\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}}]}}}}]}}}"); + try { + String responseDetails = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + JsonObject responseObj = (JsonObject) new JsonParser().parse(responseDetails); + return (long) responseObj.get("count").getAsLong(); + } catch (Exception e) { + LOGGER.error("Error in getTotalQualysAssetCount", e); + throw new DataException(e); + } + } + + /** + * Gets the vulnerability by qid. + * + * @param qid + * the qid + * @return the vulnerability by qid + */ + public Map getVulnerabilityByQid(String qid) { + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append("qualys-kb/kb/_search"); + StringBuilder requestBody = new StringBuilder( + "{\"query\":{\"bool\":{\"must\":[{\"term\":{\"latest\":\"true\"}},{\"term\":{\"qid\":\""); + requestBody.append(qid); + requestBody.append("\"}}]}}}"); + + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilityByQid from ES", e); + } + JsonParser jsonParser = new JsonParser(); + Map vuln = new HashMap<>(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonArray hits = resultJson.get("hits").getAsJsonObject().get("hits").getAsJsonArray(); + if (hits.size() > 0) { + for (int i = 0; i < hits.size(); i++) { + JsonObject obj = (JsonObject) hits.get(i); + JsonObject sourceJson = (JsonObject) obj.get("_source"); + if (sourceJson != null) { + vuln = new Gson().fromJson(sourceJson, new TypeToken>() { + }.getType()); + vuln.remove("latest"); + vuln.remove("_loadDate"); + } + } + } + } + return vuln; + } + + /** + * Gets the unique vuln with parent. + * + * @param assetGroup + * the asset group + * @param severitylevel + * the severitylevel + * @param parentType + * the parent type + * @return the vulnerability by qid + * @throws DataException + * the data exception + */ + public Map getDistributionSummaryByInfraType(String assetGroup, String severitylevel, + String parentType) throws DataException { + + Map infraInfo = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(parentType); + urlToQuery.append("/").append(SEARCH); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}}," + + "{\"has_child\":{\"type\":\"vulninfo\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}}," + + "{\"terms\":{\"severitylevel\":[%s]}}]}}}}]}},\"aggs\":{\"NAME\":{\"children\":{\"type\":\"vulninfo\"}," + + "\"aggs\":{\"NAME\":{\"filter\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[%s]}}]}}," + + "\"aggs\":{\"NAME\":{\"cardinality\":{\"script\":\"doc['qid'].toString().replace('.0','')\",\"precision_threshold\":40000}}}}}}}}"); + String requestJson = String.format(requestBody.toString(), severitylevel, severitylevel); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestJson); + } catch (Exception e) { + LOGGER.error("Error in getDistributionSummaryByInfraType", e); + throw new DataException(e); + } + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject hitsJson = (JsonObject) jsonParser.parse(resultJson.get(HITS).toString()); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + long totalVulnerableAssets = hitsJson.get(TOTAL).getAsLong(); + long vulnerabilities = aggsJson.getAsJsonObject(NAME.toUpperCase()).getAsJsonObject(NAME.toUpperCase()) + .get(DOC_COUNT).getAsLong(); + long uniqueVulnCount = aggsJson.getAsJsonObject(NAME.toUpperCase()).getAsJsonObject(NAME.toUpperCase()) + .getAsJsonObject(NAME.toUpperCase()).get(VALUE).getAsLong(); + + infraInfo.put(TOTAL_VULN_ASSETS, totalVulnerableAssets); + infraInfo.put(VULNEREBILITIES, vulnerabilities); + infraInfo.put(UNIQUE_VULN_COUNT, uniqueVulnCount); + } + return infraInfo; + } + + /** + * Gets the prod info by env. + * + * @param assetGroup + * the asset group + * @param severitylevel + * the severitylevel + * @return the prod info by env + */ + public Map getProdInfoByEnv(String assetGroup, String severitylevel) { + + Map prodInfo = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(SEARCH); + + StringBuilder requestbody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"has_child\":{\"type\":\"vulninfo\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},"); + requestbody.append("{\"terms\":{\"severitylevel\":[%s]}}]}}}}],") + .append("\"should\":[{\"prefix\":{\"tags.Environment.keyword\":\"Production\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"production\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"Prd\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"prd\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"PRD\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"Prod\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"PROD\"}}],") + .append("\"minimum_should_match\":1}},") + .append("\"aggs\":{\"NAME\":{\"children\":{\"type\":\"vulninfo\"},") + .append("\"aggs\":{\"NAME\":{\"filter\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[%s]}}]}},") + .append("\"aggs\":{\"NAME\":{\"cardinality\":{\"script\":\"doc['qid'].toString().replace('.0','')\",\"precision_threshold\": 40000}}}}}}}}"); + String requestJson = String.format(requestbody.toString(), severitylevel, severitylevel); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestJson); + } catch (Exception e) { + LOGGER.error("Error in getProdInfoByEnv", e); + } + + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject hitsJson = (JsonObject) jsonParser.parse(resultJson.get(HITS).toString()); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + long totalVulnerableAssets = hitsJson.get(TOTAL).getAsLong(); + long vulnerabilities = aggsJson.getAsJsonObject(NAME.toUpperCase()).getAsJsonObject(NAME.toUpperCase()) + .get(DOC_COUNT).getAsLong(); + long uniqueVulnCount = aggsJson.getAsJsonObject(NAME.toUpperCase()).getAsJsonObject(NAME.toUpperCase()) + .getAsJsonObject(NAME.toUpperCase()).get(VALUE).getAsLong(); + + prodInfo.put(TOTAL_VULN_ASSETS, totalVulnerableAssets); + prodInfo.put(VULNEREBILITIES, vulnerabilities); + prodInfo.put(UNIQUE_VULN_COUNT, uniqueVulnCount); + } + + return prodInfo; + + } + + /** + * Gets the non prod info by env. + * + * @param assetGroup + * the asset group + * @param severitylevel + * the severitylevel + * @return the non prod info by env + */ + public Map getNonProdInfoByEnv(String assetGroup, String severitylevel) { + + Map nonProdInfo = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(SEARCH); + + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},"); + requestBody.append( + "{\"has_child\":{\"type\":\"vulninfo\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[%s]}}]}}}}],") + .append("\"must_not\":[").append("{\"prefix\":{\"tags.Environment.keyword\":\"Production\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"production\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"Prd\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"prd\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"PRD\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"Prod\"}},") + .append("{\"prefix\":{\"tags.Environment.keyword\":\"PROD\"}}]}},") + .append("\"aggs\":{\"NAME\":{\"children\":{\"type\":\"vulninfo\"},\"aggs\":{\"NAME\":{\"filter\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[%s]}}]}},") + .append("\"aggs\":{\"NAME\":{\"cardinality\":{\"script\":\"doc['qid'].toString().replace('.0','')\",\"precision_threshold\": 40000}}}}}}}}"); + String requestJson = String.format(requestBody.toString(), severitylevel, severitylevel); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestJson); + } catch (Exception e) { + LOGGER.error("Error in getNonProdInfoByEnv", e); + } + + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject hitsJson = (JsonObject) jsonParser.parse(resultJson.get(HITS).toString()); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + long totalVulnerableAssets = hitsJson.get(TOTAL).getAsLong(); + long vulnerabilities = aggsJson.getAsJsonObject(NAME.toUpperCase()).getAsJsonObject(NAME.toUpperCase()) + .get(DOC_COUNT).getAsLong(); + long uniqueVulnCount = aggsJson.getAsJsonObject(NAME.toUpperCase()).getAsJsonObject(NAME.toUpperCase()) + .getAsJsonObject(NAME.toUpperCase()).get(VALUE).getAsLong(); + + nonProdInfo.put(TOTAL_VULN_ASSETS, totalVulnerableAssets); + nonProdInfo.put(VULNEREBILITIES, vulnerabilities); + nonProdInfo.put(UNIQUE_VULN_COUNT, uniqueVulnCount); + } + return nonProdInfo; + + } + + /** + * Gets the distribution summary by vuln type. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the distribution summary by vuln type + * @throws DataException + * the data exception + */ + public List> getDistributionSummaryByVulnType(String assetGroup, String severity) + throws DataException { + + List> distributionList = new ArrayList<>(); + long totalVulnCount = 0; + Map infoOS = new HashMap<>(); + infoOS.put("category", "OS"); + infoOS.put(TOTAL_VULN_ASSETS, 0); + infoOS.put(VULNEREBILITIES, 0); + infoOS.put(UNIQUE_VULN_COUNT, 0); + + Map infoApp = new HashMap<>(); + infoApp.put("category", "Application"); + infoApp.put(TOTAL_VULN_ASSETS, 0); + infoApp.put(VULNEREBILITIES, 0); + infoApp.put(UNIQUE_VULN_COUNT, 0); + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(SEARCH); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},").append( + "{\"has_child\":{\"type\":\"vulninfo\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[%s]}}]}}}}]}},") + .append("\"aggs\":{\"vulninfo\":{\"children\":{\"type\":\"vulninfo\"},\"aggs\":{\"sev-filter\":{\"filter\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[%s]}}]}},") + .append("\"aggs\":{\"classification\":{\"terms\":{\"field\":\"classification.keyword\",\"size\":10},\"aggs\":{\"resources\":{\"cardinality\":{\"field\":\"_resourceid.keyword\",\"precision_threshold\":40000}}}}}}}}}}"); + + String requestJson = String.format(requestBody.toString(), severity, severity); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestJson); + } catch (Exception e) { + LOGGER.error("Error in getVulnerabilitySummaryByClassification from ES", e); + throw new DataException(e); + } + + JsonParser jsonParser = new JsonParser(); + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray buckets = aggsJson.getAsJsonObject(VULN_INFO).getAsJsonObject("sev-filter") + .getAsJsonObject("classification").getAsJsonArray(BUCKETS); + if (buckets.size() > 0) { + for (int i = 0; i < buckets.size(); i++) { + totalVulnCount += buckets.get(i).getAsJsonObject().get(DOC_COUNT).getAsLong(); + if (buckets.get(i).getAsJsonObject().get("key").toString().replace("\"", "").equals("OS")) { + infoOS.put(TOTAL_VULN_ASSETS, + buckets.get(i).getAsJsonObject().get("resources").getAsJsonObject().get(VALUE).getAsLong()); + infoOS.put(VULNEREBILITIES, buckets.get(i).getAsJsonObject().get(DOC_COUNT).getAsLong()); + } else { + infoApp.put(TOTAL_VULN_ASSETS, + buckets.get(i).getAsJsonObject().get("resources").getAsJsonObject().get(VALUE).getAsLong()); + infoApp.put(VULNEREBILITIES, buckets.get(i).getAsJsonObject().get(DOC_COUNT).getAsLong()); + } + } + } + + requestBody = new StringBuilder("{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},") + .append("{\"has_child\":{\"type\":\"vulninfo\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[%s]}}]}}}}]}},") + .append("\"aggs\":{\"vulninfo\":{\"children\":{\"type\":\"vulninfo\"},\"aggs\":{\"sev-filter\":{\"filter\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[%s]}}]}},") + .append("\"aggs\":{\"classification\":{\"terms\":{\"field\":\"classification.keyword\",\"size\":10},\"aggs\":{\"unique-qid\":{\"cardinality\":{\"script\":\"doc['qid'].toString().replace('.0','')\",\"precision_threshold\":40000}}}}}}}}}}"); + + requestJson = String.format(requestBody.toString(), severity, severity); + responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestJson); + } catch (Exception e) { + LOGGER.error(Constants.ERROR_UNIQUEHOST, e); + throw new DataException(e); + } + + resultJson = (JsonObject) jsonParser.parse(responseJson); + aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + buckets = aggsJson.getAsJsonObject(VULN_INFO).getAsJsonObject("sev-filter").getAsJsonObject("classification") + .getAsJsonArray(BUCKETS); + if (buckets.size() > 0) { + for (int i = 0; i < buckets.size(); i++) { + if (buckets.get(i).getAsJsonObject().get("key").toString().replace("\"", "").equals("OS")) { + infoOS.put(UNIQUE_VULN_COUNT, + buckets.get(i).getAsJsonObject().get(UNIQUE_QID).getAsJsonObject().get(VALUE).getAsLong()); + } else { + infoApp.put(UNIQUE_VULN_COUNT, + buckets.get(i).getAsJsonObject().get(UNIQUE_QID).getAsJsonObject().get(VALUE).getAsLong()); + } + } + } + + if (totalVulnCount > 0) { + distributionList.add(infoOS); + distributionList.add(infoApp); + } + double contribution = HUNDRED; + for (int i = 0; i < distributionList.size(); i++) { + Map info = distributionList.get(i); + if (totalVulnCount > 0) { + double contributionPercent = Math + .floor((Double.valueOf(info.get(VULNEREBILITIES).toString()) / totalVulnCount) * HUNDRED); + if (i == distributionList.size() - 1) { + info.put("contribution", contribution); + } else { + info.put("contribution", contributionPercent); + contribution = contribution - contributionPercent; + } + } + } + return distributionList; + } + + /** + * Gets the all qid by AG. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the all qid by AG + * @throws DataException + * the data exception + */ + public Map getAllQidByAG(String assetGroup, String severity) throws DataException { + + Map qids = new HashMap<>(); + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(VULN_INFO); + urlToQuery.append("/").append(SEARCH); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":["); + requestBody.append(severity); + requestBody.append( + "]}}]}},\"aggs\":{\"qid\":{\"terms\":{\"script\":\"(doc['qid'].value+'~').replace('.0','')+doc['title.keyword'].value.toLowerCase()+'~'+doc['classification.keyword'].value\",\"size\":100000}}}}"); + + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getAllQidByAG from ES", e); + throw new DataException(e); + } + JsonParser jsonParser = new JsonParser(); + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray buckets = aggsJson.getAsJsonObject("qid").getAsJsonArray(BUCKETS); + if (buckets.size() > 0) { + for (int i = 0; i < buckets.size(); i++) { + qids.put(buckets.get(i).getAsJsonObject().get("key").toString().replace("\"", ""), + buckets.get(i).getAsJsonObject().get(DOC_COUNT)); + } + } + return qids; + } + + /** + * Gets the apps by severity. + * + * @param assetGroup + * the asset group + * @param parentType + * the parent type + * @param severity + * the severity + * @return the apps by severity + * @throws Exception + * the exception + */ + public Map getAppsBySeverity(String assetGroup, String parentType, String severity) throws Exception { + + Map appDetails = new HashMap<>(); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup); + urlToQuery.append("/").append(parentType); + urlToQuery.append("/").append(SEARCH); + + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}}]}}," + + "\"aggs\":{\"apps\":{\"terms\":{\"field\":\"tags.Application.keyword\",\"size\":10000}," + + "\"aggs\":{\"vulns\":{\"children\":{\"type\":\"vulninfo\"},\"aggs\":{\"NAME\":{\"filters\":{\"filters\":{\"severity\":{\"terms\":{\"severitylevel\":["); + requestBody.append(severity); + requestBody.append("]}}}}}}}}}}}"); + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getAppsBySeverity from ES", e); + throw e; + } + JsonParser jsonParser = new JsonParser(); + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray buckets = aggsJson.getAsJsonObject("apps").getAsJsonArray(BUCKETS); + if (buckets.size() > 0) { + for (int i = 0; i < buckets.size(); i++) { + appDetails.put(buckets.get(i).getAsJsonObject().get("key").getAsString(), + buckets.get(i).getAsJsonObject().getAsJsonObject("vulns").getAsJsonObject("NAME") + .getAsJsonObject("buckets").getAsJsonObject("severity").get(DOC_COUNT).getAsLong()); + } + } + return appDetails; + } + + /** + * Creates the trend annotation. + * + * @param request + * the request + * @return true, if successful + * @throws JsonProcessingException + * the json processing exception + */ + public boolean createTrendAnnotation(TrendNote request) throws JsonProcessingException { + + SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd"); + SimpleDateFormat dateformatId = new SimpleDateFormat("yyyyMMdd"); + String assetGroup; + if (StringUtils.isBlank(request.getAg())) { + assetGroup = ""; + } else { + assetGroup = request.getAg(); + } + Date date = request.getDate(); + Map payLoad = new HashMap<>(); + payLoad.put("ag", assetGroup); + payLoad.put("note", request.getNote()); + payLoad.put("date", dateformat.format(date)); + if (StringUtils.isEmpty(assetGroup)) { + payLoad.put(NOTE_ID, dateformatId.format(date)); + } else { + payLoad.put(NOTE_ID, assetGroup + "_" + dateformatId.format(date)); + } + + List> docs = new ArrayList<>(); + docs.add(payLoad); + createIndex("assetgroup_annotations"); + return uploadData("assetgroup_annotations", "annotations", docs, NOTE_ID); + } + + /** + * Creates the index. + * + * @param indexName + * the index name + */ + public void createIndex(String indexName) { + if (!indexExists(indexName)) { + String payLoad = "{\"settings\": { \"index.mapping.ignore_malformed\": true }}"; + invokeAPI("PUT", indexName, payLoad); + } + } + + /** + * Index exists. + * + * @param indexName + * the index name + * @return true, if successful + */ + private boolean indexExists(String indexName) { + Response response = invokeAPI("HEAD", indexName, null); + if (response != null) { + return response.getStatusLine().getStatusCode() == 200 ? true : false; + } + return false; + } + + /** + * Upload data. + * + * @param index + * the index + * @param type + * the type + * @param docs + * the docs + * @param idKey + * the id key + * @return true, if successful + */ + private boolean uploadData(String index, String type, List> docs, String idKey) { + String actionTemplate = "{ \"index\" : { \"_index\" : \"%s\", \"_type\" : \"%s\", \"_id\" : \"%s\"} }%n"; + + LOGGER.info("*********UPLOADING*** " + type); + if (null != docs && !docs.isEmpty()) { + StringBuilder bulkRequest = new StringBuilder(); + int i = 0; + for (Map doc : docs) { + if (doc != null) { + String id = doc.get(idKey).toString(); + StringBuilder docStrBuilder = new StringBuilder(createESDoc(doc)); + + if (docStrBuilder != null) { + bulkRequest.append(String.format(actionTemplate, index, type, id)); + bulkRequest.append(docStrBuilder + "\n"); + } + i++; + if (i % Constants.THOUSAND == 0 || bulkRequest.toString().getBytes().length + / (Constants.THOUSAND_TWENTY_FOUR * Constants.THOUSAND_TWENTY_FOUR) > Constants.FIVE) { + LOGGER.info("Uploaded" + i); + Response resp = invokeAPI("POST", "/_bulk?refresh=true", bulkRequest.toString()); + try { + String responseStr = ""; + if (null != resp) { + responseStr = EntityUtils.toString(resp.getEntity()); + } + if (responseStr.contains("\"errors\":true")) { + Response retryResp = invokeAPI("POST", "/_bulk?refresh=true", bulkRequest.toString()); + String retryResponse = ""; + if (null != retryResp) { + retryResponse = EntityUtils.toString(retryResp.getEntity()); + } + if (retryResponse.contains("\"errors\":true")) { + LOGGER.error(retryResponse); + } + } + } catch (Exception e) { + LOGGER.error("Bulk upload failed", e); + return false; + } + bulkRequest = new StringBuilder(); + } + } + } + if (bulkRequest.length() > 0) { + LOGGER.info("Uploaded" + i); + Response resp = invokeAPI("POST", "/_bulk?refresh=true", bulkRequest.toString()); + try { + String responseStr = ""; + if (null != resp) { + responseStr = EntityUtils.toString(resp.getEntity()); + } + if (responseStr.contains("\"errors\":true")) { + Response retryResp = invokeAPI("POST", "/_bulk?refresh=true", bulkRequest.toString()); + String retryResponse = ""; + if (null != retryResp) { + retryResponse = EntityUtils.toString(retryResp.getEntity()); + } + + if (retryResponse.contains("\"errors\":true")) { + LOGGER.error(retryResponse); + } + } + if (null != resp) { + return resp.getStatusLine().getStatusCode() == 200 ? true : false; + } else { + return false; + } + } catch (Exception e) { + LOGGER.error("Bulk upload failed", e); + return false; + } + } + } + return true; + } + + /** + * Creates the ES doc. + * + * @param doc + * the doc + * @return the string + */ + private String createESDoc(Map doc) { + ObjectMapper objMapper = new ObjectMapper(); + String docJson = "{}"; + try { + docJson = objMapper.writeValueAsString(doc); + } catch (JsonProcessingException e) { + LOGGER.error("Error in createESDoc", e); + } + return docJson; + } + + /** + * Invoke API. + * + * @param method + * the method + * @param endpoint + * the endpoint + * @param payLoad + * the pay load + * @return the response + */ + private Response invokeAPI(String method, String endpoint, String payLoad) { + HttpEntity entity = null; + try { + if (payLoad != null) { + entity = new NStringEntity(payLoad, ContentType.APPLICATION_JSON); + } + return getRestClient().performRequest(method, endpoint, Collections.emptyMap(), entity); + } catch (IOException e) { + LOGGER.error("Error in invokeAPI", e); + } + return null; + } + + /** + * Gets the rest client. + * + * @return the rest client + */ + private RestClient getRestClient() { + if (restClient == null) { + restClient = RestClient.builder(new HttpHost(updateESHost, updateESPort)).build(); + } + return restClient; + } + + /** + * Gets the trend annotations. + * + * @param ag + * the ag + * @param from + * the from + * @return the trend annotations + */ + public List> getTrendAnnotations(String ag, Date from) { + + List> notes = new ArrayList<>(); + SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd"); + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/") + .append("assetgroup_annotations/annotations/_search"); + StringBuilder requestBody = new StringBuilder( + "{\"size\":10000,\"query\":{\"bool\":{\"must\":[{\"range\":{\"date\":{\"gte\":\""); + requestBody.append(dateformat.format(from)); + requestBody.append("\",\"lte\":\""); + requestBody.append(dateformat.format(new Date())); + requestBody.append("\",\"format\":\"yyyy-MM-dd\"}}}"); + requestBody.append(",{\"terms\":{\"ag.keyword\":[\"\",\""); + requestBody.append(ag).append("\"]}}]}}}"); + + String responseJson = ""; + try { + responseJson = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getTrendAnnotations from ES", e); + } + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseJson)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseJson); + JsonArray hits = resultJson.get("hits").getAsJsonObject().get("hits").getAsJsonArray(); + Map note; + if (hits.size() > 0) { + for (int i = 0; i < hits.size(); i++) { + JsonObject obj = (JsonObject) hits.get(i); + JsonObject sourceJson = (JsonObject) obj.get("_source"); + if (sourceJson != null) { + note = new Gson().fromJson(sourceJson, new TypeToken>() { + }.getType()); + notes.add(note); + } + } + } + } + return notes; + } + + /** + * Delete trend annotation. + * + * @param noteId + * the note id + * @return true, if successful + */ + public boolean deleteTrendAnnotation(String noteId) { + boolean result = false; + try { + result = invokeAPI("POST", "assetgroup_annotations/annotations/_delete_by_query?refresh&q=_id:" + noteId, + null).getStatusLine().getStatusCode() == 200; + } catch (Exception e) { + LOGGER.error("Error in deleteTrendAnnotation ", e); + } + return result; + } + + /** + * Gets the data from pacman RDS. + * + * @param query + * the query + * @return the data from pacman RDS + */ + public List> getDataFromPacmanRDS(String query) { + return rdsRepository.getDataFromPacman(query); + } + + /** + * Gets the running instances count. + * + * @param index + * the index + * @param vulnType + * the vuln type + * @return the running instances count + * @throws DataException + * the data exception + */ + public long getRunningInstancesCount(String index, String vulnType) throws DataException { + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(index).append("/").append(vulnType) + .append("/").append(UNDERSCORE_COUNT); + StringBuilder requestBody = new StringBuilder( + "{\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}}"); + if (vulnType.equals(EC2)) { + requestBody.append(",{\"match\":{\"statename\":\"running\"}}"); + } + requestBody.append("]}}}"); + + try { + String responseDetails = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + JsonObject responseObj = (JsonObject) new JsonParser().parse(responseDetails); + return responseObj.get("count").getAsLong(); + } catch (Exception e) { + LOGGER.error("Error in getRunningInstancesCount", e); + throw new DataException(e); + } + } + + /** + * Gets the exempted by rule count. + * + * @param index + * the index + * @param vulnType + * the vuln type + * @return the exempted by rule count + * @throws DataException + * the data exception + */ + public long getExemptedByRuleCount(String index, String vulnType) throws DataException { + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(index).append("/") + .append(UNDERSCORE_COUNT); + StringBuilder requestBody = new StringBuilder( + "{\"query\":{\"bool\":{\"must\":[{\"match\":{\"issueStatus.keyword\":\"exempted\"}},{\"match\":{\"ruleId.keyword\":\""); + if (vulnType.equals(EC2)) { + requestBody.append(EC2_QUALYS_RULEID); + }else if(vulnType.equals(VIRTUALMACHINE)) { + requestBody.append(VIRTUALMACHINE_QUALYS_RULEID); + }else { + requestBody.append( + "PacMan_Onprem-asset-scanned-by-qualys-API_version-1_OnpremassetscannedbyqualysAPI_onpremserver"); + } + requestBody.append("\"}}]}}}"); + + try { + String responseDetails = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + JsonObject responseObj = (JsonObject) new JsonParser().parse(responseDetails); + return responseObj.get("count").getAsLong(); + } catch (Exception e) { + LOGGER.error("Error in getExemptedCount", e); + throw new DataException(e); + } + } + + /** + * Gets the unique host by severity. + * + * @param index + * the index + * @param severity + * the severity + * @return the unique host by severity + * @throws DataException + * the data exception + */ + public Map getUniqueHostBySeverity(String index, String severity) throws DataException { + + Map severityInfo = new HashMap<>(); + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(index).append("/vulninfo/_search"); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"terms\":{\"severitylevel\":["); + requestBody.append(severity); + requestBody.append("]}}]}},\"aggs\":{\"severity\":{\"terms\":{\"field\":\"severitylevel\",\"size\":10}}}}"); + + String responseDetails = ""; + try { + responseDetails = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getUniqueHostBySeverity", e); + throw new DataException(e); + } + + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseDetails)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseDetails); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray outerBuckets = aggsJson.getAsJsonObject("severity").getAsJsonArray(BUCKETS); + if (outerBuckets.size() > 0) { + for (int i = 0; i < outerBuckets.size(); i++) { + severityInfo.put("S" + outerBuckets.get(i).getAsJsonObject().get("key").toString(), + outerBuckets.get(i).getAsJsonObject().get(DOC_COUNT).getAsLong()); + } + } else { + severityInfo.put("S3",0); + severityInfo.put("S4",0); + severityInfo.put("S5",0); + } + } + return severityInfo; + } + + /** + * Gets the compliant hosts by severity. + * + * @param assetGroup + * the asset group + * @return the compliant hosts by severity + * @throws DataException + * the data exception + */ + public Map getCompliantHostsBySeverity(String assetGroup) throws DataException { + + Map severityInfo = new HashMap<>(); + + List nonCompliantResourceIds = getNonCompliantResourceIds(assetGroup); + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(assetGroup).append("/vulninfo/_search"); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":true}},{\"terms\":{\"severitylevel\":[3,4,5]}}],\"must_not\":[{\"terms\":{\"_resourceid.keyword\":"); + requestBody.append(nonCompliantResourceIds); + requestBody.append("}}]}},\"aggs\":{\"severity\":{\"terms\":{\"field\":\"severitylevel\",\"size\":10}}}}"); + + String responseDetails = ""; + try { + responseDetails = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getCompliantHostsBySeverity", e); + throw new DataException(e); + } + + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseDetails)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseDetails); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray outerBuckets = aggsJson.getAsJsonObject("severity").getAsJsonArray(BUCKETS); + if (outerBuckets.size() > 0) { + for (int i = 0; i < outerBuckets.size(); i++) { + severityInfo.put("S" + outerBuckets.get(i).getAsJsonObject().get("key").toString(), + outerBuckets.get(i).getAsJsonObject().get(DOC_COUNT).getAsLong()); + } + } else { + severityInfo.put("S3",0); + severityInfo.put("S4",0); + severityInfo.put("S5",0); + } + } + + return severityInfo; + } + + /** + * Gets the non compliant resource ids. + * + * @param index + * the index + * @return the non compliant resource ids + * @throws DataException + * the data exception + */ + public List getNonCompliantResourceIds(String index) throws DataException { + + List resourceIds = new ArrayList<>(); + + StringBuilder urlToQuery = new StringBuilder(esUrl).append("/").append(index).append("/vulninfo/_search"); + StringBuilder requestBody = new StringBuilder( + "{\"size\":0,\"query\":{\"bool\":{\"must\":[{\"match\":{\"latest\":\"true\"}},{\"terms\":{\"severitylevel\":[5]}}]}}," + + "\"aggs\":{\"resourceid\":{\"terms\":{\"field\":\"_resourceid.keyword\",\"size\":10000}}}}"); + + String responseDetails = ""; + try { + responseDetails = PacHttpUtils.doHttpPost(urlToQuery.toString(), requestBody.toString()); + } catch (Exception e) { + LOGGER.error("Error in getNonCompliantResourceIds", e); + throw new DataException(e); + } + + JsonParser jsonParser = new JsonParser(); + if (StringUtils.isNotEmpty(responseDetails)) { + JsonObject resultJson = (JsonObject) jsonParser.parse(responseDetails); + JsonObject aggsJson = (JsonObject) jsonParser.parse(resultJson.get(AGGREGATIONS).toString()); + JsonArray outerBuckets = aggsJson.getAsJsonObject("resourceid").getAsJsonArray(BUCKETS); + if (outerBuckets.size() > 0) { + for (int i = 0; i < outerBuckets.size(); i++) { + resourceIds.add(outerBuckets.get(i).getAsJsonObject().get("key").toString()); + } + } + } + return resourceIds; + } + + @SuppressWarnings("deprecation") + public List> fetchOrgInfoForApps() throws Exception { + Map mustFilter = new HashMap<>(); + mustFilter.put(Constants.LATEST, Constants.TRUE); + return elasticSearchRepository.getDataFromES("aws_apps", "apps", mustFilter, null, null, + Arrays.asList("appTag", "_orgInfo.mgmntLevel", "_orgInfo.name", "_orgInfo.isOwner"), null); + + } + + public List> getTrendProgress(String assetGroup, String ruleId, LocalDate startDate, + LocalDate endDate, String trendCategory) throws DataException { + Map mustFilter = new HashMap<>(); + mustFilter.put(CommonUtils.convertAttributetoKeyword("ag"), assetGroup); + if ("issuecompliance".equals(trendCategory)) { + mustFilter.put(CommonUtils.convertAttributetoKeyword("ruleId"), ruleId); + } + + Map rangeMap = new HashMap<>(); + rangeMap.put("gte", startDate.format(DateTimeFormatter.ISO_DATE)); + rangeMap.put("lte", endDate.format(DateTimeFormatter.ISO_DATE)); + + Map dateRangeMap = new HashMap<>(); + dateRangeMap.put("date", rangeMap); + + mustFilter.put(RANGE, dateRangeMap); + try { + return elasticSearchRepository.getSortedDataFromES(AG_STATS, trendCategory, mustFilter, null, null, + Arrays.asList("date", "total", "compliant", "noncompliant", "compliance_percent"), null, null); + } catch (Exception e) { + throw new DataException(e); + } + } + + public int vulnerabilityAssetsCount(String assetGroup, String targetType, Map mustFilter, int from, + int size, Map mustTermsFilter) throws Exception { + List> results = new ArrayList<>(); + results = elasticSearchRepository.getSortedDataFromES(assetGroup, targetType, mustFilter, null, null, null, + mustTermsFilter, null); + return results.size(); + } +} \ No newline at end of file diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityTrendGenerator.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityTrendGenerator.java new file mode 100644 index 000000000..92748aacf --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityTrendGenerator.java @@ -0,0 +1,598 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :kkumar28 + Modified Date: Oct 20, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.repository; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.entity.NStringEntity; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Repository; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.utils.PacHttpUtils; + + +/** + * The Class VulnerabilityTrendGenerator. + */ +@Repository +public class VulnerabilityTrendGenerator implements Constants { + + /** The es host. */ + @Value("${elastic-search.host}") + private String esHost; + + /** The es port. */ + @Value("${elastic-search.port}") + private int esPort; + + /** The es cluster name. */ + @Value("${elastic-search.clusterName}") + private String esClusterName; + + /** The date format. */ + @Value("${formats.date}") + private String dateFormat; + + /** The Constant LOGGER. */ + private static final Logger LOGGER = LoggerFactory + .getLogger(VulnerabilityTrendGenerator.class); + + /** + * Generate trend. + * + * @param ag the ag + * @param severity the severity + * @param fromDate the from date + * @return the list + * @throws Exception the exception + */ + + public List> generateTrend(String ag,String severity, Date fromDate) throws Exception { + //long start = System.currentTimeMillis(); + List> dateList = new ArrayList<>(); + LocalDate from = LocalDate.parse(new SimpleDateFormat("yyyy-MM-dd").format(fromDate)); + + + + String queryBody = "\"query\":{\"bool\":{\"must\":[{\"terms\":{\"severitylevel\":["+severity+"]}}],\"should\":[{\"range\":{\"_closedate\":{\"gte\":\""+from+"\"}}},{\"match\":{\"_status\":\"open\"}}],\"minimum_should_match\":1}}"; + + + long totalCount = getTotalDocCount(ag,"vulninfo","{"+queryBody+"}"); + //System.out.println("TOtal Count Open New"+totalCount); + if (totalCount > 0) { + + + List issueOpenDates = new ArrayList<>(); + ExecutorService executionService = Executors.newFixedThreadPool(2); + Map newFoundMap = new HashMap<>(); + Map openCountMap = new HashMap<>(); + executionService.execute(()-> { + newFoundMap.putAll(fetchNewlyFoundVulnByDay(ag,severity,from)); + }); + + executionService.execute( () -> { + + // ES needs minimum 2 slices, if records are less , we need to slice + // accordingly + final int scrollSize = totalCount > 10000 ? 10000 + : (int) (totalCount / 2) + 1; + + final int slices = totalCount > scrollSize ? (int) totalCount + / scrollSize + 1 : 2; + + IntStream.range(0, slices) + .parallel() + .forEach(i -> { + List issueOpenDatesList = fetchVulnInfoDateRanges(ag,scrollSize,slices,i,queryBody,from); + synchronized(issueOpenDates){ + issueOpenDates.addAll(issueOpenDatesList); + } + }); + + openCountMap.putAll(issueOpenDates.parallelStream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))); + + }); + + executionService.shutdown(); + while(!executionService.isTerminated()){} + + + + openCountMap.entrySet().forEach(entry->{ + Map dateObj = new HashMap<>(); + String date = entry.getKey(); + Long open = entry.getValue(); + Long newlyFound = newFoundMap.get(date); + + dateObj.put("date",date); + dateObj.put("open",open); + dateObj.put("new",newlyFound==null?0l:newlyFound); + dateList.add(dateObj); + + }); + //System.out.println("Time taken Generate Trend :"+(System.currentTimeMillis()-start)); + return dateList ; + } else { + + throw new DataException(NO_DATA_FOUND); + } + } + + /** + * Fetch newly found vuln by day. + * + * @param ag the ag + * @param severity the severity + * @param from the from + * @return the map + */ + private Map fetchNewlyFoundVulnByDay(String ag,String severity,LocalDate from ){ + + StringBuilder queryBody = new StringBuilder(); + queryBody.append("\"query\":{\"bool\":{\"must\":["). + append("{\"terms\":{\"severitylevel\":["+severity+"]}}"). + append(",{\"range\":{\"_firstFound\":{\"gte\":\""). + append(from.toString()). + append("\"}}}]}}"); + + String searchUrl = "http://"+esHost+":"+esPort+"/"+ag+"/vulninfo/_search?size=0"; + StringBuilder request = new StringBuilder(); + request.append("{"). + append("\"aggs\":{\"dates\":{\"date_histogram\":{\"field\":\"_firstFound\",\"interval\":\"day\",\"format\":\"yyyy-MM-dd\"}}}"). + append(",").append(queryBody).append("}"); + + Map newFoundMap = new HashMap<>(); + try { + String searchResponse = PacHttpUtils.doHttpPost(searchUrl, request.toString()); + JsonParser parser = new JsonParser(); + JsonObject responeObj = parser.parse(searchResponse).getAsJsonObject(); + JsonArray dateBuckets = responeObj.getAsJsonObject("aggregations").getAsJsonObject("dates").getAsJsonArray("buckets"); + for(JsonElement jsonElement:dateBuckets){ + JsonObject dateObj = jsonElement.getAsJsonObject(); + newFoundMap.put(dateObj.get("key_as_string").getAsString(),dateObj.get("doc_count").getAsLong()); + + } + } catch (Exception e) { + LOGGER.error("error",e); + } + + return newFoundMap; + + } + + /** + * Fetch vuln info date ranges. + * + * @param ag the ag + * @param scrollSize the scroll size + * @param slices the slices + * @param sliceNo the slice no + * @param queryBody the query body + * @param from the from + * @return the list + */ + @SuppressWarnings("unchecked") + private List fetchVulnInfoDateRanges(String ag,int scrollSize,int slices,int sliceNo,String queryBody,LocalDate from){ + //String searchUrl = "http://"+esHost+":"+esPort+"/"+ag+"/vulninfo/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + String searchUrl = "/"+ag+"/vulninfo/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + StringBuilder request = new StringBuilder(); + request.append("{\"size\":"). + append(scrollSize). + append(",\"_source\":[\"_firstFound\",\"_closedate\"],"). + append("\"slice\": {\"id\":"). + append(sliceNo).append(",\"max\":").append(slices).append("},"). + append(queryBody).append("}"); + try{ + List> hitsList = scrollAndFetch(searchUrl,request.toString()); + return hitsList.parallelStream().map(obj-> (Map)obj.get("_source")).flatMap( obj-> getDateRange(obj.get("_firstFound"),obj.get("_closedate"),from,DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[Z]['Z']")).stream()).collect(Collectors.toList()); + + }catch(Exception e){ + LOGGER.error("error",e); + } + return new ArrayList<>(); + + } + + + private List> scrollAndFetch(String searchUrl, String requestBody){ + List> hitsList = new ArrayList<>(); + String uri = searchUrl; + String request = requestBody; + String searchResponse ; + JsonParser parser = new JsonParser(); + JsonObject responeObj ; + long total ; + String scrollId ; + JsonArray hits; + //String scrollUri ="http://"+esHost+":"+esPort+"/_search/scroll"; + String scrollUri ="/_search/scroll?filter_path=hits.total,hits.hits._source,_scroll_id"; + String scrollRequest = "{\"scroll\":\"2m\",\"scroll_id\":\"%s\"}"; + try{ + do{ + + //searchResponse = PacHttpUtils.doHttpPost(uri, request); + searchResponse = invokeESCall("GET",uri, request); + parser = new JsonParser(); + responeObj = parser.parse(searchResponse).getAsJsonObject(); + scrollId = responeObj.get("_scroll_id").getAsString(); + total = responeObj.getAsJsonObject("hits").get("total").getAsLong(); + hits = responeObj.getAsJsonObject("hits").getAsJsonArray("hits"); + hitsList.addAll(new Gson().fromJson(hits,new TypeToken>>(){}.getType())); + uri = scrollUri ; + request = String.format(scrollRequest, scrollId); + //System.out.println("SCROLL:"+total+":"+hitsList.size()); + if(hits.size()==0) break; + }while(total>hitsList.size()); + }catch(Exception e){ + //System.out.println(uri); + //System.out.println(request); + LOGGER.error("error",e); + } + + return hitsList; + } + + /** + * Gets the total doc count. + * + * @param ag the ag + * @param type the type + * @param queryBody the query body + * @return the total doc count + * @throws DataException the data exception + */ + private long getTotalDocCount(String ag, String type, String queryBody) throws DataException{ + String countUrl = "http://"+esHost+":"+esPort+"/"+ag+"/"+type+"/_count"; + String countResponse = ""; + try { + countResponse = PacHttpUtils.doHttpPost(countUrl, queryBody); + } catch (Exception e) { + throw new DataException(e); + } + JsonParser jsonParser = new JsonParser(); + JsonObject response = jsonParser.parse(countResponse).getAsJsonObject(); + return Double.valueOf(response.get("count").getAsString()).longValue(); + + } + + /** + * Gets the date range. + * + * @param from the from + * @param to the to + * @param excludeBefore the exclude before + * @param inputFormatter the input formatter + * @return the date range + */ + private List getDateRange(Object from, Object to, LocalDate excludeBefore, DateTimeFormatter inputFormatter){ + LocalDate fromDt; + LocalDate toDt; + List dateRage = new ArrayList<>(); + if(from!=null){ + fromDt = LocalDateTime.parse(from.toString(),inputFormatter).toLocalDate(); + if(to==null){ + toDt = LocalDate.now(); + }else{ + toDt = LocalDateTime.parse(to.toString(),inputFormatter).toLocalDate(); + } + DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE; + while(fromDt.isBefore(toDt)){ + if(!fromDt.isBefore(excludeBefore)){ + dateRage.add(formatter.format(fromDt)); + } + fromDt = fromDt.plusDays(1); + } + } + return dateRage; + } + + private List getDateRangeWithResourceId(Object from, Object to, LocalDate excludeBefore, DateTimeFormatter inputFormatter,String resourceId){ + LocalDate fromDt; + LocalDate toDt; + List dateRage = new ArrayList<>(); + if(from!=null){ + fromDt = LocalDateTime.parse(from.toString(),inputFormatter).toLocalDate(); + if(to==null){ + toDt = LocalDate.now(); + }else{ + toDt = LocalDateTime.parse(to.toString(),inputFormatter).toLocalDate(); + } + DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE; + while(fromDt.isBefore(toDt)){ + if(!fromDt.isBefore(excludeBefore)){ + dateRage.add(new DateResInfo(formatter.format(fromDt),resourceId)); + } + fromDt = fromDt.plusDays(1); + } + } + return dateRage; + } + + /** + * Fetch assets date ranges for ec 2. + * + * @param ag the ag + * @param from the from + * @param isAssetsAffected the is assets affected + * @return the list + * @throws DataException the data exception + */ + @SuppressWarnings("unchecked") + public List fetchAssetsDateRangesForEc2(String ag, LocalDate from) throws DataException { + List dates = new ArrayList<>(); + //String searchUrl = "http://"+esHost+":"+esPort+"/"+ag+"/ec2/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + String searchUrl = "/"+ag+"/ec2/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + StringBuilder request = new StringBuilder(); + request.append("{\"query\":{\"range\": {\"discoverydate.keyword\": {\"gte\":\""+from+"\"}}}}"); + long totalCount = getTotalDocCount(ag,"ec2",request.toString()); + //System.out.println("fetchAssetsDateRangesForEc2 Total Count :"+ totalCount ); + if (totalCount > 0) { + final int scrollSize = totalCount > 10000 ? 10000 + : (int) (totalCount / 2) + 1; + + final int slices = totalCount > scrollSize ? (int) totalCount + / scrollSize + 1 : 2; + + IntStream.range(0, slices).parallel().forEach(i -> { + StringBuilder query = new StringBuilder("{\"size\":").append(scrollSize); + query.append(",\"_source\":[\"firstdiscoveredon\",\"discoverydate\"],"); + query.append("\"slice\": {\"id\":").append(i).append(",\"max\":").append(slices).append("},"); + query.append(request.toString().substring(1, request.length())); + List ec2DatesList = new ArrayList<>(); + try{ + List> hitsList = scrollAndFetch(searchUrl,query.toString()); + ec2DatesList = hitsList.parallelStream().map(obj-> (Map)obj.get("_source")).flatMap( obj-> + getDateRange(obj.get(Constants.FIRST_DISCOVERED_ON),obj.get("discoverydate"), + from,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX")) + .stream()).collect(Collectors.toList()); + }catch(Exception e){ + LOGGER.error("Error in fetchAssetsDateRangesForEc2",e); + } + synchronized(dates){ + dates.addAll(ec2DatesList); + //System.out.println(dates.size()); + } + }); + } + return dates; + } + + /** + * Fetch assets date ranges on prem. + * + * @param ag the ag + * @param from the from + * @param isAssetsAffected the is assets affected + * @return the list + * @throws DataException the data exception + */ + @SuppressWarnings("unchecked") + public List fetchAssetsDateRangesOnPrem(String ag, LocalDate from) throws DataException { + + List dates = new ArrayList<>(); + //String searchUrl = "http://"+esHost+":"+esPort+"/"+ag+"/onpremserver/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + String searchUrl = "/"+ag+"/onpremserver/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + StringBuilder queryBody = new StringBuilder(); + queryBody.append("\"query\":{\"bool\":{\"must\":[{\"script\":{\"script\":\"") + .append("LocalDate.parse(doc['discoverydate.keyword'].value.substring(0,10)).isAfter(LocalDate.of(").append(from.getYear()+","+from.getMonthValue()+","+from.getDayOfMonth()).append("))") + .append("|| ( !doc['last_discovered.keyword'].empty && LocalDate.parse(doc['last_discovered.keyword'].value.substring(0,10)).isAfter(LocalDate.of(").append(from.getYear()+","+from.getMonthValue()+","+from.getDayOfMonth()).append(")))") + .append("\"}}") + .append("]}}"); + long totalCount = getTotalDocCount(ag,"onpremserver","{"+queryBody.toString()+"}"); + if (totalCount > 0) { + final int scrollSize = totalCount > 10000 ? 10000 + : (int) (totalCount / 2) + 1; + + final int slices = totalCount > scrollSize ? (int) totalCount + / scrollSize + 1 : 2; + + IntStream.range(0, slices) + .parallel().forEach(i -> { + StringBuilder request = new StringBuilder("{\"size\":").append(scrollSize); + request.append(",\"_source\":[\"_resourceid\",\"first_discovered\",\"firstdiscoveredon\",\"discoverydate\",\"last_discovered\"],"); + request.append("\"slice\": {\"id\":").append(i).append(",\"max\":").append(slices).append("},").append(queryBody).append("}"); + + List onpremDatesList = new ArrayList<>(); + try{ + List> hitsList = scrollAndFetch(searchUrl,request.toString()); + List> docs = new ArrayList<>(); + hitsList.parallelStream().forEach(hit -> { + Map doc = (Map)hit.get("_source"); + if(doc.get("first_discovered") != null) { + doc.put(Constants.FIRST_DISCOVERED_ON, doc.get("first_discovered")); + } + //if(doc.get("last_discovered") != null) { + // doc.put("discoverydate", doc.get("last_discovered")); + // } + synchronized(docs){ + docs.add(doc); + } + }); + + onpremDatesList = docs.parallelStream().flatMap(obj-> + getDateRange(obj.get(Constants.FIRST_DISCOVERED_ON).toString().substring(0,19),obj.get("discoverydate").toString().substring(0,19), + from,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + .stream()).collect(Collectors.toList()); + + }catch(Exception e){ + LOGGER.error("Error in fetchAssetsDateRangesOnPrem",e); + } + synchronized(dates){ + dates.addAll(onpremDatesList); + } + }); + } + return dates; + } + + /** + * Fetch assets date ranges on prem. + * + * @param ag the ag + * @param from the from + * @param isAssetsAffected the is assets affected + * @return + * @return the list + * @throws DataException the data exception + */ + public Map fetchAssetsAffected(String ag, LocalDate from,String severity) throws DataException { + String queryBody = "\"query\":{\"bool\":{\"must\":[{\"terms\":{\"severitylevel\":["+severity+"]}}],\"should\":[{\"range\":{\"_closedate\":{\"gte\":\""+from+"\"}}},{\"match\":{\"_status\":\"open\"}}],\"minimum_should_match\":1}}"; + + long totalCount = getTotalDocCount(ag,"vulninfo","{"+queryBody+"}"); + //System.out.println("Assets Affected Total Count :"+ totalCount ); + List dateResouceCompleteList = new ArrayList<>(); + if (totalCount > 0) { + final int scrollSize = totalCount > 10000 ? 10000 + : (int) (totalCount / 2) + 1; + + final int slices = totalCount > scrollSize ? (int) totalCount + / scrollSize + 1 : 2; + + IntStream.range(0, slices) + .parallel().forEach(i -> { + List datesResourceList = fetchVulnInfoDateRangesPerResource(ag,scrollSize,slices,i,queryBody,from); + synchronized(dateResouceCompleteList){ + dateResouceCompleteList.addAll(datesResourceList); + } + }); + } + Map> dateResoruceMap = dateResouceCompleteList.parallelStream().collect(Collectors.groupingBy( + obj-> obj.getDate(), Collectors.mapping(obj->obj.getResoruceId(), Collectors.toSet()))); + return dateResoruceMap.entrySet().parallelStream().collect(Collectors.toMap(entry->entry.getKey(), entry-> Long.valueOf(""+entry.getValue().size()))); + + } + + + /** + * Fetch vuln info date ranges. + * + * @param ag the ag + * @param scrollSize the scroll size + * @param slices the slices + * @param sliceNo the slice no + * @param queryBody the query body + * @param from the from + * @return the list + */ + @SuppressWarnings("unchecked") + private List fetchVulnInfoDateRangesPerResource(String ag,int scrollSize,int slices,int sliceNo,String queryBody,LocalDate from){ + // String searchUrl = "http://"+esHost+":"+esPort+"/"+ag+"/vulninfo/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + String searchUrl = "/"+ag+"/vulninfo/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + StringBuilder request = new StringBuilder(); + request.append("{\"size\":"). + append(scrollSize). + append(",\"_source\":[\"_firstFound\",\"_closedate\",\"_resourceid\"],"). + append("\"slice\": {\"id\":"). + append(sliceNo).append(",\"max\":").append(slices).append("},"). + append(queryBody).append("}"); + try{ + List> hitsList = scrollAndFetch(searchUrl,request.toString()); + return hitsList.parallelStream().map(obj-> (Map)obj.get("_source")).flatMap( obj-> getDateRangeWithResourceId(obj.get("_firstFound"),obj.get("_closedate"),from,DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[Z]['Z']"),obj.get("_resourceid").toString()).stream()).collect(Collectors.toList()); + + }catch(Exception e){ + LOGGER.error("error",e); + } + return new ArrayList<>(); + + } + + private RestClient getRestClient() { + RestClientBuilder builder = RestClient.builder(new HttpHost(esHost, esPort)); + builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() { + @Override + public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) { + return requestConfigBuilder.setConnectionRequestTimeout(0).setSocketTimeout(60000); + } + }).setMaxRetryTimeoutMillis(60000); + return builder.build(); + } + + private String invokeESCall(String method, String endpoint, String payLoad) { + + HttpEntity entity = null; + try { + if (payLoad != null) { + entity = new NStringEntity(payLoad, ContentType.APPLICATION_JSON); + } + return EntityUtils.toString(getRestClient() + .performRequest(method, endpoint, Collections.emptyMap(), entity).getEntity()); + } catch (IOException e) { + LOGGER.error("Error in invokeESCall ", e); + } + return null; + } +} + +class DateResInfo { + String date; + String resoruceId; + DateResInfo(String date, String resoruceId){ + this.date = date; + this.resoruceId = resoruceId; + } + public String getDate() { + return date; + } + public void setDate(String date) { + this.date = date; + } + public String getResoruceId() { + return resoruceId; + } + public void setResoruceId(String resoruceId) { + this.resoruceId = resoruceId; + } +} \ No newline at end of file diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityTrendGenerator2.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityTrendGenerator2.java new file mode 100644 index 000000000..e442f5e97 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityTrendGenerator2.java @@ -0,0 +1,613 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/** + Copyright (C) 2017 T Mobile Inc - All Rights Reserve + Purpose: + Author :kkumar28 + Modified Date: Oct 20, 2017 + + **/ +package com.tmobile.pacman.api.vulnerability.repository; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.entity.NStringEntity; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Repository; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.utils.PacHttpUtils; + + +/** + * The Class VulnerabilityTrendGenerator. + */ +@Repository +public class VulnerabilityTrendGenerator2 implements Constants { + + /** The es host. */ + @Value("${elastic-search.host}") + private String esHost; + + /** The es port. */ + @Value("${elastic-search.port}") + private int esPort; + + /** The es cluster name. */ + @Value("${elastic-search.clusterName}") + private String esClusterName; + + /** The date format. */ + @Value("${formats.date}") + private String dateFormat; + + /** The Constant LOGGER. */ + private static final Logger LOGGER = LoggerFactory + .getLogger(VulnerabilityTrendGenerator2.class); + + /** + * Generate trend. + * + * @param ag the ag + * @param severity the severity + * @param fromDate the from date + * @return the list + * @throws Exception the exception + */ + + public List> generateTrend(String ag,String severity, Date fromDate) throws Exception { + + List> dateList = new ArrayList<>(); + LocalDate from = LocalDate.parse(new SimpleDateFormat("yyyy-MM-dd").format(fromDate)); + + + + String queryBody = "\"query\":{\"bool\":{\"must\":[{\"terms\":{\"severitylevel\":["+severity+"]}}],\"should\":[{\"range\":{\"_closedate\":{\"gte\":\""+from+"\"}}},{\"match\":{\"_status\":\"open\"}}],\"minimum_should_match\":1}}"; + + + long totalCount = getTotalDocCount(ag,"vulninfo","{"+queryBody+"}"); + long start = System.currentTimeMillis(); + if (totalCount > 0) { + + + ExecutorService executionService = Executors.newFixedThreadPool(2); + Map newFoundMap = new HashMap<>(); + Map openCountMap = new HashMap<>(); + + + executionService.execute( () -> { + openCountMap.putAll(fetchVulnInfoDateRanges(ag,queryBody,from)); + System.out.println("Time taken fetchVulnInfoDateRanges :"+(System.currentTimeMillis()-start)); + System.out.println("Time taken Date Grouping :"+(System.currentTimeMillis()-start)); + }); + + executionService.execute(()-> { + newFoundMap.putAll(fetchNewlyFoundVulnByDay(ag,severity,from)); + System.out.println("Time taken new Trend :"+(System.currentTimeMillis()-start)); + }); + + executionService.shutdown(); + while(!executionService.isTerminated()){} + + + + openCountMap.entrySet().forEach(entry->{ + Map dateObj = new HashMap<>(); + String date = entry.getKey(); + Long open = entry.getValue(); + Long newlyFound = newFoundMap.get(date); + + dateObj.put("date",date); + dateObj.put("open",open); + dateObj.put("new",newlyFound==null?0l:newlyFound); + dateList.add(dateObj); + + }); + System.out.println("Time taken Generate Trend :"+(System.currentTimeMillis()-start)); + return dateList ; + } else { + + throw new DataException(NO_DATA_FOUND); + } + } + + /** + * Fetch newly found vuln by day. + * + * @param ag the ag + * @param severity the severity + * @param from the from + * @return the map + */ + private Map fetchNewlyFoundVulnByDay(String ag,String severity,LocalDate from ){ + + StringBuilder queryBody = new StringBuilder(); + queryBody.append("\"query\":{\"bool\":{\"must\":["). + append("{\"terms\":{\"severitylevel\":["+severity+"]}}"). + append(",{\"range\":{\"_firstFound\":{\"gte\":\""). + append(from.toString()). + append("\"}}}]}}"); + + String searchUrl = "http://"+esHost+":"+esPort+"/"+ag+"/vulninfo/_search?size=0"; + StringBuilder request = new StringBuilder(); + request.append("{"). + append("\"aggs\":{\"dates\":{\"date_histogram\":{\"field\":\"_firstFound\",\"interval\":\"day\",\"format\":\"yyyy-MM-dd\"}}}"). + append(",").append(queryBody).append("}"); + + Map newFoundMap = new HashMap<>(); + try { + String searchResponse = PacHttpUtils.doHttpPost(searchUrl, request.toString()); + JsonParser parser = new JsonParser(); + JsonObject responeObj = parser.parse(searchResponse).getAsJsonObject(); + JsonArray dateBuckets = responeObj.getAsJsonObject("aggregations").getAsJsonObject("dates").getAsJsonArray("buckets"); + for(JsonElement jsonElement:dateBuckets){ + JsonObject dateObj = jsonElement.getAsJsonObject(); + newFoundMap.put(dateObj.get("key_as_string").getAsString(),dateObj.get("doc_count").getAsLong()); + + } + } catch (Exception e) { + LOGGER.error("error",e); + } + + return newFoundMap; + + } + + /** + * Fetch vuln info date ranges. + * + * @param ag the ag + * @param scrollSize the scroll size + * @param slices the slices + * @param sliceNo the slice no + * @param queryBody the query body + * @param from the from + * @return the list + */ + private Map fetchVulnInfoDateRanges(String ag,String queryBody,LocalDate fromDate){ + //String searchUrl = "http://"+esHost+":"+esPort+"/"+ag+"/vulninfo/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + + System.out.println("Inside fetchVulnInfoDateRanges "); + long start = System.currentTimeMillis(); + String searchUrl = "/"+ag+"/vulninfo/_search"; + StringBuilder request = new StringBuilder(); + request.append("{\"size\":0,"). + append("\"aggs\": {\"from\": { \"terms\": { \"script\": \"new SimpleDateFormat('yyyy-MM-dd').format(doc['_firstFound'].value)\", \"size\": 10000},\"aggs\":" + + " { \"to\": { \"terms\": {\"script\": \"new SimpleDateFormat('yyyy-MM-dd').format(doc['_closedate'].value)\", \"size\": 10000 } }}}},"). + + append(queryBody).append("}"); + + Map openCountMap = new ConcurrentHashMap<>(); + try{ + + String searchResponse = invokeESCall("GET",searchUrl, request.toString()); + System.out.println("****fetchVulnInfoDateRanges >> Time after ES Query"+ (System.currentTimeMillis()-start)); + JsonParser parser = new JsonParser(); + JsonObject responeObj = parser.parse(searchResponse).getAsJsonObject(); + System.out.println("****fetchVulnInfoDateRanges >> Time took in ES query "+ responeObj.get("took").getAsString()); + JsonArray fromBuckets = responeObj.getAsJsonObject("aggregations").getAsJsonObject("from").getAsJsonArray("buckets"); + Map> fromToMap = new HashMap<>(); + for(JsonElement from : fromBuckets) { + JsonObject fromObj = (JsonObject)from; + String fromDt = fromObj.get("key").getAsString(); + JsonArray toBukets = fromObj.getAsJsonObject("to").getAsJsonArray("buckets"); + Map toList = new HashMap<>(); + fromToMap.put(fromDt,toList); + for(JsonElement to : toBukets) { + JsonObject toObj = (JsonObject)to; + String toDt = toObj.get("key").getAsString(); + long count = toObj.get("doc_count").getAsLong(); + if("1969-12-31".equals(toDt)) { + toDt = null; + } + toList.put(toDt,count); + } + } + + System.out.println("****fetchVulnInfoDateRanges >> Time after Parsing Result"+ (System.currentTimeMillis()-start)); + + fromToMap.forEach((from,v)-> { + v.forEach((to,count)-> { + List ranges = getDateRange(from,to,fromDate,DateTimeFormatter.ofPattern("yyyy-MM-dd")); + ranges.parallelStream().forEach(date -> { + Long currentVal = openCountMap.get(date); + openCountMap.put(date,currentVal==null?count:(currentVal+count)); + } + ); + }); + + }); + + System.out.println("****fetchVulnInfoDateRanges >> Time after Finding date range "+ (System.currentTimeMillis()-start)); + + }catch(Exception e){ + LOGGER.error("error",e); + openCountMap.clear(); + } + + return openCountMap; + + } + + + private List> scrollAndFetch(String searchUrl, String requestBody){ + List> hitsList = new ArrayList<>(); + String uri = searchUrl; + String request = requestBody; + String searchResponse ; + JsonParser parser = new JsonParser(); + JsonObject responeObj ; + long total ; + String scrollId ; + JsonArray hits; + //String scrollUri ="http://"+esHost+":"+esPort+"/_search/scroll"; + String scrollUri ="/_search/scroll?filter_path=hits.total,hits.hits._source,_scroll_id"; + String scrollRequest = "{\"scroll\":\"2m\",\"scroll_id\":\"%s\"}"; + try{ + do{ + + //searchResponse = PacHttpUtils.doHttpPost(uri, request); + searchResponse = invokeESCall("GET",uri, request); + parser = new JsonParser(); + responeObj = parser.parse(searchResponse).getAsJsonObject(); + scrollId = responeObj.get("_scroll_id").getAsString(); + total = responeObj.getAsJsonObject("hits").get("total").getAsLong(); + hits = responeObj.getAsJsonObject("hits").getAsJsonArray("hits"); + hitsList.addAll(new Gson().fromJson(hits,new TypeToken>>(){}.getType())); + uri = scrollUri ; + request = String.format(scrollRequest, scrollId); + //System.out.println("SCROLL:"+total+":"+hitsList.size()); + if(hits.size()==0) break; + }while(total>hitsList.size()); + }catch(Exception e){ + //System.out.println(uri); + //System.out.println(request); + LOGGER.error("error",e); + } + + return hitsList; + } + + /** + * Gets the total doc count. + * + * @param ag the ag + * @param type the type + * @param queryBody the query body + * @return the total doc count + * @throws DataException the data exception + */ + private long getTotalDocCount(String ag, String type, String queryBody) throws DataException{ + String countUrl = "http://"+esHost+":"+esPort+"/"+ag+"/"+type+"/_count"; + String countResponse = ""; + try { + countResponse = PacHttpUtils.doHttpPost(countUrl, queryBody); + } catch (Exception e) { + throw new DataException(e); + } + JsonParser jsonParser = new JsonParser(); + JsonObject response = jsonParser.parse(countResponse).getAsJsonObject(); + return Double.valueOf(response.get("count").getAsString()).longValue(); + + } + + /** + * Gets the date range. + * + * @param from the from + * @param to the to + * @param excludeBefore the exclude before + * @param inputFormatter the input formatter + * @return the date range + */ + private List getDateRange(Object from, Object to, LocalDate excludeBefore, DateTimeFormatter inputFormatter){ + LocalDate fromDt; + LocalDate toDt; + List dateRage = new ArrayList<>(); + if(from!=null){ + fromDt = LocalDate.parse(from.toString(),inputFormatter); + if(fromDt.isBefore(excludeBefore)) { + fromDt = excludeBefore; + } + if(to==null){ + toDt = LocalDate.now(); + }else{ + toDt = LocalDate.parse(to.toString(),inputFormatter); + } + DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE; + int daysBetween = (int) ChronoUnit.DAYS.between(fromDt, toDt); + + for(int i=0;i fetchAssetsDateRangesForEc2(String ag, LocalDate from) throws DataException { + String searchUrl = "/"+ag+"/ec2/_search?filter_path=aggregations.resources.buckets.key,aggregations.resources.buckets.from.buckets.key,aggregations.resources.buckets.from.buckets.to.buckets.key,took"; + StringBuilder queryBody = new StringBuilder(); + queryBody.append("\"query\":{\"range\": {\"discoverydate.keyword\": {\"gte\":\""+from+"\"}}}"); + long totalCount = getTotalDocCount(ag,"ec2","{"+queryBody.toString()+"}"); + StringBuilder request = new StringBuilder(); + request.append("{\"size\":0,"). + append("\"aggs\": { \"resources\": {\"terms\": {\"field\": \"_resourceid.keyword\", \"size\": 1000000},\"aggs\": { \"from\": {\"terms\": { \"script\": \"doc['firstdiscoveredon.keyword'].value.substring(0,10)\", \"size\": 10000 }, \"aggs\": { \"to\": { \"terms\": { \"script\": \"doc['discoverydate.keyword'].value.substring(0,10)\",\"size\": 10000}} }}} }} ,"). + append(queryBody).append("}"); + + Map dateResourceCountMap = new HashMap<>(); + + if (totalCount > 0) { + + long start = System.currentTimeMillis(); + try{ + String searchResponse = invokeESCall("GET",searchUrl, request.toString()); + System.out.println("****fetchAssetsDateRangesForEc2 >> Time after ES Query"+ (System.currentTimeMillis()-start)); + dateResourceCountMap = findResoucerDistributionByDate(searchResponse,from); + + }catch(Exception e){ + LOGGER.error("error",e); + + } + + } + return dateResourceCountMap; + } + + /** + * Fetch assets date ranges on prem. + * + * @param ag the ag + * @param from the from + * @param isAssetsAffected the is assets affected + * @return the list + * @throws DataException the data exception + */ + @SuppressWarnings("unchecked") + public List fetchAssetsDateRangesOnPrem(String ag, LocalDate from) throws DataException { + + List dates = new ArrayList<>(); + String searchUrl = "/"+ag+"/onpremserver/_search?scroll=2m&filter_path=hits.total,hits.hits._source,_scroll_id"; + StringBuilder queryBody = new StringBuilder(); + queryBody.append("\"query\":{\"bool\":{\"must\":[{\"script\":{\"script\":\"") + .append("LocalDate.parse(doc['discoverydate.keyword'].value.substring(0,10)).isAfter(LocalDate.of(").append(from.getYear()+","+from.getMonthValue()+","+from.getDayOfMonth()).append("))") + .append("|| ( !doc['last_discovered.keyword'].empty && LocalDate.parse(doc['last_discovered.keyword'].value.substring(0,10)).isAfter(LocalDate.of(").append(from.getYear()+","+from.getMonthValue()+","+from.getDayOfMonth()).append(")))") + .append("\"}}") + .append("]}}"); + long totalCount = getTotalDocCount(ag,"onpremserver","{"+queryBody.toString()+"}"); + if (totalCount > 0) { + final int scrollSize = totalCount > 10000 ? 10000 + : (int) (totalCount / 2) + 1; + + final int slices = totalCount > scrollSize ? (int) totalCount + / scrollSize + 1 : 2; + + IntStream.range(0, slices) + .parallel().forEach(i -> { + StringBuilder request = new StringBuilder("{\"size\":").append(scrollSize); + request.append(",\"_source\":[\"_resourceid\",\"first_discovered\",\"firstdiscoveredon\",\"discoverydate\"],"); + request.append("\"slice\": {\"id\":").append(i).append(",\"max\":").append(slices).append("},").append(queryBody).append("}"); + + List onpremDatesList = new ArrayList<>(); + try{ + List> hitsList = scrollAndFetch(searchUrl,request.toString()); + List> docs = new ArrayList<>(); + hitsList.parallelStream().forEach(hit -> { + Map doc = (Map)hit.get("_source"); + if(doc.get("first_discovered") != null) { + doc.put(Constants.FIRST_DISCOVERED_ON, doc.get("first_discovered")); + } + + synchronized(docs){ + docs.add(doc); + } + }); + + onpremDatesList = docs.parallelStream().flatMap(obj-> + getDateRange(obj.get(Constants.FIRST_DISCOVERED_ON).toString().substring(0,10),obj.get("discoverydate").toString().substring(0,10), + from,DateTimeFormatter.ofPattern("yyyy-MM-dd")) + .stream()).collect(Collectors.toList()); + + }catch(Exception e){ + LOGGER.error("Error in fetchAssetsDateRangesOnPrem",e); + } + synchronized(dates){ + dates.addAll(onpremDatesList); + } + }); + } + return dates; + } + + /** + * Fetch assets date ranges on prem. + * + * @param ag the ag + * @param from the from + * @param isAssetsAffected the is assets affected + * @return + * @return the list + * @throws DataException the data exception + */ + public Map fetchAssetsAffected(String ag, LocalDate from,String severity) throws DataException { + String queryBody = "\"query\":{\"bool\":{\"must\":[{\"terms\":{\"severitylevel\":["+severity+"]}}],\"should\":[{\"range\":{\"_closedate\":{\"gte\":\""+from+"\"}}},{\"match\":{\"_status\":\"open\"}}],\"minimum_should_match\":1}}"; + + long totalCount = getTotalDocCount(ag,"vulninfo","{"+queryBody+"}"); + + if (totalCount > 0) { + return fetchVulnInfoDateRangesPerResource(ag,queryBody,from); + } + return new HashMap<>(); + + } + + + /** + * Fetch vuln info date ranges. + * + * @param ag the ag + * @param scrollSize the scroll size + * @param slices the slices + * @param sliceNo the slice no + * @param queryBody the query body + * @param from the from + * @return the list + */ + private Map fetchVulnInfoDateRangesPerResource(String ag,String queryBody,LocalDate fromDate){ + + String searchUrl = "/"+ag+"/vulninfo/_search?filter_path=aggregations.resources.buckets.key,aggregations.resources.buckets.from.buckets.key,aggregations.resources.buckets.from.buckets.to.buckets.key,took"; + StringBuilder request = new StringBuilder(); + request.append("{\"size\":0,"). + append("\"aggs\": { \"resources\": {\"terms\": {\"field\": \"_resourceid.keyword\", \"size\": 1000000},\"aggs\": { \"from\": {\"terms\": { \"script\": \"new SimpleDateFormat('yyyy-MM-dd').format(doc['_firstFound'].value)\", \"size\": 10000 }, \"aggs\": { \"to\": { \"terms\": { \"script\": \"new SimpleDateFormat('yyyy-MM-dd').format(doc['_closedate'].value)\",\"size\": 10000}} }}} }} ,"). + append(queryBody).append("}"); + + Map dateResourceCountMap = new ConcurrentHashMap<>(); + long start = System.currentTimeMillis(); + try { + String searchResponse = invokeESCall("GET",searchUrl, request.toString()); + System.out.println("****fetchVulnInfoDateRangesPerResource >> Time after ES Query"+ (System.currentTimeMillis()-start)); + dateResourceCountMap = findResoucerDistributionByDate(searchResponse,fromDate); + + System.out.println("****Time after Parsing Result"+ (System.currentTimeMillis()-start)); + }catch(Exception e){ + LOGGER.error("error",e); + + } + return dateResourceCountMap; + + } + /** + * Return the resoruce distribution based on the aggregated results + * + * The response follows the nested aggreation on resources > fromDate > ToDate + * + * @param searchResponse + * @return + */ + private Map findResoucerDistributionByDate(String searchResponse,LocalDate fromDate){ + JsonParser parser = new JsonParser(); + JsonObject responeObj = parser.parse(searchResponse).getAsJsonObject(); + System.out.println("****Time taken for ES Query "+responeObj.get("took").getAsLong()); + JsonArray resourceBuckets = responeObj.getAsJsonObject("aggregations").getAsJsonObject("resources").getAsJsonArray("buckets"); + Map> dateResourceMap = new ConcurrentHashMap<>(); + + for(JsonElement resourceElmnt : resourceBuckets) { + JsonObject resourceObj = (JsonObject)resourceElmnt; + String resource = resourceObj.get("key").getAsString(); + + JsonArray fromBukets = resourceObj.getAsJsonObject("from").getAsJsonArray("buckets"); + + for(JsonElement from : fromBukets) { + JsonObject fromObj = (JsonObject)from; + String fromDt = fromObj.get("key").getAsString(); + + JsonArray toBukets = fromObj.getAsJsonObject("to").getAsJsonArray("buckets"); + for(JsonElement to : toBukets) { + JsonObject toObj = (JsonObject)to; + String toDt = toObj.get("key").getAsString(); + if("1969-12-31".equals(toDt)) { + toDt = null; + } + List ranges = getDateRange(fromDt,toDt,fromDate,DateTimeFormatter.ofPattern("yyyy-MM-dd")); + ranges.parallelStream().forEach(date -> { + Set currentResources = dateResourceMap.get(date); + if(currentResources==null) { + currentResources = new HashSet<>(); + dateResourceMap.put(date, currentResources); + } + + currentResources.add(resource); + + } + ); + + } + + } + } + return dateResourceMap.entrySet().parallelStream().collect(Collectors.toMap(entry->entry.getKey(), entry-> Long.valueOf(""+entry.getValue().size()))); + } + + private RestClient getRestClient() { + RestClientBuilder builder = RestClient.builder(new HttpHost(esHost, esPort)); + builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() { + @Override + public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) { + return requestConfigBuilder.setConnectionRequestTimeout(0).setSocketTimeout(60000); + } + }).setMaxRetryTimeoutMillis(60000); + return builder.build(); + } + + private String invokeESCall(String method, String endpoint, String payLoad) { + + HttpEntity entity = null; + try { + if (payLoad != null) { + entity = new NStringEntity(payLoad, ContentType.APPLICATION_JSON); + } + return EntityUtils.toString(getRestClient() + .performRequest(method, endpoint, Collections.emptyMap(), entity).getEntity()); + } catch (IOException e) { + LOGGER.error("Error in invokeESCall ", e); + } + return null; + } +} diff --git a/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/service/VulnerabilityService.java b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/service/VulnerabilityService.java new file mode 100644 index 000000000..ddaf7419a --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/java/com/tmobile/pacman/api/vulnerability/service/VulnerabilityService.java @@ -0,0 +1,2934 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.service; + +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Strings; +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.exception.ServiceException; +import com.tmobile.pacman.api.commons.repo.ElasticSearchRepository; +import com.tmobile.pacman.api.commons.utils.CommonUtils; +import com.tmobile.pacman.api.commons.utils.ResponseUtils; +import com.tmobile.pacman.api.vulnerability.client.AssetServiceClient; +import com.tmobile.pacman.api.vulnerability.client.ComplianceServiceClient; +import com.tmobile.pacman.api.vulnerability.domain.AssetApi; +import com.tmobile.pacman.api.vulnerability.domain.AssetApiData; +import com.tmobile.pacman.api.vulnerability.domain.AssetCount; +import com.tmobile.pacman.api.vulnerability.domain.AssetCountByAppEnvDTO; +import com.tmobile.pacman.api.vulnerability.domain.AssetCountDTO; +import com.tmobile.pacman.api.vulnerability.domain.AssetCountData; +import com.tmobile.pacman.api.vulnerability.domain.Request; +import com.tmobile.pacman.api.vulnerability.domain.ResponseData; +import com.tmobile.pacman.api.vulnerability.domain.TrendNote; +import com.tmobile.pacman.api.vulnerability.repository.VulnerabilityRepository; +import com.tmobile.pacman.api.vulnerability.repository.VulnerabilityTrendGenerator; +import com.tmobile.pacman.api.vulnerability.repository.VulnerabilityTrendGenerator2; + +/** + * The Class VulnerabilityService. + */ +@Service +public class VulnerabilityService implements Constants { + + /** The vulnerability repository. */ + @Autowired + private VulnerabilityRepository vulnerabilityRepository; + + /** The vuln trend generator. */ + @Autowired + VulnerabilityTrendGenerator2 vulnTrendGenerator; + + + + /** The compliance service. */ + // @Autowired + // ComplianceService complianceService; + + /** The vuln types. */ + @Value("${vulnerability.types}") + private String vulnTypes; + + /** The vuln summary severity. */ + @Value("${vulnerability.summary.severity}") + private String vulnSummarySeverity; + + /** The vuln application occurrence fields. */ + @Value("${vulnerability.application.occurance}") + private String vulnOccurrenceFields; + + /** The vuln resource Id fields. */ + @Value("${vulnerability.application.resourcedetails}") + private String vulnResourceIdFields; + + /** The vuln resource Id fields for both Ec2 and onprem. */ + @Value("${vulnerability.application.resourcedetailsboth}") + private String vulnResourceIdFieldsBoth; + + /** The asset service client. */ + @Autowired + private AssetServiceClient assetServiceClient; + @Autowired + private ComplianceServiceClient complianceServiceClient; + + /** The es repository. */ + @Autowired + ElasticSearchRepository esRepository; + + /** The Constant logger. */ + private static final Log logger = LogFactory.getLog(VulnerabilityService.class); + + /** + * Gets the vulnerabilities details. + * + * @param assetGroup + * the asset group + * @param filter + * the filter + * @return the vulnerabilities details + * @throws Exception + * the exception + */ + public List> getVulnerabilitiesDetails(String assetGroup, Map filter) + throws Exception { + List> vulnerabilitiesDetails = new ArrayList<>(); + try { + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + if (!vulnTargetTypes.isEmpty()) { + for (String parentType : vulnTargetTypes) { + Map vulnAssetsAffected = vulnerabilityRepository.getAssetsAffectedCount(assetGroup, + filter, parentType); + List> vulnerabilitiesData = formVulnerabilitiesData(vulnerabilityRepository + .getAllVulnerabilities(new ArrayList(vulnAssetsAffected.keySet())), + vulnAssetsAffected); + List> vulnerabilitiesDetailsTemp = new ArrayList<>(); + if (vulnerabilitiesDetails.isEmpty()) { + vulnerabilitiesDetails = new ArrayList<>(vulnerabilitiesData); + } else { + for (Map vulnData : vulnerabilitiesData) { + boolean qidMatched = false; + for (Map vulnDetail : vulnerabilitiesDetails) { + if (vulnData.get("qid").equals(vulnDetail.get("qid"))) { + vulnDetail.put(ASSETS_AFFECTED, + Long.valueOf(vulnDetail.get(ASSETS_AFFECTED).toString()) + + Long.valueOf(vulnData.get(ASSETS_AFFECTED).toString())); + qidMatched = true; + break; + } + } + if (!qidMatched) { + vulnerabilitiesDetailsTemp.add(vulnData); + } + } + vulnerabilitiesDetails.addAll(vulnerabilitiesDetailsTemp); + } + } + } + + } catch (Exception e) { + logger.error("Error in getVulnerabilitiesDetails ", e); + throw e; + } + + return vulnerabilitiesDetails; + } + + @SuppressWarnings("unchecked") + public List> getAllVulnerabilitiesDetailsByAssetGroup(String assetGroup, + Map filter,String applicationFilter,String environmentFilter, String searchText, int from, int size) throws Exception { + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + boolean flag = false; + if (vulnTargetTypes.size() > 1) { + flag = true; + } + List> vulnerabilitiesDetails = new ArrayList<>(); + List> vulnerabilitiesDetailsFinal = new ArrayList<>(); + /* Map mustFilter = new HashMap<>(); + Map mustTermsFilter = new HashMap<>(); + mustFilter.put("latest", "true"); + for (Map.Entry entry : filter.entrySet()) { + List severities = Arrays.asList(entry.getValue().split(",")); + mustTermsFilter.put(entry.getKey(), severities); + } + String targetType = "vulninfo"; + List occurrenceFieldList = Arrays.asList(vulnOccurrenceFields.split(","));*/ + List occurrenceFieldList = Arrays.asList(vulnOccurrenceFields.split(",")); + for (String parentType : vulnTargetTypes) { + String targetType = "vulninfo"; + Map mustFilterMap = new LinkedHashMap<>(); + mustFilterMap.put("latest", "true"); + Map parentBool = new HashMap<>(); + List> mustList = new ArrayList<>(); + Map matchMap = new HashMap<>(); + Map match = new HashMap<>(); + Map mustTermsFilter = new HashMap<>(); + for (Map.Entry entry : filter.entrySet()) { + List severities = Arrays.asList(entry.getValue().split(",")); + mustTermsFilter.put(entry.getKey(), severities); + } + // mustFilterMap.putAll(mustTermsFilter); + match.put(Constants.LATEST, Constants.TRUE); + matchMap.put(Constants.MATCH, match); + mustList.add(matchMap); + + if (applicationFilter != null) { + Map applicationFilterMap = new HashMap(); + Map applicationFilterMap1 = new HashMap(); + applicationFilterMap.put("tags.Application.keyword", applicationFilter); + applicationFilterMap1.put("match", applicationFilterMap); + mustList.add(applicationFilterMap1); + } + + if (environmentFilter != null) { + Map environmentFilterMap = new HashMap(); + Map environmentFilterMap1 = new HashMap(); + environmentFilterMap.put("tags.Environment.keyword", environmentFilter); + environmentFilterMap1.put("match", environmentFilterMap); + mustList.add(environmentFilterMap1); + } + + parentBool.put("must", mustList); + Map queryMap = new HashMap<>(); + queryMap.put("bool", parentBool); + Map parentEntryMap = new LinkedHashMap<>(); + parentEntryMap.put("parent_type", parentType); + parentEntryMap.put("query", queryMap); + mustFilterMap.put("has_parent", parentEntryMap); + + try { + + vulnerabilitiesDetails = vulnerabilityRepository.getAllVulnerabilitiesByAssetGroup(assetGroup, targetType, mustFilterMap,occurrenceFieldList, from, size, + mustTermsFilter); + vulnerabilitiesDetailsFinal.addAll(vulnerabilitiesDetails); + } catch (Exception e) { + logger.error("Error in getAllVulnerabilitiesDetailsByAssetGroup ", e); + throw e; + } + } + List listDistinctResourceId = getResourceId(vulnerabilitiesDetailsFinal); + List> resourceIdDetails = getResourcedetailsByResourceId(assetGroup, listDistinctResourceId, + flag); + + List> cartesianResultList = getCartesianProduct(vulnerabilitiesDetailsFinal, resourceIdDetails); + cartesianResultList = (List>) filterMatchingCollectionElements(cartesianResultList, + searchText, true); + for (Map map : cartesianResultList) { + List underscoreKeys = new ArrayList(); + for (Map.Entry entry : map.entrySet()) { + if (entry.getKey().startsWith("_")) { + underscoreKeys.add(entry.getKey()); + } + } + underscoreKeys.forEach(underscoreElement -> { + String underscoreRemovedKey = underscoreElement.substring(1, underscoreElement.length()); + map.put(underscoreRemovedKey, map.get(underscoreElement)); + map.remove(underscoreElement); + }); + } + + return cartesianResultList; + } + + public List> getResourcedetailsByResourceId(String assetGroup, + List listDistinctResourceId, boolean flag) throws Exception { + List> resourceDetails = new ArrayList<>(); + Map mustFilter = new HashMap<>(); + Map mustTermFilter = new HashMap<>(); + mustFilter.put("_entity", "true"); + // mustFilter.put("latest", "true"); + List resourceFieldList = Arrays.asList(vulnResourceIdFields.split(",")); + List resourceFieldListBoth = Arrays.asList(vulnResourceIdFieldsBoth.split(",")); + Map queryMap2 = new HashMap<>(); + Map queryMap1 = new HashMap<>(); + Map mustFilterAddition = new HashMap<>(); + queryMap1.put("latest", "true"); + queryMap2.put("match", queryMap1); + mustFilterAddition.put("type", "vulninfo"); + mustFilterAddition.put("query", queryMap2); + mustFilter.put("has_child", mustFilterAddition); + if (listDistinctResourceId != null && listDistinctResourceId.size() <= 1024) { + mustTermFilter.put("_resourceid.keyword", listDistinctResourceId); + } else { + mustTermFilter = null; + } + + try { + + if (flag == true) { + + resourceDetails = vulnerabilityRepository.getDetailsByResourceId(assetGroup, mustFilter, + resourceFieldListBoth, mustTermFilter); + } else { + resourceDetails = vulnerabilityRepository.getDetailsByResourceId(assetGroup, mustFilter, + resourceFieldList, mustTermFilter); + + } + + + } catch (Exception e) { + logger.error("Error in getResourcedetailsByResourceId ", e); + throw e; + } + + return resourceDetails; + } + + /** + * Gets the vulnerability summary. + * + * @param assetGroup + * the asset group + * @param reqSeverity + * the req severity + * @return the vulnerability summary + * @throws ServiceException + * the service exception + */ + @SuppressWarnings({ "unchecked" }) + public Map getVulnerabilitySummary(String assetGroup, String reqSeverity) throws ServiceException { + + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + + Map vulnerabilitySummary = new HashMap<>(); + List> severityInfo = new ArrayList<>(); + if (vulnTargetTypes.isEmpty()) { + for (int i = 3; i <= 5; i++) { + Map sevInfo = new HashMap<>(); + sevInfo.put(SEVERITY, "S" + i); + sevInfo.put(SEVEITY_LEVEL, i); + sevInfo.put(COUNT, 0); + sevInfo.put("appCount", 0); + sevInfo.put("hostCount", 0); + sevInfo.put(UNIQUE_VULN_COUNT, 0); + sevInfo.put(VULN_COUNT, 0); + severityInfo.add(sevInfo); + } + vulnerabilitySummary.put(SEV_INFO, severityInfo); + vulnerabilitySummary.put(VULNEREBILITIES, 0); + vulnerabilitySummary.put("hosts", 0); + vulnerabilitySummary.put(TOTAL_VULN_ASSETS, 0); + vulnerabilitySummary.put("compliantpercent", 100); + vulnerabilitySummary.put("assetsWithVulns", 0); + return vulnerabilitySummary; + + } else { + + Map> queryResults = new HashMap<>(); + + long totalQualysCount = 0; + long vulnerableAssetCount = 0; + long totalAssetCount = 0; + + ExecutorService executor = Executors.newFixedThreadPool(3); + executor.execute(() -> { + queryResults.put("uniqueHost", vulnerabilityRepository.getUniqueHost(assetGroup, reqSeverity)); + }); + executor.execute(() -> { + queryResults.put("VulnInfo", vulnerabilityRepository.getVulnInfo(assetGroup, reqSeverity)); + + }); + executor.execute(() -> { + queryResults.put("uniqueApp", vulnerabilityRepository.getUniqueApp(assetGroup)); + }); + executor.shutdown(); + while (!executor.isTerminated()) { + } + + Map uniqueHost = queryResults.get("uniqueHost"); + Map vulnInfo = queryResults.get("VulnInfo"); + Map uniqueApp = queryResults.get("uniqueApp"); + + List sevList = Arrays.asList(reqSeverity.split(",")); + List summarySevList = Arrays.asList(vulnSummarySeverity.split(",")); + Map sevVulnInfo; + Map vulnInfoMap; + + List> sevVulnfoList = new ArrayList<>(); + vulnerabilitySummary.put(SEV_INFO, sevVulnfoList); + + for (String sev : sevList) { + sevVulnInfo = new HashMap<>(); + sevVulnInfo.put(SEVEITY_LEVEL, Integer.valueOf(sev)); + sevVulnInfo.put(SEVERITY, "S" + sev); + sevVulnInfo.put("hostCount", uniqueHost.get(sev) == null ? 0 : uniqueHost.get(sev)); + if (summarySevList.contains(sev)) { + vulnerableAssetCount += Long.valueOf(sevVulnInfo.get("hostCount").toString()); + } + vulnInfoMap = (Map) vulnInfo.get(sev); + if (vulnInfoMap != null) { + sevVulnInfo.put(COUNT, vulnInfoMap.get(VULN_COUNT)); + sevVulnInfo.put(VULN_COUNT, vulnInfoMap.get(VULN_COUNT)); + sevVulnInfo.put(UNIQUE_VULN_COUNT, vulnInfoMap.get(UNIQUE_VULN_COUNT)); + } else { + sevVulnInfo.put(COUNT, 0); + sevVulnInfo.put(VULN_COUNT, 0); + sevVulnInfo.put(UNIQUE_VULN_COUNT, 0); + } + sevVulnInfo.put("appCount", uniqueApp.get(sev) == null ? 0 : uniqueApp.get(sev)); + sevVulnfoList.add(sevVulnInfo); + } + vulnerabilitySummary.put(UNIQUE_VULN_COUNT, sevVulnfoList.stream() + .mapToLong(sevData -> Long.valueOf(sevData.get(UNIQUE_VULN_COUNT).toString())).sum()); + vulnerabilitySummary.put("assetsWithVulns", uniqueHost.get(TOTAL) == null ? 0 : uniqueHost.get(TOTAL)); + vulnerabilitySummary.put(VULNEREBILITIES, vulnInfo.get(TOTAL) == null ? 0 : vulnInfo.get(TOTAL)); + + if (sevList.stream().filter(summarySevList::contains).count() != summarySevList.size()) { + vulnerableAssetCount = Long.valueOf( + vulnerabilityRepository.getUniqueHost(assetGroup, vulnSummarySeverity).get(TOTAL).toString()); + } + + for (String vulnType : vulnTargetTypes) { + try { + if (vulnType.equals(ONPREMSERVER)) { + + AssetCount totalAssets = assetServiceClient.getTotalAssetsCount(assetGroup, vulnType, null); + AssetCountData data = totalAssets.getData(); + AssetCountByAppEnvDTO[] assetcount = data.getAssetcount(); + Long totalAssetsCount = 0l; + for (AssetCountByAppEnvDTO assetCount_Count : assetcount) { + if (assetCount_Count.getType().equalsIgnoreCase(vulnType)) { + totalAssetsCount = Long.parseLong(assetCount_Count.getCount()); + } + } + totalAssetCount += totalAssetsCount; + totalQualysCount += vulnerabilityRepository.getTotalQualysHostCount(assetGroup, vulnType); + + } else { + Request request = new Request(); + request.setAg(assetGroup); + Map filter = new HashMap<>(); + filter.put("domain", "Infra & Platforms"); + if (vulnType.equals(EC2)) { + filter.put("ruleId.keyword", EC2_QUALYS_RULEID); + } + + if (vulnType.equals(VIRTUALMACHINE)) { + filter.put("ruleId.keyword", VIRTUALMACHINE_QUALYS_RULEID); + } + + request.setFilter(filter); + // Commenting the old code + // Map response = + // complianceService.getRulecompliance(request).getResponse().get(0); + // new code starts + + List> responses = new ArrayList<>(); + Map response = new HashMap<>(); + ResponseEntity nonCompliancePolicyRuleresponse = complianceServiceClient + .getNonCompliancePolicyByRule(request); + if (nonCompliancePolicyRuleresponse != null) { + Map responseBody = (Map) nonCompliancePolicyRuleresponse + .getBody(); + Map dataResponseMap = (Map) responseBody.get("data"); + responses = (List>) dataResponseMap.get("response"); + if (responses.size() > 0) { + response = responses.get(0); + // new code ends + totalAssetCount += Long.valueOf(response.get("assetsScanned").toString()); + totalQualysCount += Long.valueOf(response.get("passed").toString()); + } + } + } + } + // catch(ServiceException | DataException e){ + catch (DataException e) { + throw new ServiceException(); + } + } + + try { + + vulnerabilitySummary.put("hosts", totalAssetCount); + if (totalQualysCount > totalAssetCount) { + totalQualysCount = totalAssetCount; + } + + long totalVulnerableAssets = totalAssetCount - totalQualysCount + vulnerableAssetCount; + if (totalVulnerableAssets > totalAssetCount) { + totalVulnerableAssets = totalAssetCount; + } + vulnerabilitySummary.put(TOTAL_VULN_ASSETS, totalVulnerableAssets); + + float compliantCount = (float) totalAssetCount - totalVulnerableAssets; + float compliantpercent = 100; + if (totalAssetCount > 0) { + compliantpercent = (compliantCount / totalAssetCount) * 100; + } + DecimalFormat df = new DecimalFormat("#.00"); + vulnerabilitySummary.put("compliantpercent", Math.floor(Float.valueOf(df.format(compliantpercent)))); + vulnerabilitySummary.put("hostsScanned", totalQualysCount); + vulnerabilitySummary.put("hostsNotScanned", totalAssetCount - totalQualysCount); + if (totalAssetCount > 0) { + vulnerabilitySummary.put("hostsScanCoverage", + Math.floor((1.0 * totalQualysCount / totalAssetCount) * 100)); + } else { + vulnerabilitySummary.put("hostsScanCoverage", 0); + } + } catch (Exception e) { + logger.error(e); + throw new ServiceException(e); + } + if (vulnerabilitySummary.isEmpty()) { + throw new ServiceException(NO_DATA_FOUND); + } + return vulnerabilitySummary; + } + + } + + /** + * Gets the vulnerability by app and env. + * + * @param assetGroup + * the asset group + * @param filter + * the filter + * @param application + * the application + * @return the vulnerability by app and env + * @throws Exception + * the exception + */ + public List> getVulnerabilityByAppAndEnv(String assetGroup, String filter, String application) + throws Exception { + + List> vulnApplications = new ArrayList<>(); + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + + if (!vulnTargetTypes.isEmpty()) { + for (String parentType : vulnTargetTypes) { + vulnApplications.addAll(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(assetGroup, filter, + application, parentType, null)); + } + } + + return vulnApplications; + } + + /** + * Gets the vulnerability trend. + * + * @param assetGroup + * the asset group + * @param filter + * the filter + * @param from + * the from + * @param to + * the to + * @return the vulnerability trend + * @throws Exception + * the exception + */ + public List> getVulnerabilityTrend(String assetGroup, Map filter, Date from, + Date to) throws Exception { + return vulnerabilityRepository.getVulnerabilityTrend(assetGroup, filter, from, to); + } + + /** + * Gets the vulnerability trend. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @param from + * the from + * @return the vulnerability trend with open new count + * @throws Exception + * the exception + */ + public List> getVulnerabilityNewOpenTrend(String assetGroup, String severity, Date from) + throws Exception { + return vulnTrendGenerator.generateTrend(assetGroup, severity, from); + } + + /** + * Gets the vulnerabilities distribution. + * + * @param assetGroup + * the asset group + * @return the vulnerabilities distribution + * @throws Exception + * the exception + */ + public List> getVulnerabilitiesDistribution(String assetGroup) throws Exception { + + List> vulnDistributions = new ArrayList<>(); + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + if (!vulnTargetTypes.isEmpty()) { + for (String parentType : vulnTargetTypes) { + vulnDistributions + .addAll(vulnerabilityRepository.getVulnerabilitiesDistribution(assetGroup, parentType)); + } + } + return vulnDistributions; + } + + /** + * Filter matching collection elements. + * + * @param masterDetailList + * the master detail list + * @param searchText + * the search text + * @param b + * the b + * @return the object + * @throws ServiceException + * the ServiceException + */ + public Object filterMatchingCollectionElements(List> masterDetailList, String searchText, + boolean b) throws ServiceException { + return CommonUtils.filterMatchingCollectionElements(masterDetailList, searchText, true); + } + + /** + * Gets the vulnerabilitysummary by resource id. + * + * @param instanceId + * the instance id + * @return the vulnerabilitysummary by resource id + */ + public Map getVulnerabilitysummaryByResourceId(String instanceId) { + return vulnerabilityRepository.getVulnerabilitysummaryByResourceId(instanceId); + } + + /** + * Gets the vulnerability details by resource id. + * + * @param instanceId + * the instance id + * @return the vulnerability details by resource id + */ + public List> getVulnerabilityDetailsByResourceId(String instanceId) { + + List> vulnerabilitiesDetails = new ArrayList<>(); + try { + List> vulnerabilitiesDetailsList = vulnerabilityRepository + .getVulnerabilityDetailsByResourceId(instanceId); + vulnerabilitiesDetails = formVulnerabilitiesData(vulnerabilitiesDetailsList, new HashMap()); + } catch (Exception e) { + logger.error("Error in getVulnerabilitiesDetails ", e); + throw e; + } + + return vulnerabilitiesDetails; + } + + /** + * Form vulnerabilities data. + * + * @param vulnerabilitiesDetails + * the vulnerabilities details + * @param vulnAssetsAffected + * the vuln assets affected + * @return the list + */ + private List> formVulnerabilitiesData(List> vulnerabilitiesDetails, + Map vulnAssetsAffected) { + + List> vulnerabilitiesDetailsList = new ArrayList<>(); + + vulnerabilitiesDetails.parallelStream().forEach(vulnObject -> { + Map vulnObj = new LinkedHashMap<>(); + vulnObj.put(TITLE, vulnObject.get(TITLE).toString()); + vulnObj.put(SEVERITY, "S" + Double.valueOf(vulnObject.get(SEVEITY_LEVEL).toString()).intValue()); + if (!CollectionUtils.isEmpty(vulnAssetsAffected)) { + vulnObj.put(ASSETS_AFFECTED, vulnAssetsAffected.get(String.valueOf(vulnObject.get("qid").toString()))); + } + vulnObj.put("qid", Double.valueOf(vulnObject.get("qid").toString()).longValue()); + vulnObj.put(CATEGORY, vulnObject.get(CATEGORY).toString()); + vulnObj.put(VULN_TYPE, vulnObject.get(VULN_TYPE).toString()); + if (vulnObject.containsKey(PATCHABLE)) { + vulnObj.put(PATCHABLE, "1".equals(vulnObject.get(PATCHABLE).toString()) ? true : false); + } + vulnObj.put(SEVEITY_LEVEL, Double.valueOf(vulnObject.get(SEVEITY_LEVEL).toString()).intValue()); + synchronized (vulnerabilitiesDetailsList) { + vulnerabilitiesDetailsList.add(vulnObj); + } + }); + + if (!CollectionUtils.isEmpty(vulnAssetsAffected)) { + return vulnerabilitiesDetailsList.stream() + .sorted((h1, + h2) -> (int) (Double.parseDouble(h2.get(ASSETS_AFFECTED).toString()) + - (Double.parseDouble(h1.get(ASSETS_AFFECTED).toString())))) + .sorted((h1, + h2) -> (int) (Double.parseDouble(h2.get(SEVEITY_LEVEL).toString()) + - (Double.parseDouble(h1.get(SEVEITY_LEVEL).toString())))) + .collect(Collectors.toList()); + } else { + return vulnerabilitiesDetailsList.stream() + .sorted((h1, + h2) -> (int) (Double.parseDouble(h2.get(SEVEITY_LEVEL).toString()) + - (Double.parseDouble(h1.get(SEVEITY_LEVEL).toString())))) + .collect(Collectors.toList()); + } + + } + + /** + * Gets the target types. + * + * @param assetGroup + * the asset group + * @return the target types + */ + private String getTargetTypes(String assetGroup) { + String tTypesTemp; + String ttypes = null; + AssetApi assetApi = assetServiceClient.getTargetTypeList(assetGroup, null); + AssetApiData data = assetApi.getData(); + AssetCountDTO[] targetTypes = data.getTargettypes(); + for (AssetCountDTO name : targetTypes) { + if (!Strings.isNullOrEmpty(name.getType())) { + tTypesTemp = new StringBuilder().append('\'').append(name.getType()).append('\'').toString(); + if (Strings.isNullOrEmpty(ttypes)) { + ttypes = tTypesTemp; + } else { + ttypes = new StringBuilder(ttypes).append(",").append(tTypesTemp).toString(); + } + } + } + return ttypes; + } + + /** + * Gets the vulnerability distribution summary. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the vulnerability distribution summary + * @throws Exception + */ + public List> getVulnerabilityDistributionSummary(String assetGroup, String severity) + throws Exception { + List> distributionSummary = new ArrayList<>(); + + Map> appDetails = getDistributionSummary(assetGroup, severity); + Map> directorsInfo = getDirectorsForAGByApp(appDetails.keySet()); + + if (StringUtils.isEmpty(severity)) { + for (int i = 3; i <= 5; i++) { + distributionSummary.add(formDistributionSummary(appDetails, directorsInfo, String.valueOf(i))); + } + } else + distributionSummary.add(formDistributionSummary(appDetails, directorsInfo, severity)); + + return distributionSummary; + } + + /** + * Form distribution summary. + * + * @param appDetails + * the app details + * @param directApp + * the direct app + * @param vpApp + * the vp app + * @param severity + * the severity + * @return the map + */ + private Map formDistributionSummary(Map> appDetails, + Map> directApp, String severity) { + + String mngtLevel2 = "2"; + String mngtLevel3 = "3"; + String mngtLevel4 = "4"; + String mngtLevel5 = "5"; + String mngtLevel6 = "6"; + String mngtLevel7 = "7"; + + List> svpData = new ArrayList<>(); + List> vpData = new ArrayList<>(); + List> srDirectorData = new ArrayList<>(); + List> directorData = new ArrayList<>(); + List> appData = new ArrayList<>(); + + int total = 0; + + for (Entry> entry : appDetails.entrySet()) { + String appName = entry.getKey(); + Map sev = entry.getValue(); + + Map appTemp = new HashMap<>(); + appTemp.put("name", appName); + appTemp.put(COUNT, sev.get("S" + severity)); + total += Integer.valueOf(sev.get("S" + severity).toString()); + appData.add(appTemp); + + ExecutorService executionService = Executors.newFixedThreadPool(2); + executionService.execute(() -> { + if (directApp.get(mngtLevel2) != null) { + formDistributionSummaryDetails(directApp.get(mngtLevel2), appName, svpData, sev, severity); + } + }); + + executionService.execute(() -> { + if (directApp.get(mngtLevel3) != null) { + formDistributionSummaryDetails(directApp.get(mngtLevel3), appName, vpData, sev, severity); + } + }); + + executionService.execute(() -> { + if (directApp.get(mngtLevel4) != null) { + formDistributionSummaryDetails(directApp.get(mngtLevel4), appName, srDirectorData, sev, severity); + } + }); + + executionService.execute(() -> { + if (directApp.get(mngtLevel5) != null) { + Map directorLevel5Above = new HashMap<>(); + if (directApp.get(mngtLevel5) != null) { + directorLevel5Above.putAll(directApp.get(mngtLevel5)); + } + if (directApp.get(mngtLevel6) != null) { + directorLevel5Above.putAll(directApp.get(mngtLevel6)); + } + if (directApp.get(mngtLevel7) != null) { + directorLevel5Above.putAll(directApp.get(mngtLevel7)); + } + formDistributionSummaryDetails(directorLevel5Above, appName, directorData, sev, severity); + } + }); + executionService.shutdown(); + while (!executionService.isTerminated()) { + } + } + + Map svpInfo = new LinkedHashMap<>(); + svpInfo.put("type", "SVP"); + svpInfo.put("data", svpData); + Map vpInfo = new LinkedHashMap<>(); + vpInfo.put("type", "VP"); + vpInfo.put("data", vpData); + Map srDirectorInfo = new LinkedHashMap<>(); + srDirectorInfo.put("type", "Sr.Director"); + srDirectorInfo.put("data", srDirectorData); + Map directorInfo = new LinkedHashMap<>(); + directorInfo.put("type", "Director"); + directorInfo.put("data", directorData); + Map appInfo = new LinkedHashMap<>(); + appInfo.put("type", "Application"); + appInfo.put("data", appData); + + List> distributionList = new ArrayList<>(); + distributionList.add(svpInfo); + distributionList.add(vpInfo); + distributionList.add(srDirectorInfo); + distributionList.add(directorInfo); + distributionList.add(appInfo); + + Map severityMap = new HashMap<>(); + severityMap.put(SEVERITY, Integer.valueOf(severity)); + severityMap.put("distribution", distributionList); + severityMap.put("total", total); + return severityMap; + } + + /** + * Gets the aging summary. + * + * @param assetGroup + * the asset group + * @return the aging summary + */ + public List> getAgingSummary(String assetGroup) { + return vulnerabilityRepository.getAgingSummary(assetGroup); + } + + /** + * Gets the aging distribution summary. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the aging distribution summary + * @throws Exception + */ + @SuppressWarnings("unchecked") + public List> getAgingDistributionSummary(String assetGroup, String severity) throws Exception { + + List> distributionSummary = new ArrayList<>(); + List> vulnApplications = new ArrayList<>(); + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + if (!vulnTargetTypes.isEmpty()) { + for (String parentType : vulnTargetTypes) { + try { + vulnApplications + .addAll(vulnerabilityRepository.getAgingByApplication(assetGroup, parentType, severity)); + } catch (Exception e) { + logger.error(e); + } + } + } + Map appDetails = new ConcurrentHashMap<>(); + vulnApplications.parallelStream().forEach(vulnApps -> { + List> severityInfo = (List>) vulnApps.get(SEV_INFO); + Map> sevDetails = new HashMap<>(); + for (Map sevInfo : severityInfo) { + Map days = new HashMap<>(); + days.put("days", sevInfo.get("days")); + days.put(COUNT, sevInfo.get(COUNT)); + sevDetails.put(sevInfo.get(SEVERITY).toString(), days); + } + appDetails.put(vulnApps.get("application").toString(), sevDetails); + }); + + Map> directorsInfo = getDirectorsForAGByApp(appDetails.keySet()); + if (StringUtils.isEmpty(severity)) { + for (int i = 3; i <= 5; i++) { + distributionSummary.add(formAgingDistributionSummary(appDetails, directorsInfo, String.valueOf(i))); + } + } else + distributionSummary.add(formAgingDistributionSummary(appDetails, directorsInfo, severity)); + + return distributionSummary; + } + + /** + * Form aging distribution summary. + * + * @param appDetails + * the app details + * @param directApp + * the direct app + * @param vpApp + * the vp app + * @param severity + * the severity + * @return the map + */ + @SuppressWarnings("unchecked") + private Map formAgingDistributionSummary(Map appDetails, + Map> directApp, String severity) { + + String mngtLevel2 = "2"; + String mngtLevel3 = "3"; + String mngtLevel4 = "4"; + String mngtLevel5 = "5"; + String mngtLevel6 = "6"; + String mngtLevel7 = "7"; + + List> svpData = new ArrayList<>(); + List> vpData = new ArrayList<>(); + List> srDirectorData = new ArrayList<>(); + List> directorData = new ArrayList<>(); + List> appData = new ArrayList<>(); + + ObjectMapper oMapper = new ObjectMapper(); + + for (Entry entry : appDetails.entrySet()) { + String appName = entry.getKey(); + Map sev = oMapper.convertValue(entry.getValue(), Map.class); + Map appTemp = new HashMap<>(); + appTemp.put("name", appName); + Map sevInfo = oMapper.convertValue(sev.get("S" + severity), Map.class); + if (sevInfo.get(COUNT).toString().equals(ZERO) || sevInfo.get(COUNT).toString().equals(DOUBLE_ZERO)) { + appTemp.put("days", 0); + } else { + appTemp.put("days", Math.floor(Double.valueOf(sevInfo.get("days").toString()) + / Double.valueOf(sevInfo.get(COUNT).toString()))); + } + + appData.add(appTemp); + + ExecutorService executionService = Executors.newFixedThreadPool(4); + executionService.execute(() -> { + if (directApp.get(mngtLevel2) != null) { + formAgingDistributionSummaryDetails(directApp.get(mngtLevel2), appName, svpData, sevInfo, severity); + } + }); + + executionService.execute(() -> { + if (directApp.get(mngtLevel3) != null) { + formAgingDistributionSummaryDetails(directApp.get(mngtLevel3), appName, vpData, sevInfo, severity); + } + }); + + executionService.execute(() -> { + if (directApp.get(mngtLevel4) != null) { + formAgingDistributionSummaryDetails(directApp.get(mngtLevel4), appName, srDirectorData, sevInfo, + severity); + } + }); + + executionService.execute(() -> { + if (directApp.get(mngtLevel5) != null) { + Map directorLevel5Above = new HashMap<>(); + if (directApp.get(mngtLevel5) != null) { + directorLevel5Above.putAll(directApp.get(mngtLevel5)); + } + if (directApp.get(mngtLevel6) != null) { + directorLevel5Above.putAll(directApp.get(mngtLevel6)); + } + if (directApp.get(mngtLevel7) != null) { + directorLevel5Above.putAll(directApp.get(mngtLevel7)); + } + formAgingDistributionSummaryDetails(directorLevel5Above, appName, directorData, sevInfo, severity); + } + }); + executionService.shutdown(); + while (!executionService.isTerminated()) { + } + } + + ExecutorService executionService = Executors.newFixedThreadPool(4); + executionService.execute(() -> { + validatingAgingDays(svpData); + }); + executionService.execute(() -> { + validatingAgingDays(vpData); + }); + executionService.execute(() -> { + validatingAgingDays(srDirectorData); + }); + executionService.execute(() -> { + validatingAgingDays(directorData); + }); + executionService.shutdown(); + while (!executionService.isTerminated()) { + } + + Map svpInfo = new LinkedHashMap<>(); + svpInfo.put("type", "SVP"); + svpInfo.put("data", svpData); + Map vpInfo = new LinkedHashMap<>(); + vpInfo.put("type", "VP"); + vpInfo.put("data", vpData); + Map srDirectorInfo = new LinkedHashMap<>(); + srDirectorInfo.put("type", "Sr.Director"); + srDirectorInfo.put("data", srDirectorData); + Map directorInfo = new LinkedHashMap<>(); + directorInfo.put("type", "Director"); + directorInfo.put("data", directorData); + Map appInfo = new LinkedHashMap<>(); + appInfo.put("type", "Application"); + appInfo.put("data", appData); + + List> distributionList = new ArrayList<>(); + distributionList.add(svpInfo); + distributionList.add(vpInfo); + distributionList.add(srDirectorInfo); + distributionList.add(directorInfo); + distributionList.add(appInfo); + + Map severityMap = new HashMap<>(); + severityMap.put(SEVERITY, Integer.valueOf(severity)); + severityMap.put("distribution", distributionList); + return severityMap; + } + + /** + * Gets the vulnerability by qid. + * + * @param qid + * the qid + * @return the vulnerability by qid + */ + public List> getVulnerabilityByQid(String qid) { + + List> vulnByCategories = new ArrayList<>(); + Map vulnKbData = vulnerabilityRepository.getVulnerabilityByQid(qid); + vulnByCategories.add(formGeneralCategory(vulnKbData)); + vulnByCategories.add(formDetailsCategory(vulnKbData)); + vulnByCategories.add(formSoftwareCategory(vulnKbData)); + vulnByCategories.add(formImpactCategory(vulnKbData)); + vulnByCategories.add(formThreatCategory(vulnKbData)); + vulnByCategories.add(formSolutionCategory(vulnKbData)); + vulnByCategories.add(formExploitabilityCategory(vulnKbData)); + vulnByCategories.add(formAssociatedMalware(vulnKbData)); + return vulnByCategories; + } + + /** + * Form general category. + * + * @param vulnKbData + * the vuln kb data + * @return the map + */ + @SuppressWarnings("unchecked") + private Map formGeneralCategory(Map vulnKbData) { + + ObjectMapper oMapper = new ObjectMapper(); + + Map category = new LinkedHashMap<>(); + category.put("name", "General Information"); + Map attributes = new LinkedHashMap<>(); + attributes.put("QID", null == vulnKbData.get("qid") ? "" : vulnKbData.get("qid")); + attributes.put("Title", null == vulnKbData.get(TITLE) ? "" : vulnKbData.get(TITLE)); + attributes.put("Severity Level", null == vulnKbData.get(SEVEITY_LEVEL) ? "" : vulnKbData.get(SEVEITY_LEVEL)); + attributes.put("Vulnerability Type", null == vulnKbData.get(VULN_TYPE) ? "" : vulnKbData.get(VULN_TYPE)); + attributes.put("Category", null == vulnKbData.get(CATEGORY) ? "" : vulnKbData.get(CATEGORY)); + + Map discovery = oMapper.convertValue(vulnKbData.get("discovery"), Map.class); + + if (discovery != null) { + attributes.put("Authentication", fetchAttributes(discovery, "authtypelist", "authtype", true)); + } + + attributes.put("Service Modified", null == vulnKbData.get("lastservicemodificationdatetime") ? "" + : vulnKbData.get("lastservicemodificationdatetime")); + attributes.put("Published", + null == vulnKbData.get("publisheddatetime") ? "" : vulnKbData.get("publisheddatetime")); + + category.put(ATTRIBUTES, attributes); + return category; + } + + /** + * Form details category. + * + * @param vulnKbData + * the vuln kb data + * @return the map + */ + @SuppressWarnings("unchecked") + private Map formDetailsCategory(Map vulnKbData) { + ObjectMapper oMapper = new ObjectMapper(); + Map category = new HashMap<>(); + category.put("name", "Details"); + Map attributes = new LinkedHashMap<>(); + if (Arrays + .asList(fetchAttributes(vulnKbData, "discovery", "additionalinfo", false).toString().split("\\s*,\\s*")) + .contains("Patch Available")) { + attributes.put("Patch Availble", "Yes"); + } else + attributes.put("Patch Availble", "No"); + + attributes.put("CVE ID", fetchAttributes(vulnKbData, "cvelist", "cve", true)); + attributes.put("Vendor Reference", fetchAttributes(vulnKbData, "vendorreferencelist", "vendorreference", true)); + attributes.put("Bugtraq ID", fetchAttributes(vulnKbData, "bugtraqlist", "bugtraq", true)); + + if (null != vulnKbData.get("pciflag")) { + attributes.put("PCI Flag", Double.valueOf(vulnKbData.get("pciflag").toString()) == 0 ? false : true); + } + + attributes.put("PCI Reasons", fetchAttributes(vulnKbData, "pcireasons", "pcireason", true)); + if (null != vulnKbData.get("supportedmodules")) { + attributes.put("Supported Modules", vulnKbData.get("supportedmodules")); + } + + Map cvss = oMapper.convertValue(vulnKbData.get("cvss"), Map.class); + Map cvss3 = oMapper.convertValue(vulnKbData.get("cvssv3"), Map.class); + if (cvss != null) { + attributes.put("CVSS Base", cvss.get("base")); + attributes.put("CVSS Temporal", cvss.get("temporal")); + attributes.put("CVSS Access Vector", fetchAttributes(cvss, "access", "vector", false)); + } + if (cvss3 != null) { + attributes.put("CVSS3 Base", cvss3.get("base")); + attributes.put("CVSS3 Temporal", cvss3.get("temporal")); + } + + category.put(ATTRIBUTES, attributes); + return category; + } + + /** + * Form software category. + * + * @param vulnKbData + * the vuln kb data + * @return the map + */ + private Map formSoftwareCategory(Map vulnKbData) { + + Map category = new HashMap<>(); + category.put("name", "Software"); + category.put(ATTRIBUTES, fetchAttributes(vulnKbData, "softwarelist", "software", true)); + return category; + } + + /** + * Form threat category. + * + * @param vulnKbData + * the vuln kb data + * @return the map + */ + private Map formThreatCategory(Map vulnKbData) { + + Map category = new HashMap<>(); + category.put("name", "Threat"); + category.put(ATTRIBUTES, null == vulnKbData.get("diagnosis") ? "" : vulnKbData.get("diagnosis")); + return category; + } + + /** + * Form impact category. + * + * @param vulnKbData + * the vuln kb data + * @return the map + */ + private Map formImpactCategory(Map vulnKbData) { + + Map category = new HashMap<>(); + category.put("name", "Impact"); + category.put(ATTRIBUTES, null == vulnKbData.get("consequence") ? "" : vulnKbData.get("consequence")); + return category; + } + + /** + * Form solution category. + * + * @param vulnKbData + * the vuln kb data + * @return the map + */ + private Map formSolutionCategory(Map vulnKbData) { + + Map category = new HashMap<>(); + category.put("name", "Solution"); + category.put(ATTRIBUTES, null == vulnKbData.get("solution") ? "" : vulnKbData.get("solution")); + return category; + } + + /** + * Form exploitability category. + * + * @param vulnKbData + * the vuln kb data + * @return the map + */ + @SuppressWarnings("unchecked") + private Map formExploitabilityCategory(Map vulnKbData) { + + ObjectMapper oMapper = new ObjectMapper(); + List> attributes = new ArrayList<>(); + Map category = new HashMap<>(); + category.put("name", "Exploitability"); + + Map correlation = oMapper.convertValue(vulnKbData.get("correlation"), Map.class); + if (correlation != null && !correlation.isEmpty()) { + List> exploits = (List>) fetchAttributes(correlation, "exploits", + "expltsrc", true); + for (Map exploitTemp : exploits) { + Map exploit = new HashMap<>(); + exploit.put(SRC_NAME, exploitTemp.get(SRC_NAME)); + exploit.put("exploits", fetchAttributes(exploitTemp, "expltlist", "explt", true)); + attributes.add(exploit); + } + } + category.put(ATTRIBUTES, attributes); + return category; + } + + /** + * Form associated malware. + * + * @param vulnKbData + * the vuln kb data + * @return the map + */ + @SuppressWarnings("unchecked") + private Map formAssociatedMalware(Map vulnKbData) { + ObjectMapper oMapper = new ObjectMapper(); + List> attributes = new ArrayList<>(); + Map category = new HashMap<>(); + category.put("name", "Malware"); + + Map correlation = oMapper.convertValue(vulnKbData.get("correlation"), Map.class); + if (correlation != null && !correlation.isEmpty()) { + List> exploits = (List>) fetchAttributes(correlation, "malware", + "mwsrc", true); + for (Map exploitTemp : exploits) { + Map exploit = new HashMap<>(); + exploit.put(SRC_NAME, exploitTemp.get(SRC_NAME)); + exploit.put("malwares", fetchAttributes(exploitTemp, "mwlist", "mwinfo", true)); + attributes.add(exploit); + } + } + category.put(ATTRIBUTES, attributes); + return category; + } + + /** + * Fetch attributes. + * + * @param vulnKbData + * the vuln kb data + * @param parent + * the parent + * @param child + * the child + * @param isList + * the is list + * @return the object + */ + @SuppressWarnings("unchecked") + private Object fetchAttributes(Map vulnKbData, String parent, String child, boolean isList) { + Map parentMap = new ObjectMapper().convertValue(vulnKbData.get(parent), Map.class); + Object childObj; + if (parentMap != null) { + childObj = parentMap.get(child); + if (childObj != null) + return childObj; + } + + if (isList) { + return new ArrayList<>(); + } else { + return ""; + } + } + + /** + * Gets the distribution summary. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the distribution summary + */ + @SuppressWarnings("unchecked") + private Map> getDistributionSummary(String assetGroup, String severity) { + + List> vulnApplications = new ArrayList<>(); + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + if (!vulnTargetTypes.isEmpty()) { + for (String parentType : vulnTargetTypes) { + try { + vulnApplications.addAll(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(assetGroup, + "tags.Application.keyword", "", parentType, severity)); + } catch (Exception e) { + logger.error("Exception in getting getDistributionSummary ", e); + } + } + } + + Map> appDetails = new ConcurrentHashMap<>(); + vulnApplications.parallelStream().forEach(vulnApps -> { + List> severityInfo = (List>) vulnApps.get(SEV_INFO); + Map sevDetails = new HashMap<>(); + severityInfo.forEach(sevInfo -> { + sevDetails.put(sevInfo.get(SEVERITY).toString(), sevInfo.get(COUNT)); + }); + appDetails.put(vulnApps.get("application").toString(), sevDetails); + }); + return appDetails; + } + + /** + * Gets the highest lowest performers. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @param type + * the type + * @return the highest lowest performers + */ + @SuppressWarnings("unchecked") + public Map getHighestLowestPerformers(String assetGroup, String severity, String type) { + + String mngtLevel2 = "2"; + String mngtLevel3 = "3"; + String mngtLevel4 = "4"; + String mngtLevel5 = "5"; + String mngtLevel6 = "6"; + String mngtLevel7 = "7"; + + Map appDetails = new HashMap<>(); + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + + if (StringUtils.isBlank(severity)) { + severity = SEVERITY_LEVELS; + } + Map perfData = new HashMap<>(); + + if ("org".equalsIgnoreCase(type)) { + if (!vulnTargetTypes.isEmpty()) { + for (String parentType : vulnTargetTypes) { + try { + Map appDetailsTemp = vulnerabilityRepository.getAppsBySeverity(assetGroup, + parentType, severity); + if (appDetails.isEmpty()) { + appDetails = new HashMap<>(appDetailsTemp); + } else { + for (Entry appDetailTemp : appDetailsTemp.entrySet()) { + boolean appExists = false; + for (Entry appDetail : appDetails.entrySet()) { + if (appDetail.getKey().equals(appDetailTemp.getKey())) { + appDetails.put(appDetail.getKey(), + appDetail.getValue() + appDetailTemp.getValue()); + appExists = true; + break; + } + } + if (!appExists) { + appDetails.put(appDetailTemp.getKey(), appDetailTemp.getValue()); + } + } + } + } catch (Exception e) { + logger.error("Exception in getHighestLowestPeformers org", e); + } + } + } + try { + Map>> directorsInfo = getDirectorsForAG(appDetails.keySet()); + if (directorsInfo.get(mngtLevel2) != null && directorsInfo.get(mngtLevel2).size() > 1) { + formPerformersData(appDetails, perfData, directorsInfo.get(mngtLevel2)); + } else if (directorsInfo.get(mngtLevel3) != null && directorsInfo.get(mngtLevel3).size() > 1) { + formPerformersData(appDetails, perfData, directorsInfo.get(mngtLevel3)); + } else if (directorsInfo.get(mngtLevel4) != null && directorsInfo.get(mngtLevel4).size() > 1) { + formPerformersData(appDetails, perfData, directorsInfo.get(mngtLevel4)); + } else { + Map> directorLevel5Above = new HashMap<>(); + if (directorsInfo.get(mngtLevel5) != null) { + directorLevel5Above.putAll(directorsInfo.get(mngtLevel5)); + } + if (directorsInfo.get(mngtLevel6) != null) { + directorLevel5Above.putAll(directorsInfo.get(mngtLevel6)); + } + if (directorsInfo.get(mngtLevel7) != null) { + directorLevel5Above.putAll(directorsInfo.get(mngtLevel7)); + } + formPerformersData(appDetails, perfData, directorLevel5Above); + } + } catch (Exception e) { + logger.error("Exception in getHighestLowestPeformers org", e); + } + } else if (APPLICATION.equalsIgnoreCase(type)) { + try { + List> vulnApplications = getVulnerabilityByAppAndEnv(assetGroup, TAGS_APPS, ""); + for (Map appInfo : vulnApplications) { + String app = appInfo.get(APPS).toString(); + List> sevInfo = (List>) appInfo.get(SEV_INFO); + perfData.put(app, getVulnInstanceCount(sevInfo, severity)); + } + } catch (Exception e) { + logger.error("Exception in getHighestLowestPeformers app", e); + } + + } else if (ENVIRONMENT.equalsIgnoreCase(type)) { + + try { + List> vulnEnvmnts = getVulnerabilityByAppAndEnv(assetGroup, TAGS_ENV, ""); + for (Map envInfo : vulnEnvmnts) { + String env = envInfo.get(ENV).toString(); + List> sevInfo = (List>) envInfo.get(SEV_INFO); + perfData.put(env, getVulnInstanceCount(sevInfo, severity)); + } + } catch (Exception e) { + logger.error("Exception in getHighestLowestPeformers env", e); + } + } + return perfData.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors + .toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)); + } + + /** + * Gets the vuln instance count. + * + * @param sevInfoList + * the sev info list + * @param severity + * the severity + * @return the vuln instance count + */ + private int getVulnInstanceCount(List> sevInfoList, String severity) { + List sevList = Arrays.asList(severity.split(",")); + return sevInfoList.stream().filter(sevInfo -> sevList.contains(sevInfo.get("severitylevel").toString())) + .mapToInt(sevInfo -> Double.valueOf(sevInfo.get("vulnInstanceCount").toString()).intValue()).sum(); + } + + /** + * Gets the vuln target types. + * + * @param assetGroup + * the asset group + * @return the vuln target types + */ + private List getVulnTargetTypes(String assetGroup) { + + String validTargetTypes = getTargetTypes(assetGroup); + List vulnTargetTypes = new ArrayList<>(); + for (String vulnType : vulnTypes.split(",")) { + if (!StringUtils.isEmpty(validTargetTypes) && validTargetTypes.contains(vulnType.trim())) { + vulnTargetTypes.add(vulnType); + } + } + return vulnTargetTypes; + } + + /** + * Gets the distribution summary by infra type. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the distribution summary by infra type + * @throws ServiceException + * the service exception + */ + public List> getDistributionSummaryByInfraType(String assetGroup, String severity) + throws ServiceException { + + List> distributionList = new ArrayList<>(); + + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + if (StringUtils.isBlank(severity)) { + severity = SEVERITY_LEVELS; + } + long totalVulnCount = 0; + for (String vulnType : vulnTargetTypes) { + Map info = new HashMap<>(); + try { + info = vulnerabilityRepository.getDistributionSummaryByInfraType(assetGroup, severity, vulnType); + } catch (Exception e) { + logger.error("Error in getDistributionSummaryByInfraType ", e); + throw new ServiceException(); + } + + totalVulnCount += Long.valueOf(info.get(VULNEREBILITIES).toString()); + + if (vulnType.equals(ONPREMSERVER)) { + info.put(CATEGORY, "On-Prem"); + distributionList.add(info); + } else { + Optional> cloudInfoOptional = distributionList.stream() + .filter(distro -> distro.get(CATEGORY).equals("Cloud")).findAny(); + if (cloudInfoOptional.isPresent()) { + Map currentCloudInfo = cloudInfoOptional.get(); + currentCloudInfo.put(TOTAL_VULN_ASSETS, + Long.valueOf(currentCloudInfo.get(TOTAL_VULN_ASSETS).toString()) + + Long.valueOf(info.get(TOTAL_VULN_ASSETS).toString())); + currentCloudInfo.put(VULNEREBILITIES, Long.valueOf(currentCloudInfo.get(VULNEREBILITIES).toString()) + + Long.valueOf(info.get(VULNEREBILITIES).toString())); + currentCloudInfo.put(UNIQUE_VULN_COUNT, + Long.valueOf(currentCloudInfo.get(UNIQUE_VULN_COUNT).toString()) + + Long.valueOf(info.get(UNIQUE_VULN_COUNT).toString())); + } else { + info.put(CATEGORY, "Cloud"); + distributionList.add(info); + } + + } + + } + + double contribution = HUNDRED; + for (int i = 0; i < distributionList.size(); i++) { + Map info = distributionList.get(i); + double contributionPercent = Math + .floor((Double.valueOf(info.get(VULNEREBILITIES).toString()) / totalVulnCount) * HUNDRED); + if (i == distributionList.size() - 1) { + info.put(CONTRIBUTION, contribution); + } else { + info.put(CONTRIBUTION, contributionPercent); + contribution = contribution - contributionPercent; + } + } + return distributionList; + } + + /** + * Gets the distribution summary by env. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the distribution summary by env + * @throws ServiceException + * the service exception + */ + public List> getDistributionSummaryByEnv(String assetGroup, String severity) + throws ServiceException { + + List> distributionList = new ArrayList<>(); + if (StringUtils.isBlank(severity)) { + severity = SEVERITY_LEVELS; + } + + long totalVulnCount = 0; + + Map prodInfo = new HashMap<>(); + prodInfo.put(TOTAL_VULN_ASSETS, 0); + prodInfo.put(VULNEREBILITIES, 0); + prodInfo.put(UNIQUE_VULN_COUNT, 0); + + Map nonProdInfo = new HashMap<>(); + nonProdInfo.put(TOTAL_VULN_ASSETS, 0); + nonProdInfo.put(VULNEREBILITIES, 0); + nonProdInfo.put(UNIQUE_VULN_COUNT, 0); + try { + Map prodInfoTemp = vulnerabilityRepository.getProdInfoByEnv(assetGroup, severity); + Map nonProdInfoTemp = vulnerabilityRepository.getNonProdInfoByEnv(assetGroup, severity); + + totalVulnCount += prodInfoTemp.get(VULNEREBILITIES) + nonProdInfoTemp.get(VULNEREBILITIES); + + for (Entry entry : prodInfo.entrySet()) { + prodInfo.put(entry.getKey(), + Long.valueOf(entry.getValue().toString()) + prodInfoTemp.get(entry.getKey())); + } + + for (Entry entry : nonProdInfo.entrySet()) { + nonProdInfo.put(entry.getKey(), + Long.valueOf(entry.getValue().toString()) + nonProdInfoTemp.get(entry.getKey())); + } + } catch (Exception e) { + throw new ServiceException(e); + } + prodInfo.put(CATEGORY, "Prod"); + distributionList.add(prodInfo); + nonProdInfo.put(CATEGORY, "Non-Prod"); + distributionList.add(nonProdInfo); + + double contribution = HUNDRED; + for (int i = 0; i < distributionList.size(); i++) { + Map info = distributionList.get(i); + if (totalVulnCount > 0) { + double contributionPercent = Math + .floor((Double.valueOf(info.get(VULNEREBILITIES).toString()) / totalVulnCount) * HUNDRED); + if (i == distributionList.size() - 1) { + info.put(CONTRIBUTION, contribution); + } else { + info.put(CONTRIBUTION, contributionPercent); + contribution = contribution - contributionPercent; + } + } else { + info.put(CONTRIBUTION, 0); + } + } + return distributionList; + } + + /** + * Gets the distribution summary by vuln type. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the distribution summary by vuln type + * @throws DataException + * the data exception + */ + public List> getDistributionSummaryByVulnType(String assetGroup, String severity) + throws DataException { + if (StringUtils.isBlank(severity)) { + severity = SEVERITY_LEVELS; + } + return vulnerabilityRepository.getDistributionSummaryByVulnType(assetGroup, severity); + } + + /** + * Gets the remediation actions summary. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @return the remediation actions summary + * @throws DataException + * the data exception + */ + public List> getRemediationActionsSummary(String assetGroup, String severity) + throws DataException { + + List> remediationList = new ArrayList<>(); + if (StringUtils.isBlank(severity)) { + severity = SEVERITY_LEVELS; + } + + List> eolActions = vulnerabilityRepository + .getDataFromPacmanRDS("SELECT matchingString,subAction FROM cf_RemediationCriteria WHERE " + + "action='Remove/Replace EOL Software'"); + List> stopRemoveActions = vulnerabilityRepository + .getDataFromPacmanRDS("SELECT matchingString,subAction FROM cf_RemediationCriteria WHERE " + + "action='Stop Service/Remove Software'"); + List> swConfigChangeActions = vulnerabilityRepository + .getDataFromPacmanRDS("SELECT matchingString,subAction FROM cf_RemediationCriteria WHERE " + + "action='Software Configuration Change'"); + List> swUpdateActions = vulnerabilityRepository.getDataFromPacmanRDS( + "SELECT matchingString,subAction FROM cf_RemediationCriteria WHERE " + "action='Software Update'"); + + String softwareConfig = getAllMatchingString(swConfigChangeActions); + String softwareUpdate = getAllMatchingString(swUpdateActions); + + Map osPatching = new HashMap<>(); + osPatching.put(ACTION, "OS Patching"); + osPatching.put(DESCRIPTION, "Apply the patches released by the operating system provider"); + osPatching.put(CONTRIBUTION, 0); + Map eolSoftware = new HashMap<>(); + eolSoftware.put(ACTION, "Remove/Replace EOL Software"); + eolSoftware.put(CONTRIBUTION, 0); + eolSoftware.put(DESCRIPTION, "Remove or replace below listed End of Life Software versions"); + Map noSolution = new HashMap<>(); + noSolution.put(ACTION, "No Solution Available"); + noSolution.put(CONTRIBUTION, 0); + noSolution.put(DESCRIPTION, "Vulnerabilities with no published solution yet"); + Map stopRemove = new HashMap<>(); + stopRemove.put(ACTION, "Stop Service/Remove Software"); + stopRemove.put(CONTRIBUTION, 0); + stopRemove.put(DESCRIPTION, "Stop unimportant vulnerable services, remove malicious softwares from the hosts"); + Map swConfigChange = new HashMap<>(); + swConfigChange.put(ACTION, "Software Configuration Change"); + swConfigChange.put(CONTRIBUTION, 0); + swConfigChange.put(DESCRIPTION, + "Fix the configurations of the below listed softwares. Some default configurations like default admin username and password should be replaced with a stronger one"); + Map swUpdate = new HashMap<>(); + swUpdate.put(ACTION, "Software Update"); + swUpdate.put(CONTRIBUTION, 0); + swUpdate.put(DESCRIPTION, + "Update the below listed softwares to their latest version or apply patches released by the software provider "); + + List> eolSubActions = new ArrayList<>(); + List> stopRemoveSubActions = new ArrayList<>(); + List> swConfigChangeSubActions = new ArrayList<>(); + List> swUpdateSubActions = new ArrayList<>(); + + Map unclassified = new HashMap<>(); + unclassified.put(ACTION, "Unclassified"); + unclassified.put(DESCRIPTION, + "These vulnerabilities are not classified yet. Refer the vulnerability description to fix the vulnerability"); + unclassified.put(CONTRIBUTION, 0); + + Map qids = vulnerabilityRepository.getAllQidByAG(assetGroup, severity); + Long total = qids.entrySet().stream().mapToLong(entry -> Long.valueOf(entry.getValue().toString())).sum(); + for (String qidTitleClass : qids.keySet()) { + String qid = qidTitleClass.split("~")[0]; + String vulnTitle = qidTitleClass.split("~")[1].toLowerCase(); + String classification = qidTitleClass.split("~")[2]; + if ("OS".equalsIgnoreCase(classification)) { + osPatching.put(CONTRIBUTION, Long.valueOf(osPatching.get(CONTRIBUTION).toString()) + + Long.valueOf(qids.get(qidTitleClass).toString())); + } else if (vulnTitle.contains("EOL/Obsolete".toLowerCase())) { + eolSoftware.put(CONTRIBUTION, Long.valueOf(eolSoftware.get(CONTRIBUTION).toString()) + + Long.valueOf(qids.get(qidTitleClass).toString())); + formSubActionList(eolActions, eolSubActions, vulnTitle, + Long.valueOf(qids.get(qidTitleClass).toString())); + } else if ("11925".equals(qid) || "370914".equals(qid)) { + noSolution.put(CONTRIBUTION, Long.valueOf(noSolution.get(CONTRIBUTION).toString()) + + Long.valueOf(qids.get(qidTitleClass).toString())); + } else if (vulnTitle.contains("Java Debug Wire Protocol".toLowerCase())) { + stopRemove.put(CONTRIBUTION, Long.valueOf(stopRemove.get(CONTRIBUTION).toString()) + + Long.valueOf(qids.get(qidTitleClass).toString())); + formSubActionList(stopRemoveActions, stopRemoveSubActions, vulnTitle, + Long.valueOf(qids.get(qidTitleClass).toString())); + } else if (checkVulnTitle(vulnTitle, softwareConfig)) { + swConfigChange.put(CONTRIBUTION, Long.valueOf(swConfigChange.get(CONTRIBUTION).toString()) + + Long.valueOf(qids.get(qidTitleClass).toString())); + formSubActionList(swConfigChangeActions, swConfigChangeSubActions, vulnTitle, + Long.valueOf(qids.get(qidTitleClass).toString())); + } else if (checkVulnTitle(vulnTitle, softwareUpdate)) { + swUpdate.put(CONTRIBUTION, Long.valueOf(swUpdate.get(CONTRIBUTION).toString()) + + Long.valueOf(qids.get(qidTitleClass).toString())); + formSubActionList(swUpdateActions, swUpdateSubActions, vulnTitle, + Long.valueOf(qids.get(qidTitleClass).toString())); + } else { + unclassified.put(CONTRIBUTION, Long.valueOf(unclassified.get(CONTRIBUTION).toString()) + + Long.valueOf(qids.get(qidTitleClass).toString())); + } + } + + calculateContributionPercentage(eolSubActions, Long.valueOf(eolSoftware.get(CONTRIBUTION).toString())); + calculateContributionPercentage(stopRemoveSubActions, Long.valueOf(stopRemove.get(CONTRIBUTION).toString())); + calculateContributionPercentage(swConfigChangeSubActions, + Long.valueOf(swConfigChange.get(CONTRIBUTION).toString())); + calculateContributionPercentage(swUpdateSubActions, Long.valueOf(swUpdate.get(CONTRIBUTION).toString())); + + eolSoftware.put(SUB_ACTIONS, eolSubActions); + stopRemove.put(SUB_ACTIONS, stopRemoveSubActions); + swConfigChange.put(SUB_ACTIONS, swConfigChangeSubActions); + swUpdate.put(SUB_ACTIONS, swUpdateSubActions); + + remediationList.add(osPatching); + remediationList.add(eolSoftware); + remediationList.add(noSolution); + remediationList.add(stopRemove); + remediationList.add(swConfigChange); + remediationList.add(swUpdate); + remediationList.add(unclassified); + + calculateContributionPercentage(remediationList, total); + return remediationList; + } + + /** + * Check vuln title. + * + * @param vulnTitle + * the vuln title + * @param values + * the values + * @return true, if successful + */ + private boolean checkVulnTitle(String vulnTitle, String values) { + for (String value : values.split(",")) { + if (vulnTitle.contains(value)) { + return true; + } + } + return false; + } + + /** + * Form sub action list. + * + * @param actions + * the actions + * @param subActions + * the sub actions + * @param vulnTitle + * the vuln title + * @param contribution + * the contribution + */ + private void formSubActionList(List> actions, List> subActions, + String vulnTitle, long contribution) { + boolean titleMatched = false; + for (Map action : actions) { + if (vulnTitle.contains(action.get(MATCHING_STRING).toString().toLowerCase())) { + titleMatched = true; + formSubAction(subActions, action.get("subAction").toString(), action.get(MATCHING_STRING).toString(), + contribution); + break; + } + } + if (!titleMatched) { + formSubAction(subActions, "Others", "Others", contribution); + } + } + + /** + * Form sub action. + * + * @param subActions + * the sub actions + * @param subActionTitle + * the sub action title + * @param subActiondescr + * the sub actiondescr + * @param contribution + * the contribution + */ + private void formSubAction(List> subActions, String subActionTitle, String subActiondescr, + Long contribution) { + if (subActions.isEmpty()) { + Map subAction = new HashMap<>(); + subAction.put(ACTION, subActionTitle); + subAction.put(DESCRIPTION, subActiondescr); + subAction.put(CONTRIBUTION, contribution); + subActions.add(subAction); + } else { + boolean subActionExists = false; + for (Map subAction : subActions) { + if (subActionTitle.equals(subAction.get(ACTION).toString())) { + subActionExists = true; + subAction.put(CONTRIBUTION, Long.valueOf(subAction.get(CONTRIBUTION).toString()) + contribution); + break; + } + } + if (!subActionExists) { + Map subAction = new HashMap<>(); + subAction.put(ACTION, subActionTitle); + subAction.put(DESCRIPTION, subActiondescr); + subAction.put(CONTRIBUTION, contribution); + subActions.add(subAction); + } + } + } + + /** + * Gets the all matching string. + * + * @param actions + * the actions + * @return the all matching string + */ + private String getAllMatchingString(List> actions) { + List matchingStrings = new ArrayList<>(); + for (Map action : actions) { + matchingStrings.add(action.get(MATCHING_STRING).toString().toLowerCase()); + } + return StringUtils.join(matchingStrings, ","); + } + + /** + * Calculate contribution percentage. + * + * @param contributionList + * the contribution list + * @param total + * the total + */ + private void calculateContributionPercentage(List> contributionList, long total) { + DecimalFormat df = new DecimalFormat("###.##"); + ListIterator> it = contributionList.listIterator(); + String contributionPercent; + while (it.hasNext()) { + Map bucket = it.next(); + Long contribution = Long.valueOf(bucket.get(CONTRIBUTION).toString()); + if (contribution == 0) { + it.remove(); + } else { + contributionPercent = df.format((contribution * HUNDRED) / total); + if ("0".equals(contributionPercent)) { + it.remove(); + } else { + bucket.put(CONTRIBUTION, Float.valueOf(contributionPercent)); + } + } + } + } + + /** + * Creates the trend annotation. + * + * @param request + * the request + * @return true, if successful + * @throws JsonProcessingException + * the json processing exception + */ + public boolean createTrendAnnotation(TrendNote request) throws JsonProcessingException { + return vulnerabilityRepository.createTrendAnnotation(request); + } + + /** + * Gets the trend annotations. + * + * @param assetGroup + * the asset group + * @param from + * the from + * @return the trend annotations + * @throws DataException + * the data exception + */ + public List> getTrendAnnotations(String assetGroup, Date from) throws DataException { + + List> globalAnnotations = new ArrayList<>(); + List> assetGroupAnnotations = new ArrayList<>(); + List> annotations = vulnerabilityRepository.getTrendAnnotations(assetGroup, from); + + annotations.parallelStream().forEach(annotation -> { + if (StringUtils.isEmpty(annotation.get("ag").toString())) { + synchronized (globalAnnotations) { + globalAnnotations.add(annotation); + } + } else { + synchronized (assetGroupAnnotations) { + assetGroupAnnotations.add(annotation); + } + } + }); + + Map gloablMap = new HashMap<>(); + gloablMap.put("type", "Global"); + gloablMap.put("data", globalAnnotations); + + Map agMap = new HashMap<>(); + agMap.put("type", "AssetGroup"); + agMap.put("data", assetGroupAnnotations); + + List> noteList = new ArrayList<>(); + noteList.add(agMap); + noteList.add(gloablMap); + + return noteList; + } + + /** + * Delete trend annotation. + * + * @param noteId + * the note id + * @return true, if successful + */ + public boolean deleteTrendAnnotation(String noteId) { + return vulnerabilityRepository.deleteTrendAnnotation(noteId); + } + + /** + * Gets the vulnerability summary by assets. + * + * @param assetGroup + * the asset group + * @return the vulnerability summary by assets + * @throws ServiceException + * the service exception + * @throws DataException + * the data exception + */ + public Map getVulnerabilitySummaryByAssets(String assetGroup) + throws ServiceException, DataException { + + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + + long totalCount = 0; + long exemptedCount = 0; + long inscopeCount = 0; + long scannedCount = 0; + long unscannedCount = 0; + + for (String vulnType : vulnTargetTypes) { + try { + long tempTotalCount = vulnerabilityRepository.getRunningInstancesCount(assetGroup, vulnType); + long exemptedByRule = vulnerabilityRepository.getExemptedByRuleCount(assetGroup, vulnType); + + Request request = new Request(); + request.setAg(assetGroup); + Map filter = new HashMap<>(); + filter.put("domain", "Infra & Platforms"); + + if (vulnType.equals(EC2)) { + filter.put("ruleId.keyword", EC2_QUALYS_RULEID); + } else if (vulnType.equals(VIRTUALMACHINE)) { + filter.put("ruleId.keyword", VIRTUALMACHINE_QUALYS_RULEID); + } else { + filter.put("ruleId.keyword", ONPREM_QUALYS_RULEID); + } + request.setFilter(filter); + // + // Map response = + // complianceService.getRulecompliance(request).getResponse().get(0); + // new code starts + List> responses = new ArrayList<>(); + Map response = new HashMap<>(); + ResponseEntity nonCompliancePolicyRuleresponse = complianceServiceClient + .getNonCompliancePolicyByRule(request); + if (nonCompliancePolicyRuleresponse != null) { + Map responseBody = (Map) nonCompliancePolicyRuleresponse.getBody(); + Map dataResponseMap = (Map) responseBody.get("data"); + responses = (List>) dataResponseMap.get("response"); + if (responses.size() > 0) { + response = responses.get(0); + + // Response response = (Response) dataResponse.get("data"); + // new code ends + // This inScopeRule is total - exempted by age. SO exemptedByAge would be total- + // inScopeRule; + long inScopeRule = Long.valueOf(response.get("assetsScanned").toString()); + long exemptedByAge = tempTotalCount - inScopeRule; + totalCount += tempTotalCount; + exemptedCount += exemptedByAge + exemptedByRule; + inscopeCount += inScopeRule - exemptedByRule; // inScopeRule includes those exempted by Rule as + // well. + scannedCount += Long.valueOf(response.get("passed").toString()) - exemptedByRule; + unscannedCount += Long.valueOf(response.get("failed").toString()); + } + } + } catch (Exception e) { + throw new ServiceException(e); + } + } + + long nonCompliantCount = vulnerabilityRepository.getNonCompliantResourceIds(assetGroup).size(); + + Map compliant = vulnerabilityRepository.getCompliantHostsBySeverity(assetGroup); + Map noncompliant = vulnerabilityRepository.getUniqueHostBySeverity(assetGroup, "3,4,5"); + + noncompliant.put("S3", + Long.valueOf(noncompliant.get("S3").toString()) - Long.valueOf(compliant.get("S3").toString())); + noncompliant.put("S4", + Long.valueOf(noncompliant.get("S4").toString()) - Long.valueOf(compliant.get("S4").toString())); + + for (String key : compliant.keySet()) { + Map countMap = new HashMap<>(); + countMap.put(COUNT, Long.valueOf(compliant.get(key).toString())); + compliant.put(key, countMap); + } + + for (String key : noncompliant.keySet()) { + Map countMap = new HashMap<>(); + countMap.put(COUNT, Long.valueOf(noncompliant.get(key).toString())); + noncompliant.put(key, countMap); + } + + Map response = new HashMap<>(); + response.put(COUNT, totalCount); + + Map exempted = new HashMap<>(); + exempted.put(COUNT, exemptedCount); + response.put("exempted", exempted); + + Map inscope = new HashMap<>(); + inscope.put(COUNT, inscopeCount); + Map unscanned = new HashMap<>(); + unscanned.put(COUNT, unscannedCount); + inscope.put("unscanned", unscanned); + Map scanned = new HashMap<>(); + scanned.put(COUNT, scannedCount); + + compliant.put(COUNT, scannedCount - nonCompliantCount); + scanned.put("compliant", compliant); + noncompliant.put(COUNT, nonCompliantCount); + scanned.put("noncompliant", noncompliant); + + inscope.put("scanned", scanned); + response.put("inscope", inscope); + + return response; + } + + /** + * Gets the vulnerability assets trend. + * + * @param assetGroup + * the asset group + * @param severity + * the severity + * @param from + * the from + * @return the vulnerability assets trend + * @throws DataException + * the data exception + */ + public List> getVulnerabilityAssetsTrend(String assetGroup, String severity, Date from) + throws DataException { + + List> dateList = new ArrayList<>(); + + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + Map totalAssetsEc2 = new HashMap<>(); + Map totalAssetsOnPrem = new HashMap<>(); + + Map totalAssetsAffected = new HashMap<>(); + + ExecutorService executionService = Executors.newFixedThreadPool(3); + LocalDate fromDate = LocalDate.parse(new SimpleDateFormat("yyyy-MM-dd").format(from)); + + executionService.execute(() -> { + try { + long start = System.currentTimeMillis(); + totalAssetsAffected.putAll(vulnTrendGenerator.fetchAssetsAffected(assetGroup, fromDate, severity)); + System.out.println("Time taken assset affected " + (System.currentTimeMillis() - start)); + } catch (Exception e) { + logger.error("Error in fetchAssetsAffected fetch", e); + } + }); + + for (String vulnType : vulnTargetTypes) { + + switch(vulnType) { + + case EC2 : { + executionService.execute(() -> { + try { + long start = System.currentTimeMillis(); + totalAssetsEc2.putAll( + vulnTrendGenerator.fetchAssetsDateRangesForEc2(assetGroup, fromDate)); + //parallelStream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))); + System.out.println("Time taken EC2 Find ALL" + (System.currentTimeMillis() - start)); + } catch (Exception e) { + logger.error("Error in getVulnerabilityAssetsTrend fetch assets for EC2", e); + } + }); + break; + } + case ONPREMSERVER : { + executionService.execute(() -> { + long start = System.currentTimeMillis(); + + try { + totalAssetsOnPrem.putAll( + vulnTrendGenerator.fetchAssetsDateRangesOnPrem(assetGroup, fromDate).parallelStream() + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))); + System.out.println("Time taken ONPREM Find ALL" + (System.currentTimeMillis() - start)); + + } catch (Exception e) { + logger.error("Error in getVulnerabilityAssetsTrend fetch assets for On Prem", e); + } + }); + break; + + } + } + } + + executionService.shutdown(); + while (!executionService.isTerminated()) + ; + + Map totalAssets = addMapValues(totalAssetsEc2, totalAssetsOnPrem); + + totalAssets.entrySet().forEach(entry -> { + Map dateObj = new HashMap<>(); + String date = entry.getKey(); + Long asset = entry.getValue(); + Long assetAffected = totalAssetsAffected.get(date); + + dateObj.put("date", date); + dateObj.put("totalAssets", asset); + dateObj.put("assetsAffected", assetAffected == null ? 0l : assetAffected); + dateList.add(dateObj); + + }); + + return dateList; + } + + /** + * Adds the map values. + * + * @param ec2Assets + * the ec2 assets + * @param onpremAssets + * the onprem assets + * @return the map + */ + private Map addMapValues(Map ec2Assets, Map onpremAssets) { + Map totalMap = new HashMap<>(ec2Assets); + for (String key : onpremAssets.keySet()) { + if (totalMap.containsKey(key)) { + totalMap.put(key, onpremAssets.get(key) + totalMap.get(key)); + } else { + totalMap.put(key, onpremAssets.get(key)); + } + } + return totalMap; + } + + public Map> getDirectorsForAGByApp(Set appNames) throws Exception { + Map> directorsInfo = new HashMap<>(); + ObjectMapper mapper = new ObjectMapper(); + + vulnerabilityRepository.fetchOrgInfoForApps().forEach(app -> { + try { + if (appNames.contains(app.get(APP_TAG))) { + List> orgInfo = new ArrayList<>(); + if (null != app.get("list")) { + orgInfo.addAll(mapper.readValue(app.get("list").toString(), + new TypeReference>>() { + })); + } + orgInfo.forEach(info -> { + if (directorsInfo.containsKey(info.get("mgmntLevel"))) { + directorsInfo.get(info.get("mgmntLevel")).put(app.get(APP_TAG).toString(), + info.get("name")); + } else { + Map director = new HashMap<>(); + director.put(app.get(APP_TAG).toString(), info.get("name")); + directorsInfo.put(info.get("mgmntLevel"), director); + } + }); + } + } catch (Exception e) { + logger.error("Error in getDirectorsForAG ", e); + } + }); + return directorsInfo; + } + + private Map>> getDirectorsForAG(Set appNames) throws Exception { + + Map>> directorsInfo = new HashMap<>(); + ObjectMapper mapper = new ObjectMapper(); + + vulnerabilityRepository.fetchOrgInfoForApps().forEach(app -> { + try { + if (appNames.contains(app.get(APP_TAG))) { + List> orgInfo = new ArrayList<>(); + if (null != app.get("list")) { + orgInfo.addAll(mapper.readValue(app.get("list").toString(), + new TypeReference>>() { + })); + } + orgInfo.forEach(info -> { + if (directorsInfo.containsKey(info.get("mgmntLevel"))) { + if (directorsInfo.get(info.get("mgmntLevel")).containsKey(info.get("name"))) { + directorsInfo.get(info.get("mgmntLevel")).get(info.get("name")) + .add(app.get(APP_TAG).toString()); + } else { + List apps = new ArrayList<>(); + apps.add(app.get(APP_TAG).toString()); + directorsInfo.get(info.get("mgmntLevel")).put(info.get("name"), apps); + } + } else { + Map> director = new HashMap<>(); + List apps = new ArrayList<>(); + apps.add(app.get(APP_TAG).toString()); + director.put(info.get("name"), apps); + directorsInfo.put(info.get("mgmntLevel"), director); + } + }); + } + } catch (Exception e) { + logger.error("Error in getDirectorsForAG ", e); + } + }); + return directorsInfo; + } + + private void formPerformersData(Map appDetails, Map perfData, + Map> directApp) { + for (Entry> entry : directApp.entrySet()) { + + String director = entry.getKey(); + int directorVuln = 0; + for (String app : entry.getValue()) { + directorVuln += appDetails.get(app); + } + + if (!perfData.isEmpty()) { + boolean directorExists = false; + for (Entry existingDirectorData : perfData.entrySet()) { + if (director.equals(existingDirectorData.getKey())) { + perfData.put(director, existingDirectorData.getValue() + directorVuln); + directorExists = true; + break; + } + } + if (!directorExists) { + perfData.put(director, directorVuln); + } + } else { + perfData.put(director, directorVuln); + } + } + } + + private void formDistributionSummaryDetails(Map directApp, String appName, + List> directorData, Map sevInfo, String severity) { + if (!directorData.isEmpty()) { + if (StringUtils.isNotEmpty(directApp.get(appName))) { + String director = directApp.get(appName); + boolean directorExists = false; + for (Map existingDirectorData : directorData) { + if (director.equals(existingDirectorData.get("name"))) { + existingDirectorData.put(COUNT, Integer.valueOf(existingDirectorData.get(COUNT).toString()) + + Integer.valueOf(sevInfo.get("S" + severity).toString())); + directorExists = true; + break; + } + } + if (!directorExists) { + Map directorTemp = new HashMap<>(); + directorTemp.put("name", director); + directorTemp.put(COUNT, sevInfo.get("S" + severity)); + directorData.add(directorTemp); + } + } + } else { + if (StringUtils.isNotEmpty(directApp.get(appName))) { + Map directorTemp = new HashMap<>(); + directorTemp.put("name", directApp.get(appName)); + directorTemp.put(COUNT, sevInfo.get("S" + severity)); + directorData.add(directorTemp); + } + } + } + + private void formAgingDistributionSummaryDetails(Map directApp, String appName, + List> directorData, Map sevInfo, String severity) { + if (!directorData.isEmpty()) { + if (StringUtils.isNotEmpty(directApp.get(appName))) { + String director = directApp.get(appName); + boolean directorExists = false; + for (Map existingDirectorData : directorData) { + if (director.equals(existingDirectorData.get("name"))) { + existingDirectorData.put("days", Double.valueOf(existingDirectorData.get("days").toString()) + + Double.valueOf(sevInfo.get("days").toString())); + existingDirectorData.put(COUNT, Double.valueOf(existingDirectorData.get(COUNT).toString()) + + Double.valueOf(sevInfo.get(COUNT).toString())); + directorExists = true; + break; + } + } + if (!directorExists) { + Map directorTemp = new HashMap<>(); + directorTemp.put("name", director); + directorTemp.put("days", sevInfo.get("days")); + directorTemp.put(COUNT, sevInfo.get(COUNT)); + directorData.add(directorTemp); + } + } + } else { + if (StringUtils.isNotEmpty(directApp.get(appName))) { + Map directorTemp = new HashMap<>(); + directorTemp.put("name", directApp.get(appName)); + directorTemp.put("days", sevInfo.get("days")); + directorTemp.put(COUNT, sevInfo.get(COUNT)); + directorData.add(directorTemp); + } + } + } + + private void validatingAgingDays(List> directorData) { + + directorData.parallelStream().forEach(director -> { + if (director.get(COUNT).toString().equals(ZERO) || director.get(COUNT).toString().equals(DOUBLE_ZERO)) { + director.put("days", 0); + } else { + director.put("days", Math.floor(Double.valueOf(director.get("days").toString()) + / Double.valueOf(director.get(COUNT).toString()))); + } + director.remove(COUNT); + }); + } + + public ResponseEntity formatException(ServiceException e) { + if (e.getMessage().contains(NO_DATA_FOUND)) { + List> emptylist = new ArrayList<>(); + ResponseData res = new ResponseData(emptylist); + return ResponseUtils.buildSucessResponse(res); + } else { + return ResponseUtils.buildFailureResponse(e); + } + } + + public Map getTrendProgress(String assetGroup, String ruleId, LocalDate startDate, + LocalDate endDate, String trendCategory) throws ServiceException { + + List> trendList; + try { + trendList = vulnerabilityRepository.getTrendProgress(assetGroup, ruleId, startDate, endDate, trendCategory); + } catch (DataException e) { + throw new ServiceException(e); + } + if (!trendList.isEmpty()) { + + // Sort the list by the date in ascending order + Comparator> comp = (m1, m2) -> LocalDate + .parse(m1.get("date").toString(), DateTimeFormatter.ISO_DATE) + .compareTo(LocalDate.parse(m2.get("date").toString(), DateTimeFormatter.ISO_DATE)); + Collections.sort(trendList, comp); + LocalDate trendStartDate = LocalDate.parse(trendList.get(0).get("date").toString()); + + // Elastic Search might not have data for some dates. But we want to + // send consistent data to the consumers of this service, so we will + // populate previous where data is unavailable + fillNoDataDatesWithPrevious(trendList, trendStartDate, endDate); + + useRealTimeDataForLatestDate(trendList, assetGroup, trendCategory, ruleId, null); + + // ADD compliance_percent if not available . This is done + // temporarily.Will update with compliance_percent at source + + appendWithCompliancePercent(trendList); + + return segregateTrendProgressByWeek(assetGroup, trendList, trendStartDate, endDate); + } else { + return new HashMap<>(); + } + } + + /** + * Segregate trend progress by week. + * + * @param assetGroup + * the asset group + * @param trendProgressList + * the trend progress list + * @param startDate + * the start date + * @param endDate + * the end date + * @return the map + */ + private Map segregateTrendProgressByWeek(String assetGroup, + List> trendProgressList, LocalDate startDate, LocalDate endDate) { + + long maxInstancesForTheCompleteDateRange = 0; + + long totalNumberRunningValue = 0; + long compliantRunningValue = 0; + long noncompliantRunningValue = 0; + double complianceRunningValue = 0; + + List> allWeeksDataList = new ArrayList<>(); + + // The first day of date range is taken as the first day of week 1 of + // the + // quarter. This + // could be a Monday, Thursday or ANY day. + LocalDate startingDayOfWeek = startDate; + + // Add 6 days to get the end date. If we start on a Thursday, the week + // ends on next Wednesday + LocalDate endingDayOfWeek = startingDayOfWeek.plusDays(SIX); + + List> trendListForTheWeek = new ArrayList<>(); + + // We will send 100 weeks at most. Start with week 1(There + // is no week zero!) + for (int weekNumber = 1; weekNumber <= HUNDRED; weekNumber++) { + + LocalDate startingDayOfWeekLocalCopy = startingDayOfWeek; + LocalDate endingDayOfWeekLocalCopy = endingDayOfWeek; + + trendProgressList.forEach(ruleTrendProgressMap -> ruleTrendProgressMap.forEach((key, value) -> { + + if ("date".equals(key)) { + + // Check if this date falls in the week that we are + // currently interested in + LocalDate dateInThisIteration = LocalDate.parse(value.toString(), DateTimeFormatter.ISO_DATE); + if (dateInThisIteration.isAfter(startingDayOfWeekLocalCopy.minusDays(1)) + && (dateInThisIteration.isBefore(endingDayOfWeekLocalCopy.plusDays(1)))) { + // If the date matches, lets pick the map which + // represents this date's patching data and add + // it to + // the weeks list + trendListForTheWeek.add(ruleTrendProgressMap); + } + + } + + })); + + Map mapForTheWeek = new LinkedHashMap<>(); + + // First some k-v pairs for week number,week start date, week end + // date + mapForTheWeek.put("week", weekNumber); + mapForTheWeek.put("start_date", startingDayOfWeek.format(DateTimeFormatter.ISO_DATE)); + mapForTheWeek.put("end_date", endingDayOfWeek.format(DateTimeFormatter.ISO_DATE)); + + // Lets calculate the compliance for the week. We simply get the + // compliance for the last day of the week + + complianceRunningValue = calculateWeeklyCompliance(trendListForTheWeek); + mapForTheWeek.put(COMPLIANCE_PERCENTAGE, complianceRunningValue); + trendListForTheWeek.forEach(ruleTrendProgressMap -> { + // We don't need _id in the response + ruleTrendProgressMap.remove("_id"); + }); + + // Store a 'copy' of the weeks array list instead of the original, + // as we will clear the original and reuse it for the next + // iteration. Lets call this by the key 'compliance_info' + mapForTheWeek.put("compliance_info", new ArrayList>(trendListForTheWeek)); + + if (!trendListForTheWeek.isEmpty()) { + allWeeksDataList.add(mapForTheWeek); + + totalNumberRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList(TOTAL, + trendListForTheWeek); + compliantRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList(COMPLAINT, + trendListForTheWeek); + noncompliantRunningValue = (long) getLatestDaysNumericDataFromAWeeklyDataList(NON_COMPLIANT, + trendListForTheWeek); + + // Maintain a max instance number for the quarter that is being + // processed. + long maxInstancesRunningValue = (long) getMaxValueNumericDataFromAWeeklyDataList(TOTAL, + trendListForTheWeek); + if (maxInstancesRunningValue > maxInstancesForTheCompleteDateRange) { + maxInstancesForTheCompleteDateRange = maxInstancesRunningValue; + } + + } + + // Now, lets get ready for the iteration for next week + trendListForTheWeek.clear(); + startingDayOfWeek = startingDayOfWeek.plusDays(7); + endingDayOfWeek = endingDayOfWeek.plusDays(7); + + // If week ending date bypasses the quarter end date, lets rewind + // back to quarter end date. The quarter end date will be set as the + // week ending date. + } + + Map quarterlyDataMap = new LinkedHashMap<>(); + quarterlyDataMap.put("ag", assetGroup); + quarterlyDataMap.put("start_date", startDate.format(DateTimeFormatter.ISO_DATE)); + quarterlyDataMap.put("end_date", endDate.format(DateTimeFormatter.ISO_DATE)); + quarterlyDataMap.put("max", maxInstancesForTheCompleteDateRange); + quarterlyDataMap.put(TOTAL, totalNumberRunningValue); + quarterlyDataMap.put(COMPLAINT, compliantRunningValue); + quarterlyDataMap.put(NON_COMPLIANT, noncompliantRunningValue); + quarterlyDataMap.put(COMPLIANCE_PERCENTAGE, complianceRunningValue); + + quarterlyDataMap.put("compliance_trend", allWeeksDataList); + + return quarterlyDataMap; + + } + + private void fillNoDataDatesWithPrevious(List> trendList, LocalDate firstDay, + LocalDate lastDay) { + + // We don't want data for future weeks. If the quarter being + // requested is the ongoing quarter, the max we we are interested + // is data up to and including the ongoing day in the ongoing week. + if (lastDay.isAfter(LocalDate.now())) { + lastDay = LocalDate.now(); + } + + List listOfAllDates = new ArrayList<>(); + + LocalDate iterationDate = firstDay; + + // Have a temp variable called iterationDate. Keep incrementing it by 1, + // until we reach the end date. In each such iteration, add each date to + // our list of dates + while (!iterationDate.isAfter(lastDay)) { + listOfAllDates.add(iterationDate); + iterationDate = iterationDate.plusDays(1); + } + + // Iterate through each date. If the data from ES is missing for any + // such + // date, add a dummy map with zero values + Map currentData = new LinkedHashMap<>(); + currentData.put(TOTAL, 0); + currentData.put(COMPLAINT, 0); + currentData.put(NON_COMPLIANT, 0); + currentData.put(COMPLIANCE_PERCENT, HUNDRED); + + for (int i = 0; i < listOfAllDates.size(); i++) { + LocalDate date = listOfAllDates.get(i); + Map trendInfo = getTrendDataForDate(trendList, date); + if (trendInfo == null) { + trendInfo = new LinkedHashMap<>(); + trendInfo.put("date", date.format(DateTimeFormatter.ISO_DATE)); + trendInfo.put(NON_COMPLIANT, currentData.get(NON_COMPLIANT)); + trendInfo.put(TOTAL, currentData.get(TOTAL)); + trendInfo.put(COMPLAINT, currentData.get(COMPLAINT)); + if (currentData.get(COMPLIANCE_PERCENT) != null) { + trendInfo.put(COMPLIANCE_PERCENT, currentData.get(COMPLIANCE_PERCENT)); + } + trendList.add(i, trendInfo); + } else { + currentData = trendInfo; + } + } + + } + + /** + * Gets the trend data for date. + * + * @param trendList + * the trend list + * @param date + * the date + * @return the trend data for date + */ + private Map getTrendDataForDate(List> trendList, LocalDate date) { + + List> match = trendList.stream().filter(trendMap -> { + LocalDate dateInThisIteration = LocalDate.parse(trendMap.get("date").toString(), + DateTimeFormatter.ISO_DATE); + return dateInThisIteration.isEqual(date); + }).collect(Collectors.toList()); + if (match != null && !match.isEmpty()) { + return match.get(0); + } + return null; + } + + public void useRealTimeDataForLatestDate(List> trendList, String ag, String trendCategory, + String ruleId, String domain) throws ServiceException { + Map latestDaysTrendData = new HashMap<>(trendList.get(trendList.size() - 1)); + Map baseApiReturnMap = new HashMap<>(); + Map overallCompliance = new HashMap<>(); + long compliantQuantity = 0; + long noncompliantQuantity = 0; + long total = 0; + double compliance; + LocalDate today; + try { + switch (trendCategory) { + + case "vulncompliance": + Map vulnSummary = getVulnerabilitySummary(ag, SEVERITY_LEVELS); + total = Long.valueOf(vulnSummary.get("hosts").toString()); + noncompliantQuantity = Long.valueOf(vulnSummary.get("totalVulnerableAssets").toString()); + compliantQuantity = total - noncompliantQuantity; + + latestDaysTrendData.put(COMPLAINT, compliantQuantity); + latestDaysTrendData.put(NON_COMPLIANT, noncompliantQuantity); + latestDaysTrendData.put(TOTAL, total); + if (total > 0) { + compliance = Math.floor(compliantQuantity * HUNDRED / total); + } else { + compliance = INT_HUNDRED; + } + latestDaysTrendData.put(COMPLIANCE_PERCENT, compliance); + break; + + default: + // nothings + } + + // Check if the trend already has todays data (Compare dates) + // If yes, overwrite. If not, add at the end. + LocalDate date = null; + today = LocalDate.now(); + date = LocalDate.parse(latestDaysTrendData.get("date").toString(), DateTimeFormatter.ISO_LOCAL_DATE); + + if (date.isEqual(today)) { + logger.info("Latest days data available in trend data, so overwriting"); + trendList.set(trendList.size() - 1, latestDaysTrendData); + } else if (date.isEqual(today.minusDays(1))) { + // Ideally we need to consider this case only else, we may + // unnecessarily append wrong data. FOr eg. In case of patching + // if any previous/ progress is requested. + logger.info("Latest days data is NOT available in trend data, so adding at the end"); + latestDaysTrendData.put("date", today.format(DateTimeFormatter.ISO_LOCAL_DATE)); + trendList.add(latestDaysTrendData); + } + + } catch (ServiceException e) { + logger.error("Call to Base API to get todays data failed", e); + return; + } + + } + + /** + * Append with compliance percent. + * + * @param trendList + * the trend list + */ + private void appendWithCompliancePercent(List> trendList) { + + trendList.parallelStream().forEach(trend -> { + if (trend.get(COMPLIANCE_PERCENT) == null) { + double total = Double.parseDouble(trend.get(TOTAL).toString()); + double compliant = Double.parseDouble(trend.get(COMPLAINT).toString()); + double compliancePercent = HUNDRED; + if (total > 0) { + compliancePercent = Math.floor(compliant * HUNDRED / total); + } + trend.put(COMPLIANCE_PERCENT, compliancePercent); + } + }); + } + + /** + * Calculate weekly compliance. + * + * @param trendProgressListForTheWeek + * the trend progress list for the week + * @return the double + */ + private double calculateWeeklyCompliance(List> trendProgressListForTheWeek) { + + int index = trendProgressListForTheWeek.size() - 1; + while (index >= 0) { + Object percentObj = trendProgressListForTheWeek.get(index).get(COMPLIANCE_PERCENT); + if (null != percentObj && Double.valueOf(percentObj.toString()) != 0) { + return Double.valueOf(percentObj.toString()); + } + index--; + } + return HUNDRED; + + } + + /** + * Gets the max value numeric data from A weekly data list. + * + * @param dataKeyName + * the data key name + * @param trendProgressListForTheWeek + * the trend progress list for the week + * @return the max value numeric data from A weekly data list + */ + private double getMaxValueNumericDataFromAWeeklyDataList(String dataKeyName, + List> trendProgressListForTheWeek) { + + double maxValue = 0; + int index = trendProgressListForTheWeek.size() - 1; + + while (index >= 0) { + Object obj = trendProgressListForTheWeek.get(index).get(dataKeyName); + if (null != obj && Double.valueOf(obj.toString()) != 0 && (Double.valueOf(obj.toString()) > maxValue)) { + maxValue = Double.valueOf(obj.toString()); + } + index--; + } + + return maxValue; + } + + /** + * Gets the latest days numeric data from A weekly data list. + * + * @param dataKeyName + * the data key name + * @param ruleTrendProgressListForTheWeek + * the rule trend progress list for the week + * @return the latest days numeric data from A weekly data list + */ + private double getLatestDaysNumericDataFromAWeeklyDataList(String dataKeyName, + List> ruleTrendProgressListForTheWeek) { + + int index = ruleTrendProgressListForTheWeek.size() - 1; + + // We take the latest days data, provided its a non-zero value + while (index >= 0) { + Object obj = ruleTrendProgressListForTheWeek.get(index).get(dataKeyName); + if (null != obj && Double.valueOf(obj.toString()) != 0) { + return Double.valueOf(obj.toString()); + } + index--; + } + + return 0; + } + + public List getResourceId(List> vulnerabilityOccuranceList) { + List resourceIdList = new ArrayList(); + for (Map map : vulnerabilityOccuranceList) { + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + if (key.equals("_resourceid")) { + String value = entry.getValue().toString(); + resourceIdList.add(value); + } + + } + } + return resourceIdList.stream().distinct().collect(Collectors.toList()); + } + + public List> getCartesianProduct(List> vulnerabilityOccuranceList, + List> resourceIdDetails) { + List> finalVulnerabilityOccuranceList = new ArrayList>(); + LinkedHashMap> mapOfResourceAttributeMap = new LinkedHashMap>(); + for (Map resourceAttributeMap : resourceIdDetails) { + mapOfResourceAttributeMap.put((String) resourceAttributeMap.get("_resourceid"), resourceAttributeMap); + } + + /* + * mapOfResourceAttributeMap = resourceIdDetails.stream() + * .collect(Collectors.toMap(obj -> obj.get("_resourceid").toString(), obj -> + * obj)); + */ + for (Map vulnerabilityOccuranceMap : vulnerabilityOccuranceList) { + LinkedHashMap finalVulnerabilityOccuranceMap = new LinkedHashMap<>( + vulnerabilityOccuranceMap); + String resourceId = (String) vulnerabilityOccuranceMap.get("_resourceid"); + if (!mapOfResourceAttributeMap.containsKey(resourceId)) { + continue; + } + + finalVulnerabilityOccuranceMap.putAll(mapOfResourceAttributeMap.get(resourceId)); + finalVulnerabilityOccuranceList.add(finalVulnerabilityOccuranceMap); + } + + return finalVulnerabilityOccuranceList; + } + + public int vulnerabilityAssetCount(String assetGroup, Map termsFilter, String applicationFilter, + String environmentFilter, int from, int size) throws Exception { + + List vulnTargetTypes = getVulnTargetTypes(assetGroup); + int totalCount = 0; + if (!vulnTargetTypes.isEmpty()) { + for (String parentType : vulnTargetTypes) { + totalCount = totalCount + countForTargetType(assetGroup, parentType, termsFilter, applicationFilter, + environmentFilter, from, size); + } + } + + return totalCount; + } + + public int countForTargetType(String assetGroup, String parentType, Map termsFilter, + String applicationFilter, String environmentFilter, int from, int size) throws Exception { + int count = 0; + String targetType = "vulninfo"; + Map mustFilterMap = new LinkedHashMap<>(); + mustFilterMap.put("latest", "true"); + Map parentBool = new HashMap<>(); + List> mustList = new ArrayList<>(); + Map matchMap = new HashMap<>(); + Map match = new HashMap<>(); + Map mustTermsFilter = new HashMap<>(); + for (Map.Entry entry : termsFilter.entrySet()) { + List severities = Arrays.asList(entry.getValue().split(",")); + mustTermsFilter.put(entry.getKey(), severities); + } + match.put(Constants.LATEST, Constants.TRUE); + matchMap.put(Constants.MATCH, match); + mustList.add(matchMap); + + if (applicationFilter != null) { + Map applicationFilterMap = new HashMap(); + Map applicationFilterMap1 = new HashMap(); + applicationFilterMap.put("tags.Application.keyword", applicationFilter); + applicationFilterMap1.put("match", applicationFilterMap); + mustList.add(applicationFilterMap1); + } + + if (environmentFilter != null) { + Map environmentFilterMap = new HashMap(); + Map environmentFilterMap1 = new HashMap(); + environmentFilterMap.put("tags.Environment.keyword", environmentFilter); + environmentFilterMap1.put("match", environmentFilterMap); + mustList.add(environmentFilterMap1); + } + + parentBool.put("must", mustList); + Map queryMap = new HashMap<>(); + queryMap.put("bool", parentBool); + Map parentEntryMap = new LinkedHashMap<>(); + parentEntryMap.put("parent_type", parentType); + parentEntryMap.put("query", queryMap); + mustFilterMap.put("has_parent", parentEntryMap); + count = vulnerabilityRepository.vulnerabilityAssetsCount(assetGroup, targetType, mustFilterMap, from, size, + mustTermsFilter); + logger.info("vulnerability asset count {} " + count); + return count; + + } +} diff --git a/api/pacman-api-vulnerability/src/main/resources/banner.txt b/api/pacman-api-vulnerability/src/main/resources/banner.txt new file mode 100644 index 000000000..c17bc74a7 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/resources/banner.txt @@ -0,0 +1,52 @@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}8${AnsiColor.WHITE}o${AnsiColor.WHITE}*${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.WHITE}*${AnsiColor.WHITE}o${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}:${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}:${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}:${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}&${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}o${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}:${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}@${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}*${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}*${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}o${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}:${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}:${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}:${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}:${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}:${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}:${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}:${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}:${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}*${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}*${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}:${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}&${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}*${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}o${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}*${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BRIGHT_BLACK}#${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_BLACK}&${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.WHITE}o${AnsiColor.BRIGHT_YELLOW}*${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.BRIGHT_YELLOW}.${AnsiColor.WHITE}*${AnsiColor.BRIGHT_BLACK}8${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ +${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@${AnsiColor.BLACK}@ \ No newline at end of file diff --git a/api/pacman-api-vulnerability/src/main/resources/bootstrap.yml b/api/pacman-api-vulnerability/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..b95702daa --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +spring: + application: + name: vulnerability-service + title: Pacman Service + description: Pacman API provides vulnerability capabilities + cloud: + config: + uri: ${CONFIG_SERVER_URL:http://localhost:8888/api/config/} + enabled: true + fail-fast: true + name: api,vulnerability-service + password: ${CONFIG_PASSWORD} + username: user + label: latest + profiles: + active: ${ENVIRONMENT:prd} + +security: + oauth2: + resource: + user-info-uri: ${PACMAN_HOST_NAME}/api/auth/user diff --git a/api/pacman-api-vulnerability/src/main/resources/spring-logback.xml b/api/pacman-api-vulnerability/src/main/resources/spring-logback.xml new file mode 100644 index 000000000..3224e2637 --- /dev/null +++ b/api/pacman-api-vulnerability/src/main/resources/spring-logback.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + ${consoleLoggingLevel} + + + %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level + %logger{36}.%M - %msg%n + + + + + + ${esLoggingLevel} + + /var/log/pacman-api-${microserviceName}-${profile}.log + true + + + true + yyyy-MM-dd'T'HH:mm:ss.SSSX + Etc/UTC + + false + + + + + + + + + + \ No newline at end of file diff --git a/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/controller/VulnerabilityControllerTest.java b/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/controller/VulnerabilityControllerTest.java new file mode 100644 index 000000000..5111a60fe --- /dev/null +++ b/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/controller/VulnerabilityControllerTest.java @@ -0,0 +1,685 @@ +package com.tmobile.pacman.api.vulnerability.controller; +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ + +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.anyListOf; +import static org.powermock.api.mockito.PowerMockito.when; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.exception.ServiceException; +import com.tmobile.pacman.api.vulnerability.controller.VulnerabilityController; +import com.tmobile.pacman.api.vulnerability.domain.Request; +import com.tmobile.pacman.api.vulnerability.domain.TrendNote; +import com.tmobile.pacman.api.vulnerability.domain.TrendRequest; +import com.tmobile.pacman.api.vulnerability.service.VulnerabilityService; + +@RunWith(PowerMockRunner.class) +public class VulnerabilityControllerTest { + + @InjectMocks + VulnerabilityController vulnerabilityController; + + @Mock + VulnerabilityService vulnerabilityService; + + @Test + public void getVulnerabilitiesDetailsTest() throws Exception { + + List> vulnDetails = new ArrayList<>(); + + Request request = new Request(); + request.setAg("ag"); + request.setFrom(0); + + when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(new ArrayList<>()); + + assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.OK); + + request.setFilter(new HashMap<>()); + vulnDetails.add(new HashMap<>()); + vulnDetails.add(new HashMap<>()); + + when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(vulnDetails); + + assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.OK); + + request.setSize(1); + when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(vulnDetails); + + assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.OK); + + request.setSize(3); + when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(vulnDetails); + + assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request).getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilitiesDetailsTest_Failure() throws Exception { + + Request request = new Request(); + + assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + request.setAg("ag"); + request.setFrom(-1); + + assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + request.setFrom(2); + List> vulnDetails = new ArrayList>(); + vulnDetails.add(new HashMap<>()); + + when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())).thenReturn(new ArrayList<>()); + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(vulnDetails); + + assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getCertificatesDetailsTest_Exception() throws Exception { + + Request request = new Request(); + request.setAg("ag"); + request.setFrom(0); + + when(vulnerabilityService.getVulnerabilitiesDetails(anyString(), anyObject())) + .thenThrow(new ServiceException()); + assertTrue(vulnerabilityController.getVulnerabilitiesDetails(request) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilitysummaryTest() throws Exception { + + when(vulnerabilityService.getVulnerabilitySummary(anyString(), anyString())).thenReturn(new HashMap<>()); + assertTrue(vulnerabilityController.getVulnerabilitysummary("ag", "3,4,5").getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilitysummaryTest_Exception() throws Exception { + + assertTrue(vulnerabilityController.getVulnerabilitysummary("", "3,4,5") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + when(vulnerabilityService.getVulnerabilitySummary(anyString(), anyString())).thenThrow(new ServiceException()); + assertTrue(vulnerabilityController.getVulnerabilitysummary("ag", "3,4,5") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilityByApplicationsTest() throws Exception { + + when(vulnerabilityService.getVulnerabilityByAppAndEnv(anyString(), anyString(), anyString())) + .thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilityByApplications("ag").getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilityByApplicationsTest_Exception() throws Exception { + + assertTrue(vulnerabilityController.getVulnerabilityByApplications("") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + when(vulnerabilityService.getVulnerabilityByAppAndEnv(anyString(), anyString(), anyString())) + .thenThrow(new Exception()); + assertTrue(vulnerabilityController.getVulnerabilityByApplications("ag") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilitiesTrendTest() throws Exception { + + TrendRequest request = new TrendRequest(); + request.setAg("ag"); + + when(vulnerabilityService.getVulnerabilityTrend(anyString(), anyObject(), anyObject(), anyObject())) + .thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.OK); + + request.setFrom(new Date()); + when(vulnerabilityService.getVulnerabilityTrend(anyString(), anyObject(), anyObject(), anyObject())) + .thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.OK); + + request.setTo(new Date()); + when(vulnerabilityService.getVulnerabilityTrend(anyString(), anyObject(), anyObject(), anyObject())) + .thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.OK); + + request = new TrendRequest(); + request.setAg("ag"); + request.setTo(new Date()); + when(vulnerabilityService.getVulnerabilityTrend(anyString(), anyObject(), anyObject(), anyObject())) + .thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request).getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilitiesTrendTest_Exception() throws Exception { + + TrendRequest request = new TrendRequest(); + assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + request.setAg("ag"); + when(vulnerabilityService.getVulnerabilityTrend(anyString(), anyObject(), anyObject(), anyObject())) + .thenThrow(new ServiceException()); + assertTrue(vulnerabilityController.getVulnerabilitiesTrend(request) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilityByEnvironmentTest() throws Exception { + + when(vulnerabilityService.getVulnerabilityByAppAndEnv(anyString(), anyString(), anyString())) + .thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilityByEnvironment("ag", "app").getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilityByEnvironmentTest_Exception() throws Exception { + + assertTrue(vulnerabilityController.getVulnerabilityByEnvironment("", null) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + when(vulnerabilityService.getVulnerabilityByAppAndEnv(anyString(), anyString(), anyString())) + .thenThrow(new Exception()); + assertTrue(vulnerabilityController.getVulnerabilityByEnvironment("ag", "app") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilityDistributionTest() throws Exception { + + when(vulnerabilityService.getVulnerabilitiesDistribution(anyString())).thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilityDistribution("ag").getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilityDistributionTest_Exception() throws Exception { + + assertTrue(vulnerabilityController.getVulnerabilityDistribution("") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + when(vulnerabilityService.getVulnerabilitiesDistribution(anyString())).thenThrow(new ServiceException()); + assertTrue(vulnerabilityController.getVulnerabilityDistribution("ag") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilitysummaryByResourceIdTest() throws Exception { + + when(vulnerabilityService.getVulnerabilitysummaryByResourceId(anyString())).thenReturn(new HashMap<>()); + assertTrue(vulnerabilityController.getVulnerabilitysummaryByResourceId("ag").getStatusCode() == HttpStatus.OK); + } + + + @Test + public void getVulnerabilitysummaryByResourceIdTest_Exception() throws Exception { + + when(vulnerabilityService.getVulnerabilitysummaryByResourceId(anyString())).thenThrow(new Exception()); + assertTrue(vulnerabilityController.getVulnerabilitysummaryByResourceId("ag").getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + + @Test + public void getVulnerabilityDetailsByResourceIdTest() throws Exception { + + when(vulnerabilityService.getVulnerabilityDetailsByResourceId(anyString())).thenReturn(new ArrayList<>()); + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(new ArrayList<>()); + + assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id", "search", 0, 0) + .getStatusCode() == HttpStatus.OK); + assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id", "search", null, null) + .getStatusCode() == HttpStatus.OK); + + List> resourceDetails = new ArrayList<>(); + resourceDetails.add(new HashMap<>()); + resourceDetails.add(new HashMap<>()); + + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(resourceDetails); + assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id", "search", 0, 0) + .getStatusCode() == HttpStatus.OK); + + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(resourceDetails); + assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id", "search", 0, 1) + .getStatusCode() == HttpStatus.OK); + + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(resourceDetails); + assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id", "search", 0, 3) + .getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilityDetailsByResourceIdTest_Exception() throws Exception { + + List> resourceDetails = new ArrayList<>(); + resourceDetails.add(new HashMap<>()); + + when(vulnerabilityService.getVulnerabilityDetailsByResourceId(anyString())).thenReturn(new ArrayList<>()); + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenReturn(resourceDetails); + assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id", "search", 2, 3) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + when(vulnerabilityService.filterMatchingCollectionElements(anyObject(), anyString(), anyBoolean())) + .thenThrow(new ServiceException()); + assertTrue(vulnerabilityController.getVulnerabilityDetailsByResourceId("id", "search", 0, 1) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilityDistributionSummaryTest() throws Exception { + + when(vulnerabilityService.getVulnerabilityDistributionSummary(anyString(), anyString())) + .thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilityDistributionSummary("ag", "sev") + .getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilityDistributionSummaryTest_Exception() throws Exception { + + when(vulnerabilityService.getVulnerabilityDistributionSummary(anyString(), anyString())) + .thenThrow(new Exception()); + assertTrue(vulnerabilityController.getVulnerabilityDistributionSummary("ag", "sev") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getAgingDistributionSummaryTest() throws Exception { + + when(vulnerabilityService.getAgingDistributionSummary(anyString(), anyString())).thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getAgingDistributionSummary("ag", "sev").getStatusCode() == HttpStatus.OK); + } + + @Test + public void getAgingDistributionSummaryTest_Exception() throws Exception { + + when(vulnerabilityService.getAgingDistributionSummary(anyString(), anyString())).thenThrow(new Exception()); + assertTrue(vulnerabilityController.getAgingDistributionSummary("ag", "sev") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getAgingSummaryTest() throws Exception { + + when(vulnerabilityService.getAgingSummary(anyString())).thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getAgingSummary("ag").getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilityByQidTest() throws Exception { + + when(vulnerabilityService.getVulnerabilityByQid(anyString())).thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getVulnerabilityByQid("qid").getStatusCode() == HttpStatus.OK); + } + + @Test + public void getDistributionSummaryByVulnTypeTest() throws Exception { + + when(vulnerabilityService.getDistributionSummaryByVulnType(anyString(), anyString())) + .thenReturn(new ArrayList<>()); + + ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByVulnType("ag", "3"); + assertTrue(responseObj.getStatusCode() == HttpStatus.OK); + } + + @Test + public void getDistributionSummaryByVulnTypeTest_Exception() throws Exception { + + when(vulnerabilityService.getDistributionSummaryByVulnType(anyString(), anyString())) + .thenThrow(new DataException()); + + ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByVulnType("ag", "3"); + assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getDistributionSummaryByInfraTypeTest() throws Exception { + + when(vulnerabilityService.getDistributionSummaryByInfraType(anyString(), anyString())) + .thenReturn(new ArrayList<>()); + + ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByInfraType("ag", "3"); + assertTrue(responseObj.getStatusCode() == HttpStatus.OK); + } + + @Test + public void getDistributionSummaryByInfraTypeTest_Exception() throws Exception { + + when(vulnerabilityService.getDistributionSummaryByInfraType(anyString(), anyString())) + .thenThrow(new ServiceException()); + + ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByInfraType("ag", "3"); + assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getDistributionSummaryByEnvTest() throws Exception { + + when(vulnerabilityService.getDistributionSummaryByEnv(anyString(), anyString())).thenReturn(new ArrayList<>()); + + ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByEnv("ag", "3"); + assertTrue(responseObj.getStatusCode() == HttpStatus.OK); + } + + @Test + public void getDistributionSummaryByEnvTest_Exception() throws Exception { + + when(vulnerabilityService.getDistributionSummaryByEnv(anyString(), anyString())) + .thenThrow(new ServiceException()); + + ResponseEntity responseObj = vulnerabilityController.getDistributionSummaryByEnv("ag", "3"); + assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getRemediationActionsSummaryTest() throws Exception { + + when(vulnerabilityService.getRemediationActionsSummary(anyString(), anyString())).thenReturn(new ArrayList<>()); + + ResponseEntity responseObj = vulnerabilityController.getRemediationActionsSummary("ag", "3"); + assertTrue(responseObj.getStatusCode() == HttpStatus.OK); + } + + @Test + public void getRemediationActionsSummaryTest_Exception() throws Exception { + + when(vulnerabilityService.getRemediationActionsSummary(anyString(), anyString())) + .thenThrow(new DataException()); + + ResponseEntity responseObj = vulnerabilityController.getRemediationActionsSummary("ag", "3"); + assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getV1HighestLowestPeformersTest() throws Exception { + + Map directorData = new HashMap<>(); + directorData.put("dir1", 1); + directorData.put("dir2", 2); + + when(vulnerabilityService.getHighestLowestPerformers(anyString(), anyString(), anyString())) + .thenReturn(directorData); + assertTrue(vulnerabilityController.getHighestLowestPerformers("ag", "3").getStatusCode() == HttpStatus.OK); + + directorData.put("dir3", 3); + + when(vulnerabilityService.getHighestLowestPerformers(anyString(), anyString(), anyString())) + .thenReturn(directorData); + assertTrue(vulnerabilityController.getHighestLowestPerformers("ag", "3").getStatusCode() == HttpStatus.OK); + + directorData.put("dir4", 4); + directorData.put("dir5", 5); + directorData.put("dir6", 6); + directorData.put("dir7", 7); + directorData.put("dir8", 8); + directorData.put("dir9", 9); + directorData.put("dir10", 10); + directorData.put("dir11", 11); + directorData.put("dir12", 12); + + when(vulnerabilityService.getHighestLowestPerformers(anyString(), anyString(), anyString())) + .thenReturn(directorData); + assertTrue(vulnerabilityController.getHighestLowestPerformers("ag", "3").getStatusCode() == HttpStatus.OK); + + } + + @Test + public void getV2HighestLowestPeformersTest() throws Exception { + + Map directorData = new HashMap<>(); + directorData.put("dir1", 1); + directorData.put("dir2", 2); + + when(vulnerabilityService.getHighestLowestPerformers(anyString(), anyString(), anyString())) + .thenReturn(directorData); + assertTrue(vulnerabilityController.getHighestLowestPerformers("ag", "3", PerfType.org) + .getStatusCode() == HttpStatus.OK); + assertTrue(vulnerabilityController.getHighestLowestPerformers("ag", "3", PerfType.application) + .getStatusCode() == HttpStatus.OK); + assertTrue(vulnerabilityController.getHighestLowestPerformers("ag", "3", PerfType.environment) + .getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulerabilityTrendTest() throws Exception { + + TrendRequest request = new TrendRequest(); + request.setAg("ag"); + + when(vulnerabilityService.getVulnerabilityNewOpenTrend(anyString(), anyString(), anyObject())) + .thenReturn(new ArrayList<>()); + + ResponseEntity responseObj = vulnerabilityController.getVulnerabilityTrend(request); + assertTrue(responseObj.getStatusCode() == HttpStatus.OK); + + request.setFrom(new Date()); + request.setFilter(new HashMap<>()); + + when(vulnerabilityService.getVulnerabilityNewOpenTrend(anyString(), anyString(), anyObject())) + .thenReturn(new ArrayList<>()); + + ResponseEntity response1Obj = vulnerabilityController.getVulnerabilityTrend(request); + assertTrue(response1Obj.getStatusCode() == HttpStatus.OK); + + Map filter = new HashMap<>(); + filter.put("severity", "3"); + request.setFilter(filter); + when(vulnerabilityService.getVulnerabilityNewOpenTrend(anyString(), anyString(), anyObject())) + .thenReturn(new ArrayList<>()); + + ResponseEntity response2Obj = vulnerabilityController.getVulnerabilityTrend(request); + assertTrue(response2Obj.getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulerabilityTrendTest_Failure() throws Exception { + + TrendRequest request = new TrendRequest(); + + ResponseEntity responseObj = vulnerabilityController.getVulnerabilityTrend(request); + assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + request.setAg("ag"); + when(vulnerabilityService.getVulnerabilityNewOpenTrend(anyString(), anyString(), anyObject())) + .thenThrow(new Exception()); + + ResponseEntity response1 = vulnerabilityController.getVulnerabilityTrend(request); + assertTrue(response1.getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void createTrendAnnotationTest() throws Exception { + + when(vulnerabilityService.createTrendAnnotation(anyObject())).thenReturn(true); + assertTrue(vulnerabilityController.createTrendAnnotation(new TrendNote()).getStatusCode() == HttpStatus.OK); + + when(vulnerabilityService.createTrendAnnotation(anyObject())).thenReturn(false); + assertTrue(vulnerabilityController.createTrendAnnotation(new TrendNote()) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getTrendAnnotationsTest() throws Exception { + + when(vulnerabilityService.getTrendAnnotations(anyString(), any(Date.class))).thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getTrendAnnotations("ag", new Date()).getStatusCode() == HttpStatus.OK); + + when(vulnerabilityService.getTrendAnnotations(anyString(), any(Date.class))).thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityController.getTrendAnnotations("ag", null).getStatusCode() == HttpStatus.OK); + + when(vulnerabilityService.getTrendAnnotations(anyString(), any(Date.class))).thenThrow(new DataException()); + assertTrue(vulnerabilityController.getTrendAnnotations("ag", new Date()) + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void deleteTrendAnnotationTest() throws Exception { + + when(vulnerabilityService.deleteTrendAnnotation(anyString())).thenReturn(true); + assertTrue(vulnerabilityController.deleteTrendAnnotation("noteId").getStatusCode() == HttpStatus.OK); + + when(vulnerabilityService.deleteTrendAnnotation(anyString())).thenReturn(false); + assertTrue(vulnerabilityController.deleteTrendAnnotation("noteId") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilityAssetsTrendTest() throws Exception { + + TrendRequest request = new TrendRequest(); + request.setAg("ag"); + + when(vulnerabilityService.getVulnerabilityAssetsTrend(anyString(), anyString(), anyObject())) + .thenReturn(new ArrayList<>()); + + ResponseEntity responseObj = vulnerabilityController.getVulnerabilityAssetsTrend(request); + assertTrue(responseObj.getStatusCode() == HttpStatus.OK); + + request.setFrom(new Date()); + request.setFilter(new HashMap<>()); + + when(vulnerabilityService.getVulnerabilityAssetsTrend(anyString(), anyString(), anyObject())) + .thenReturn(new ArrayList<>()); + + ResponseEntity response1Obj = vulnerabilityController.getVulnerabilityAssetsTrend(request); + assertTrue(response1Obj.getStatusCode() == HttpStatus.OK); + + Map filter = new HashMap<>(); + filter.put("severity", "3"); + request.setFilter(filter); + when(vulnerabilityService.getVulnerabilityAssetsTrend(anyString(), anyString(), anyObject())) + .thenReturn(new ArrayList<>()); + + ResponseEntity response2Obj = vulnerabilityController.getVulnerabilityAssetsTrend(request); + assertTrue(response2Obj.getStatusCode() == HttpStatus.OK); + } + + @Test + public void getVulnerabilityAssetsTrendTest_Failure() throws Exception { + + TrendRequest request = new TrendRequest(); + + ResponseEntity responseObj = vulnerabilityController.getVulnerabilityAssetsTrend(request); + assertTrue(responseObj.getStatusCode() == HttpStatus.EXPECTATION_FAILED); + + request.setAg("ag"); + when(vulnerabilityService.getVulnerabilityAssetsTrend(anyString(), anyString(), anyObject())) + .thenThrow(new DataException()); + + ResponseEntity response1 = vulnerabilityController.getVulnerabilityAssetsTrend(request); + assertTrue(response1.getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @Test + public void getVulnerabilitySummaryByAssetsTest() throws Exception { + + when(vulnerabilityService.getVulnerabilitySummaryByAssets(anyString())).thenReturn(new HashMap<>()); + assertTrue(vulnerabilityController.getVulnerabilitySummaryByAssets("ag").getStatusCode() == HttpStatus.OK); + + when(vulnerabilityService.getVulnerabilitySummaryByAssets(anyString())).thenThrow(new DataException()); + assertTrue(vulnerabilityController.getVulnerabilitySummaryByAssets("ag") + .getStatusCode() == HttpStatus.EXPECTATION_FAILED); + } + + @SuppressWarnings("unchecked") + @Test + public void getVulnerabilitiesOccurrencesTest() throws Exception { + + List> vulnDetails = new ArrayList<>(); + + Request request = new Request(); + request.setAg("ag"); + request.setFrom(0); + Map filter = new HashMap<>(); + filter.put("severity", "3"); + request.setFilter(filter); + + when(vulnerabilityService.vulnerabilityAssetCount(anyString(), anyMap(), anyString(),anyString(),anyInt(), anyInt())).thenReturn(10); + when(vulnerabilityService.getAllVulnerabilitiesDetailsByAssetGroup(anyString(), anyMap(),anyString(),anyString(),anyString(), anyInt(), anyInt())) + .thenReturn(vulnDetails); + + assertTrue(vulnerabilityController.getVulnerabilitiesOccurrences(request) + .getStatusCode() == HttpStatus.OK); + + request.setFilter(new HashMap<>()); + vulnDetails.add(new HashMap<>()); + vulnDetails.add(new HashMap<>()); + + when(vulnerabilityService.vulnerabilityAssetCount(anyString(), anyMap(),anyString(),anyString() ,anyInt(), anyInt())).thenReturn(10); + when(vulnerabilityService.getAllVulnerabilitiesDetailsByAssetGroup(anyString(), anyMap(),anyString(),anyString(),anyString(), anyInt(), anyInt())) + .thenReturn(vulnDetails); + + assertTrue(vulnerabilityController.getVulnerabilitiesOccurrences(request) + .getStatusCode() == HttpStatus.OK); + + request.setSize(0); + when(vulnerabilityService.vulnerabilityAssetCount(anyString(), anyMap(),anyString(),anyString(), anyInt(), anyInt())).thenReturn(10); + when(vulnerabilityService.getAllVulnerabilitiesDetailsByAssetGroup(anyString(), anyMap(),anyString(),anyString(),anyString(), anyInt(), anyInt())) + .thenReturn(vulnDetails); + + assertTrue(vulnerabilityController.getVulnerabilitiesOccurrences(request) + .getStatusCode() == HttpStatus.OK); + + request.setSize(3); + when(vulnerabilityService.vulnerabilityAssetCount(anyString(), anyMap(),anyString(),anyString(), anyInt(), anyInt())).thenReturn(10); + when(vulnerabilityService.getAllVulnerabilitiesDetailsByAssetGroup(anyString(), anyMap(), anyString(),anyString(),anyString(),anyInt(), anyInt())) + .thenReturn(vulnDetails); + + assertTrue(vulnerabilityController.getVulnerabilitiesOccurrences(request) + .getStatusCode() == HttpStatus.OK); + } +} diff --git a/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityRepositoryTest.java b/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityRepositoryTest.java new file mode 100644 index 000000000..5fd327d78 --- /dev/null +++ b/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/repository/VulnerabilityRepositoryTest.java @@ -0,0 +1,814 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.repository; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.repo.ElasticSearchRepository; +import com.tmobile.pacman.api.commons.repo.PacmanRdsRepository; +import com.tmobile.pacman.api.commons.utils.PacHttpUtils; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ PacHttpUtils.class }) +public class VulnerabilityRepositoryTest { + + @InjectMocks + private VulnerabilityRepository vulnerabilityRepository; + + @Mock + private ElasticSearchRepository elasticSearchRepository; + + @Mock + private PacmanRdsRepository rdsRepository; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void getAllVulnerabilitiesTest() throws Exception { + + String response = "{\"hits\":{\"total\":68,\"hits\":[{\"_index\":\"qualys-kb\",\"_type\":\"kb\",\"_id\":\"2\",\"_score\":8.899231," + + "\"_source\":{\"qid\":\"2\",\"vulntype\":\"Vulnerability\",\"severitylevel\":4,\"title\":\"Red Hat Update for kernel\"," + + "\"category\":\"RedHat\",\"lastservicemodificationdatetime\":\"2018-05-29T20:32:16z\",\"publisheddatetime\":\"2018-01-04T04:02:43z\"," + + "\"_loadDate\":\"2018-07-09T14:23:27z\",\"latest\":true,\"classification\":\"OS\"}}]}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + when(elasticSearchRepository.processResponseAndSendTheScrollBack(anyString(), anyObject())) + .thenCallRealMethod(); + + List> vulnerabilities = vulnerabilityRepository.getAllVulnerabilities(new ArrayList<>()); + assertTrue(vulnerabilities.size() == 1); + } + + @Test + public void getAllVulnerabilitiesTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getAllVulnerabilities(new ArrayList<>())) + .isInstanceOf(DataException.class); + } + + @Test + public void getAssetsAffectedCountTest() throws Exception { + + String response = "{\"aggregations\":{\"qid\":{\"buckets\":[{\"key\":105130,\"doc_count\":871}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + Map filter = new HashMap<>(); + filter.put("tags.Application.keyword", "app"); + filter.put(Constants.SEVEITY_LEVEL, "3"); + + Map assetsAffected = vulnerabilityRepository.getAssetsAffectedCount("ag", filter, "parent"); + assertTrue(assetsAffected.size() == 1); + } + + @Test + public void getAssetsAffectedCountTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + Map assetsAffected = vulnerabilityRepository.getAssetsAffectedCount("ag", null, "parent"); + assertTrue(assetsAffected.size() == 0); + } + + @Test + public void getVulnerabilyAcrossAppAndEnvTest() throws Exception { + + String response = "{\"hits\":{\"total\":905},\"aggregations\":{\"apps\":{\"buckets\":[{\"key\":\"ag\",\"doc_count\":905," + + "\"vulns\":{\"doc_count\":5522,\"NAME\":{\"buckets\":{\"S3\":{\"doc_count\":556},\"S4\":{\"doc_count\":469},\"S5\":{\"doc_count\":86}}}}}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository + .getVulnerabilyAcrossAppAndEnv("ag", "tags.Application.keyword", "", "parent", "3").size() == 1); + assertTrue(vulnerabilityRepository + .getVulnerabilyAcrossAppAndEnv("ag", "tags.Environment.keyword", "app", "parent", "").size() == 1); + } + + @Test + public void getVulnerabilyAcrossAppAndEnvTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv("ag", "tags.Application.keyword", + "", "parent", "3")).isInstanceOf(Exception.class); + } + + @Test + public void getVulnerabilityTrendTest() throws Exception { + + String response = "{\"aggregations\":{\"date\":{\"buckets\":[{\"key_as_string\":\"2018-07-25\",\"key\":1532476800000," + + "\"doc_count\":51,\"vulns\":{\"value\":11126}}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + Map filter = new HashMap<>(); + filter.put("tags.Application.keyword", "app"); + filter.put("tags.Environment.keyword", "env"); + assertTrue(vulnerabilityRepository.getVulnerabilityTrend("ag", filter, new Date(), new Date()).size() == 1); + } + + @Test + public void getVulnerabilityTrendTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getVulnerabilityTrend("ag", null, null, null)) + .isInstanceOf(Exception.class); + + Map filter = new HashMap<>(); + filter.put("test", "test"); + assertThatThrownBy(() -> vulnerabilityRepository.getVulnerabilityTrend("ag", filter, new Date(), null)) + .isInstanceOf(Exception.class); + } + + @Test + public void getVulnerabilitiesDistributionTest() throws Exception { + + String response = "{\"aggregations\":{\"apps\":{\"doc_count_error_upper_bound\":0,\"sum_other_doc_count\":0," + + "\"buckets\":[{\"key\":\"ag\",\"doc_count\":90,\"envs\":{\"doc_count_error_upper_bound\":0,\"sum_other_doc_count\":0," + + "\"buckets\":[{\"key\":\"Production::prd\",\"doc_count\":32,\"vulns\":{\"doc_count\":2002," + + "\"NAME\":{\"buckets\":{\"S3\":{\"doc_count\":197},\"S4\":{\"doc_count\":164},\"S5\":{\"doc_count\":30}}}}}]}}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getVulnerabilitiesDistribution("ag", "parent").size() == 1); + } + + @Test + public void getVulnerabilitiesDistributionTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getVulnerabilitiesDistribution("ag", "parent")) + .isInstanceOf(Exception.class); + } + + @Test + public void getVulnerabilitysummaryByResourceIdTest() throws Exception { + + String response = "{\"hits\":{\"total\":518},\"aggregations\":{\"NAME\":{\"buckets\":{\"S3\":{\"doc_count\":556},\"S4\":{\"doc_count\":469},\"S5\":{\"doc_count\":86}}}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + assertTrue(vulnerabilityRepository.getVulnerabilitysummaryByResourceId("resource").size() == 2); + } + + @Test + public void getVulnerabilitysummaryByResourceIdTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + assertTrue(vulnerabilityRepository.getVulnerabilitysummaryByResourceId("resource").size() == 0); + } + + @Test + public void fetchExecDirectorAppsTest() throws Exception { + + when(elasticSearchRepository.getDataFromES(anyString(), anyString(), anyObject(), anyObject(), anyObject(), + anyObject(), anyObject())).thenReturn(new ArrayList<>()); + assertThat(vulnerabilityRepository.fetchExecDirectorApps(), is(notNullValue())); + } + + @Test + public void getUniqueHostTest() throws Exception { + + String response = "{\"hits\":{\"total\":30056},\"aggregations\":{\"vulninfo\":{\"sev-filter\":{\"severity\":{\"buckets\":[" + + "{\"key\":4,\"unique-host\":{\"value\":25071}},{\"key\":3,\"unique-host\":{\"value\":23776}}," + + "{\"key\":5,\"unique-host\":{\"value\":19250}}]}}}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getUniqueHost("ag", "3,4,5").size() == 4); + } + + @Test + public void getUniqueHostTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getUniqueHost("ag", "3,4,5").size() == 0); + } + + @Test + public void getUniqueVulnTest() throws Exception { + + String response = "{\"aggregations\":{\"vulninfo\":{\"sev-filter\":{\"doc_count\":668732,\"severity\":{\"buckets\":[" + + "{\"key\":4,\"doc_count\":354352,\"unique-qid\":{\"value\":1377}},{\"key\":3,\"doc_count\":203380,\"unique-qid\":{\"value\":1868}}," + + "{\"key\":5,\"doc_count\":111000,\"unique-qid\":{\"value\":555}}]}}}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getVulnInfo("ag", "3,4,5").size() == 4); + } + + @Test + public void getUniqueVulnTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getVulnInfo("ag", "3,4,5").size() == 0); + } + + @Test + public void getUniqueAppTest() throws Exception { + + String response = "{\"aggregations\":{\"severity\":{\"buckets\":{\"S3\":{\"doc_count\":871,\"NAME\":{\"value\":1}}," + + "\"S4\":{\"doc_count\":871,\"NAME\":{\"value\":1}},\"S5\":{\"doc_count\":844,\"NAME\":{\"value\":1}}}}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getUniqueApp("ag").size() == 3); + } + + @Test + public void getUniqueAppTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + assertTrue(vulnerabilityRepository.getUniqueApp("ag").size() == 0); + } + + @Test + public void getAgingSummaryTest() throws Exception { + + String response = "{\"aggregations\":{\"severity\":{\"buckets\":[{\"key\":3,\"doc_count\":5581,\"aging\":{\"value\":53.36391327719047}}," + + "{\"key\":4,\"doc_count\":4704,\"aging\":{\"value\":41.863945578231295}},{\"key\":5,\"doc_count\":865,\"aging\":{\"value\":38.522543352601154}}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getAgingSummary("ag").size() == 3); + } + + @Test + public void getAgingSummaryTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + assertTrue(vulnerabilityRepository.getAgingSummary("ag").size() == 0); + } + + @Test + public void getAgingByApplicationTest() throws Exception { + + String response = "{\"aggregations\":{\"apps\":{\"buckets\":[{\"key\":\"ag\",\"doc_count\":905,\"vulns\":{\"doc_count\":55225," + + "\"NAME\":{\"buckets\":{\"S3\":{\"doc_count\":5569,\"aging\":{\"value\":297099}},\"S4\":{\"doc_count\":4694,\"aging\":" + + "{\"value\":196471}},\"S5\":{\"doc_count\":863,\"aging\":{\"value\":33216}}}}}}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getAgingByApplication("ag", "parent", "").size() == 1); + assertTrue(vulnerabilityRepository.getAgingByApplication("ag", "parent", "3").size() == 1); + + response = "{\"aggregations\":{\"apps\":{\"buckets\":[{\"key\":\"ag\",\"doc_count\":905,\"vulns\":{\"doc_count\":55225," + + "\"NAME\":{\"buckets\":{\"S3\":{\"doc_count\":0,\"aging\":{\"value\":297099}},\"S4\":{\"doc_count\":0,\"aging\":" + + "{\"value\":196471}},\"S5\":{\"doc_count\":0,\"aging\":{\"value\":33216}}}}}}]}}}"; + assertTrue(vulnerabilityRepository.getAgingByApplication("ag", "parent", "").size() == 1); + assertTrue(vulnerabilityRepository.getAgingByApplication("ag", "parent", "3").size() == 1); + } + + @Test + public void getAgingByApplicationTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + assertThatThrownBy(() -> vulnerabilityRepository.getAgingByApplication("ag", "parent", "3")) + .isInstanceOf(Exception.class); + } + + @Test + public void getTotalQualysHostCountTest() throws Exception { + + String response = "{\"count\":3}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getTotalQualysHostCount("ag", "parent") == 3); + } + + @Test + public void getTotalQualysHostCountTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getTotalQualysHostCount("ag", "parent")) + .isInstanceOf(DataException.class); + } + + @Test + public void getVulnerabilityByQidTest() throws Exception { + + String response = "{\"hits\":{\"total\":68,\"hits\":[{\"_index\":\"qualys-kb\",\"_type\":\"kb\",\"_id\":\"236591\",\"_score\":8.899231," + + "\"_source\":{\"qid\":\"236591\",\"vulntype\":\"Vulnerability\",\"severitylevel\":4,\"title\":\"Red Hat Update for kernel\"," + + "\"category\":\"RedHat\",\"lastservicemodificationdatetime\":\"2018-05-29T20:32:16z\",\"publisheddatetime\":\"2018-01-04T04:02:43z\"," + + "\"_loadDate\":\"2018-07-09T14:23:27z\",\"latest\":true,\"classification\":\"OS\"}}]}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThat(vulnerabilityRepository.getVulnerabilityByQid("qid"), is(notNullValue())); + } + + @Test + public void getVulnerabilityByQidTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getVulnerabilityByQid("ag").size() == 0); + } + + @Test + public void getDistributionSummaryByInfraTypeTest() throws Exception { + + String response = "{\"hits\":{\"total\":515,\"max_score\":0,\"hits\":[]},\"aggregations\":{\"NAME\":{\"doc_count\":32252," + + "\"NAME\":{\"doc_count\":6361,\"NAME\":{\"value\":37}}}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getDistributionSummaryByInfraType("ag", "", "parent").size() == 3); + } + + @Test + public void getDistributionSummaryByInfraTypeTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getDistributionSummaryByInfraType("ag", "3", "parent")) + .isInstanceOf(DataException.class); + } + + @Test + public void getProdInfoByEnvTest() throws Exception { + + String response = "{\"hits\":{\"total\":515,\"max_score\":0,\"hits\":[]},\"aggregations\":{\"NAME\":{\"doc_count\":32252," + + "\"NAME\":{\"doc_count\":6361,\"NAME\":{\"value\":37}}}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getProdInfoByEnv("ag", "").size() == 3); + } + + @Test + public void getProdInfoByEnvTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getProdInfoByEnv("ag", "3").size() == 0); + } + + @Test + public void getNonProdInfoByEnvTest() throws Exception { + + String response = "{\"hits\":{\"total\":515,\"max_score\":0,\"hits\":[]},\"aggregations\":{\"NAME\":{\"doc_count\":32252," + + "\"NAME\":{\"doc_count\":6361,\"NAME\":{\"value\":37}}}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getNonProdInfoByEnv("ag", "").size() == 3); + } + + @Test + public void getNonProdInfoByEnvTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getNonProdInfoByEnv("ag", "3").size() == 0); + } + + @Test + public void getDistributionSummaryByVulnTypeTest() throws Exception { + + String response1 = "{\"aggregations\":{\"vulninfo\":{\"doc_count\":126339,\"sev-filter\":" + + "{\"doc_count\":104821,\"classification\":{\"buckets\":[{\"key\":\"OS\",\"doc_count\":94207,\"resources\":{\"value\":4219}}," + + "{\"key\":\"Application\",\"doc_count\":10614,\"resources\":{\"value\":1869}}]}}}}}"; + + String response2 = "{\"aggregations\":{\"vulninfo\":{\"doc_count\":126339,\"sev-filter\":" + + "{\"doc_count\":104821,\"classification\":{\"buckets\":[{\"key\":\"OS\",\"doc_count\":94207,\"unique-qid\":{\"value\":1156}}," + + "{\"key\":\"Application\",\"doc_count\":10614,\"unique-qid\":{\"value\":428}}]}}}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response1, response2); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getDistributionSummaryByVulnType("ag", "").size() == 2); + } + + @Test + public void getDistributionSummaryByVulnTypeTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getDistributionSummaryByVulnType("ag", "3")) + .isInstanceOf(DataException.class); + } + + @Test + public void getAllQidByAGTest() throws Exception { + + String response = "{\"aggregations\":{\"qid\":{\"buckets\":[{\"key\":\"105130~unix group list~OS\",\"doc_count\":873}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getAllQidByAG("ag", "").size() == 1); + } + + @Test + public void getAllQidByAGTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getAllQidByAG("ag", "3")).isInstanceOf(DataException.class); + } + + @Test + public void getAppsBySeverityTest() throws Exception { + + String response = "{\"aggregations\":{\"apps\":{\"buckets\":[{\"key\":\"ag\",\"doc_count\":905," + + "\"vulns\":{\"doc_count\":55283,\"NAME\":{\"buckets\":{\"severity\":{\"doc_count\":12242}}}}}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getAppsBySeverity("ag", "parent", "").size() == 1); + } + + @Test + public void getAppsBySeverityTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getAppsBySeverity("ag", "parent", "3")) + .isInstanceOf(Exception.class); + } + + @Test + public void getTrendAnnotationsTest() throws Exception { + + String response = "{\"hits\":{\"total\":68,\"hits\":[{\"_index\":\"assetgroup_annotations\",\"_type\":\"annotations\",\"_id\":\"20180722\"," + + "\"_source\":{\"date\":\"2018-07-22\",\"note\":\"Test global\",\"ag\":\"\",\"noteId\":\"20180722\"}}]}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getTrendAnnotations("ag", new Date()).size() == 1); + } + + @Test + public void getTrendAnnotationsTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getTrendAnnotations("ag", new Date()).size() == 0); + } + + @Test + public void getDataFromPacmanRDSTest() throws Exception { + + when(rdsRepository.getDataFromPacman(anyString())).thenReturn(new ArrayList<>()); + assertTrue(vulnerabilityRepository.getDataFromPacmanRDS("query").size() == 0); + } + + @Test + public void getRunningInstancesCountTest() throws Exception { + + String response = "{\"count\":3}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getRunningInstancesCount("ag", "ec2") == 3); + assertTrue(vulnerabilityRepository.getRunningInstancesCount("ag", "onprem") == 3); + + } + + @Test + public void getRunningInstancesCountTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getRunningInstancesCount("ag", "ec2")) + .isInstanceOf(DataException.class); + } + + @Test + public void getExemptedByRuleCountTest() throws Exception { + + String response = "{\"count\":3}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getExemptedByRuleCount("ag", "ec2") == 3); + assertTrue(vulnerabilityRepository.getExemptedByRuleCount("ag", "onprem") == 3); + + } + + @Test + public void getExemptedByRuleCountTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getExemptedByRuleCount("ag", "ec2")) + .isInstanceOf(DataException.class); + } + + @Test + public void getUniqueHostBySeverityTest() throws Exception { + + String response = "{\"aggregations\":{\"severity\":{\"buckets\":[{\"key\":4,\"doc_count\":320}," + + "{\"key\":3,\"doc_count\":294},{\"key\":5,\"doc_count\":71}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getUniqueHostBySeverity("ag", "3").size() == 3); + + } + + @Test + public void getUniqueHostBySeverityTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getUniqueHostBySeverity("ag", "3")) + .isInstanceOf(DataException.class); + } + + @Test + public void getCompliantHostsBySeverityTest() throws Exception { + + String response1 = "{\"aggregations\":{\"severity\":{\"buckets\":[{\"key\":4,\"doc_count\":320}," + + "{\"key\":3,\"doc_count\":294},{\"key\":5,\"doc_count\":71}]}}}"; + String response2 = "{\"aggregations\":{\"resourceid\":{\"buckets\":[{\"key\":\"123\"},{\"key\":\"456\"}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response2, response1); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertTrue(vulnerabilityRepository.getCompliantHostsBySeverity("ag").size() == 3); + + } + + @Test + public void getCompliantHostsBySeverityTest_Exception() throws Exception { + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenThrow(new Exception()); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + + assertThatThrownBy(() -> vulnerabilityRepository.getCompliantHostsBySeverity("ag")) + .isInstanceOf(DataException.class); + } + + @SuppressWarnings("deprecation") + @Test + public void fetchOrgInfoForAppsTest() throws Exception { + + when(elasticSearchRepository.getDataFromES(anyString(), anyString(), anyObject(), anyObject(), anyObject(), + anyObject(), anyObject())).thenReturn(new ArrayList<>()); + assertThat(vulnerabilityRepository.fetchOrgInfoForApps().size(), is(0)); + } + + @Test + public void vulnerabilityAssetsCountTest() throws Exception { + List> results = new ArrayList>(); + Map map = new HashMap(); + Map map1 = new HashMap(); + map.put("ac", "66"); + map.put("acname", "second"); + map.put("res", "int"); + map.put("vp", "brt3"); + map.put("geid", "amigo"); + map1.put("rr", "345"); + map1.put("fd", "sec"); + map1.put("ij", "i"); + map1.put("cid", "b3"); + map1.put("mad", "mi"); + results.add(map); + results.add(map1); + + String assetGroup = "abc"; + Map mustFilter = new HashMap<>(); + Map mustTermsFilter = new HashMap<>(); + mustFilter.put("latest", "true"); + String targetType = "vulninfo"; + int from = 0; + int size = 0; + + String response1 = "{\"aggregations\":{\"severity\":{\"buckets\":[{\"key\":4,\"doc_count\":320}," + + "{\"key\":3,\"doc_count\":294},{\"key\":5,\"doc_count\":71}]}}}"; + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response1); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + when(elasticSearchRepository.getSortedDataFromES(anyString(), anyString(), anyObject(), anyObject(), + anyObject(), anyObject(), anyObject(), anyObject())).thenReturn(results); + assertThat(vulnerabilityRepository.vulnerabilityAssetsCount(assetGroup, targetType, mustFilter, from, size,mustTermsFilter), + is(2)); + } + + @Test + public void getAllVulnerabilitiesByAssetGroupTest() throws Exception { + List> results = new ArrayList>(); + Map map = new HashMap(); + Map map1 = new HashMap(); + map.put("ac", "66"); + map.put("acname", "second"); + map.put("res", "int"); + map.put("vp", "brt3"); + map.put("geid", "amigo"); + map1.put("rr", "345"); + map1.put("fd", "sec"); + map1.put("ij", "i"); + map1.put("cid", "b3"); + map1.put("mad", "mi"); + results.add(map); + results.add(map1); + + List occurrenceFieldList = new ArrayList(); + String assetGroup = "abc"; + Map mustFilter = new HashMap<>(); + Map mustTermsFilter = new HashMap<>(); + mustFilter.put("latest", "true"); + String targetType = "vulninfo"; + int from = 0; + int size = 5; + String response1 = "{\"aggregations\":{\"severity\":{\"buckets\":[{\"key\":4,\"doc_count\":320}," + + "{\"key\":3,\"doc_count\":294},{\"key\":5,\"doc_count\":71}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response1); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + when(elasticSearchRepository.getSortedDataFromESBySize(anyString(), anyString(), anyObject(), anyObject(), + anyObject(), anyObject(), anyInt(), anyInt(), anyObject(), anyObject(), anyObject())) + .thenReturn(results); + assertThat(vulnerabilityRepository + .getAllVulnerabilitiesByAssetGroup(assetGroup, targetType, mustFilter, occurrenceFieldList, from, size,mustTermsFilter) + .size(), is(2)); + size = 0; + + when(elasticSearchRepository.getSortedDataFromES(anyString(), anyString(), anyObject(), anyObject(), + anyObject(), anyObject(), anyObject(), anyObject())).thenReturn(results); + assertThat(vulnerabilityRepository + .getAllVulnerabilitiesByAssetGroup(assetGroup, targetType, mustFilter, occurrenceFieldList, from, size,mustTermsFilter) + .size(), is(2)); + } + + @Test + public void getDetailsByResourceIdTest() throws Exception { + List> results = new ArrayList>(); + Map map = new HashMap(); + Map map1 = new HashMap(); + map.put("ac", "66"); + map.put("acname", "second"); + map.put("res", "int"); + map.put("vp", "brt3"); + map.put("geid", "amigo"); + map1.put("rr", "345"); + map1.put("fd", "sec"); + map1.put("ij", "i"); + map1.put("cid", "b3"); + map1.put("mad", "mi"); + results.add(map); + results.add(map1); + + List occurrenceFieldList = new ArrayList(); + String assetGroup = "abc"; + Map mustFilter = new HashMap<>(); + mustFilter.put("latest", "true"); + String response1 = "{\"aggregations\":{\"severity\":{\"buckets\":[{\"key\":4,\"doc_count\":320}," + + "{\"key\":3,\"doc_count\":294},{\"key\":5,\"doc_count\":71}]}}}"; + + mockStatic(PacHttpUtils.class); + when(PacHttpUtils.doHttpPost(anyString(), anyString())).thenReturn(response1); + ReflectionTestUtils.setField(vulnerabilityRepository, "esUrl", "dummyEsURL"); + when(elasticSearchRepository.getSortedDataFromES(anyString(), anyString(), anyObject(), anyObject(), + anyObject(), anyObject(), anyObject(), anyObject())).thenReturn(results); + assertThat(vulnerabilityRepository + .getDetailsByResourceId(assetGroup, mustFilter, occurrenceFieldList, mustFilter).size(), is(2)); + + } + +} diff --git a/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/service/VulnerabilityServiceTest.java b/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/service/VulnerabilityServiceTest.java new file mode 100644 index 000000000..cf45e43ad --- /dev/null +++ b/api/pacman-api-vulnerability/src/test/java/com/tmobile/pacman/api/vulnerability/service/VulnerabilityServiceTest.java @@ -0,0 +1,1022 @@ +/******************************************************************************* + * Copyright 2018 T Mobile, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +package com.tmobile.pacman.api.vulnerability.service; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.powermock.api.mockito.PowerMockito.when; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.http.ResponseEntity; +import org.springframework.test.util.ReflectionTestUtils; + +import com.tmobile.pacman.api.commons.Constants; +import com.tmobile.pacman.api.commons.exception.DataException; +import com.tmobile.pacman.api.commons.exception.ServiceException; +import com.tmobile.pacman.api.commons.utils.CommonUtils; +import com.tmobile.pacman.api.commons.utils.ResponseUtils; +import com.tmobile.pacman.api.vulnerability.client.AssetServiceClient; +import com.tmobile.pacman.api.vulnerability.client.ComplianceServiceClient; +import com.tmobile.pacman.api.vulnerability.domain.AssetApi; +import com.tmobile.pacman.api.vulnerability.domain.AssetApiData; +import com.tmobile.pacman.api.vulnerability.domain.AssetCount; +import com.tmobile.pacman.api.vulnerability.domain.AssetCountByAppEnvDTO; +import com.tmobile.pacman.api.vulnerability.domain.AssetCountDTO; +import com.tmobile.pacman.api.vulnerability.domain.AssetCountData; +import com.tmobile.pacman.api.vulnerability.domain.Request; +import com.tmobile.pacman.api.vulnerability.domain.ResponseWithOrder; +import com.tmobile.pacman.api.vulnerability.domain.TrendNote; +import com.tmobile.pacman.api.vulnerability.repository.VulnerabilityRepository; +import com.tmobile.pacman.api.vulnerability.repository.VulnerabilityTrendGenerator; + +@RunWith(PowerMockRunner.class) +public class VulnerabilityServiceTest { + + @InjectMocks + VulnerabilityService vulnerabilityService; + + @Mock + VulnerabilityRepository vulnerabilityRepository; + + @Mock + AssetServiceClient assetServiceClient; + + @Mock + VulnerabilityTrendGenerator vulnTrendGenerator; + + @Mock + ComplianceServiceClient complianceServiceClient; + + @Mock + CommonUtils commonUtils; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + AssetApi assetApi = new AssetApi(); + AssetApiData data = new AssetApiData(); + + List ttypes = new ArrayList<>(); + AssetCountDTO tt = new AssetCountDTO(); + tt.setType("ec2"); + ttypes.add(tt); + tt = new AssetCountDTO(); + tt.setType("onpremserver"); + ttypes.add(tt); + + data.setTargettypes(ttypes.toArray(new AssetCountDTO[ttypes.size()])); + assetApi.setData(data); + + when(assetServiceClient.getTargetTypeList(anyString(), anyObject())).thenReturn(assetApi); + } + + @Test + public void getVulnerabilitiesDetailsTest() throws Exception { + + List> vulnerabilitiesData = new ArrayList<>(); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getAssetsAffectedCount(anyString(), anyObject(), anyString())) + .thenReturn(new HashMap<>()); + when(vulnerabilityRepository.getAllVulnerabilities(anyObject())).thenReturn(vulnerabilitiesData); + assertThat(vulnerabilityService.getVulnerabilitiesDetails("ag", null), is(notNullValue())); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); + assertThat(vulnerabilityService.getVulnerabilitiesDetails("ag", null), is(notNullValue())); + + Map vuln = new HashMap<>(); + vuln.put("qid", "123"); + vuln.put("assetsAffected", 1); + vuln.put(Constants.TITLE, "test"); + vuln.put(Constants.SEVEITY_LEVEL, "3"); + vuln.put(Constants.CATEGORY, "test"); + vuln.put(Constants.VULN_TYPE, "type"); + vuln.put(Constants.PATCHABLE, "1"); + vulnerabilitiesData.add(vuln); + + vuln = new HashMap<>(); + vuln.put("qid", "456"); + vuln.put("assetsAffected", 2); + vuln.put(Constants.TITLE, "test"); + vuln.put(Constants.SEVEITY_LEVEL, "5"); + vuln.put(Constants.CATEGORY, "test"); + vuln.put(Constants.VULN_TYPE, "type"); + vuln.put(Constants.PATCHABLE, "0"); + vulnerabilitiesData.add(vuln); + + vuln = new HashMap<>(); + vuln.put("qid", "789"); + vuln.put("assetsAffected", 2); + vuln.put(Constants.TITLE, "test"); + vuln.put(Constants.SEVEITY_LEVEL, "5"); + vuln.put(Constants.CATEGORY, "test"); + vuln.put(Constants.VULN_TYPE, "type"); + vulnerabilitiesData.add(vuln); + + Map assetsAffected = new HashMap(); + assetsAffected.put("123", 10L); + assetsAffected.put("456", 10L); + assetsAffected.put("789", 10L); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); + when(vulnerabilityRepository.getAssetsAffectedCount(anyString(), anyObject(), anyString())) + .thenReturn(assetsAffected); + when(vulnerabilityRepository.getAllVulnerabilities(anyObject())).thenReturn(vulnerabilitiesData); + assertThat(vulnerabilityService.getVulnerabilitiesDetails("ag", null), is(notNullValue())); + } + + @Test + public void getVulnerabilitiesDetailsTest_Exception() throws Exception { + + when(vulnerabilityRepository.getAssetsAffectedCount(anyString(), anyObject(), anyString())) + .thenReturn(new HashMap<>()); + when(vulnerabilityRepository.getAllVulnerabilities(anyObject())).thenThrow(new DataException()); + assertThatThrownBy(() -> vulnerabilityService.getVulnerabilitiesDetails("ag", null)) + .isInstanceOf(Exception.class); + } + + @Test + public void getVulnerabilitySummaryTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); + assertThat(vulnerabilityService.getVulnerabilitySummary("ag", "3,4,5"), is(notNullValue())); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getUniqueHost(anyString(), anyString())).thenReturn(new HashMap()); + when(vulnerabilityRepository.getVulnInfo(anyString(), anyString())).thenReturn(new HashMap()); + when(vulnerabilityRepository.getUniqueApp(anyString())).thenReturn(new HashMap()); + + AssetCount totalAssets = new AssetCount(); + AssetCountData data = new AssetCountData(); + + AssetCountByAppEnvDTO assetCount_Count = new AssetCountByAppEnvDTO(); + assetCount_Count.setType("onpremserver"); + assetCount_Count.setCount("1"); + + List assetAppEnvDTOs = new ArrayList(); + assetAppEnvDTOs.add(assetCount_Count); + data.setAssetcount(assetAppEnvDTOs.toArray(new AssetCountByAppEnvDTO[assetAppEnvDTOs.size()])); + totalAssets.setData(data); + + List> response = new ArrayList<>(); + LinkedHashMap obj = new LinkedHashMap<>(); + obj.put("assetsScanned", 5); + obj.put("failed", 2); + obj.put("passed", 2); + response.add(obj); + Map responseMap = new HashMap<>(); + responseMap.put("response", response); + ResponseEntity nonCompliancePolicyRuleresponse = ResponseUtils.buildSucessResponse(responseMap); + + when(complianceServiceClient.getNonCompliancePolicyByRule(any(Request.class))) + .thenReturn(nonCompliancePolicyRuleresponse); + when(vulnerabilityRepository.getTotalQualysHostCount(anyString(), anyString())).thenReturn(1L); + ReflectionTestUtils.setField(vulnerabilityService, "vulnSummarySeverity", "3"); + + Map vulnSummary = new HashMap<>(); + List> severityInfo = new ArrayList<>(); + Map severity = new HashMap<>(); + severity.put(Constants.SEVEITY_LEVEL, 3); + severity.put(Constants.COUNT, 2); + severity.put(Constants.VULN_COUNT, 2); + severityInfo.add(severity); + severity = new HashMap<>(); + severity.put(Constants.SEVEITY_LEVEL, 4); + severity.put(Constants.COUNT, 2); + severity.put(Constants.VULN_COUNT, 2); + severityInfo.add(severity); + severity = new HashMap<>(); + severity.put(Constants.SEVEITY_LEVEL, 5); + severity.put(Constants.COUNT, 2); + severity.put(Constants.VULN_COUNT, 2); + severityInfo.add(severity); + + vulnSummary.put("severityInfo", severityInfo); + assertThat(vulnerabilityService.getVulnerabilitySummary("ag", "3,4,5"), is(notNullValue())); + + Map uniqueHost = new HashMap<>(); + uniqueHost.put("total", 10); + uniqueHost.put("3", 1); + Map vulnInfo = new HashMap<>(); + Map vulnInfoMap = new HashMap<>(); + vulnInfoMap.put(Constants.VULN_COUNT, 2); + vulnInfoMap.put(Constants.UNIQUE_VULN_COUNT, 2); + vulnInfo.put("total", 10); + vulnInfo.put("3", vulnInfoMap); + Map uniqueApp = new HashMap<>(); + uniqueApp.put("3", 1); + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); + when(vulnerabilityRepository.getUniqueHost(anyString(), anyString())).thenReturn(uniqueHost); + when(vulnerabilityRepository.getVulnInfo(anyString(), anyString())).thenReturn(vulnInfo); + when(vulnerabilityRepository.getUniqueApp(anyString())).thenReturn(uniqueApp); + + when(complianceServiceClient.getNonCompliancePolicyByRule(any(Request.class))) + .thenReturn(nonCompliancePolicyRuleresponse); + // when(complianceService.getRulecompliance(any(Request.class))).thenReturn(responseWithOrder); + when(assetServiceClient.getTotalAssetsCount(anyString(), anyString(), anyString())).thenReturn(totalAssets); + when(vulnerabilityRepository.getTotalQualysHostCount(anyString(), anyString())).thenReturn(1L); + ReflectionTestUtils.setField(vulnerabilityService, "vulnSummarySeverity", "3"); + + vulnSummary.put("severityInfo", severityInfo); + + assertThat(vulnerabilityService.getVulnerabilitySummary("ag", "3"), is(notNullValue())); + + } + + /** + * commenting this test case as we moved this method to the compliance service. + * + * @throws Exception + */ + @Test + public void getVulnerabilitySummaryTest_Exception() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getUniqueHost(anyString(), anyString())).thenReturn(new HashMap()); + when(vulnerabilityRepository.getVulnInfo(anyString(), anyString())).thenReturn(new HashMap()); + when(vulnerabilityRepository.getUniqueApp(anyString())).thenReturn(new HashMap()); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnSummarySeverity", "3"); + + ResponseWithOrder responseWithOrder = new ResponseWithOrder(); + List> response = new ArrayList<>(); + LinkedHashMap obj = new LinkedHashMap<>(); + obj.put("assetsScanned", 1); + obj.put("passed", 1); + response.add(obj); + responseWithOrder.setResponse(response); + + Map responseTest = new HashMap(); + responseTest.put(Constants.DATA_KEY, responseWithOrder); + responseTest.put(Constants.STATUS_KEY, Constants.STATUS_SUCCESS); + ResponseEntity nonCompliancePolicyRuleresponse = ResponseEntity.ok().body((Object) responseTest); + + // when(complianceServiceClient.getNonCompliancePolicyByRule(any(Request.class))).thenReturn(nonCompliancePolicyRuleresponse); + // when(complianceServiceClient.getNonCompliancePolicyByRule(any(Request.class))).thenThrow(new + // ServiceException()); + // assertThatThrownBy( + // () -> + // vulnerabilityService.getVulnerabilitySummary("ag","3,4,5")).isInstanceOf(ServiceException.class); + } + + @Test + public void getVulnerabilityByAppAndEnvTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(anyString(), anyObject(), anyString(), anyString(), + anyString())).thenReturn(new ArrayList<>()); + assertThat(vulnerabilityService.getVulnerabilityByAppAndEnv("ag", "filter", "app"), is(notNullValue())); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); + assertThat(vulnerabilityService.getVulnerabilityByAppAndEnv("ag", "filter", "app").size(), is(0)); + } + + @Test + public void getVulnerabilityTrendTest() throws Exception { + + when(vulnerabilityRepository.getVulnerabilityTrend(anyString(), anyObject(), anyObject(), anyObject())) + .thenReturn(new ArrayList<>()); + assertThat(vulnerabilityService.getVulnerabilityTrend("ag", null, new Date(), new Date()), is(notNullValue())); + } + + @Test + public void getVulnerabilityNewOpenTrendTest() throws Exception { + + when(vulnTrendGenerator.generateTrend(anyString(), anyString(), anyObject())).thenReturn(new ArrayList<>()); + assertThat(vulnerabilityService.getVulnerabilityNewOpenTrend("ag", "sev", new Date()), is(notNullValue())); + } + + @Test + public void getVulnerabilitiesDistributionTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getVulnerabilitiesDistribution(anyString(), anyString())) + .thenReturn(new ArrayList<>()); + assertThat(vulnerabilityService.getVulnerabilitiesDistribution("ag"), is(notNullValue())); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); + assertThat(vulnerabilityService.getVulnerabilitiesDistribution("ag").size(), is(0)); + } + + /* + * @SuppressWarnings("static-access") + * + * @Test public void filterMatchingCollectionElementsTest() throws Exception { + * + * when(commonUtils.filterMatchingCollectionElements(anyObject(), anyObject(), + * anyObject())).thenReturn(new Object()); + * assertThat(vulnerabilityService.filterMatchingCollectionElements(new + * ArrayList<>(),"sev",true), is(notNullValue())); } + */ + + @Test + public void getVulnerabilitysummaryByResourceIdTest() throws Exception { + + when(vulnerabilityRepository.getVulnerabilitysummaryByResourceId(anyString())).thenReturn(new HashMap<>()); + assertThat(vulnerabilityService.getVulnerabilitysummaryByResourceId("id"), is(notNullValue())); + } + + @Test + public void getVulnerabilityDetailsByResourceIdTest() throws Exception { + + List> vulnerabilitiesData = new ArrayList<>(); + + Map vuln = new HashMap<>(); + vuln.put("qid", "123"); + vuln.put("assetsAffected", 1); + vuln.put(Constants.TITLE, "test"); + vuln.put(Constants.SEVEITY_LEVEL, "3"); + vuln.put(Constants.CATEGORY, "test"); + vuln.put(Constants.VULN_TYPE, "type"); + vuln.put(Constants.PATCHABLE, "1"); + vulnerabilitiesData.add(vuln); + + when(vulnerabilityRepository.getVulnerabilityDetailsByResourceId(anyString())).thenReturn(vulnerabilitiesData); + assertThat(vulnerabilityService.getVulnerabilityDetailsByResourceId("id"), is(notNullValue())); + + vuln = new HashMap<>(); + vuln.put("qid", "123"); + vuln.put("assetsAffected", 1); + vulnerabilitiesData.add(vuln); + + when(vulnerabilityRepository.getVulnerabilityDetailsByResourceId(anyString())).thenReturn(vulnerabilitiesData); + assertThatThrownBy(() -> vulnerabilityService.getVulnerabilityDetailsByResourceId("id")) + .isInstanceOf(Exception.class); + } + + @Test + public void getVulnerabilityDistributionSummaryTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(anyString(), anyString(), anyString(), anyString(), + anyString())).thenReturn(getApps()); + when(vulnerabilityRepository.fetchOrgInfoForApps()).thenReturn(fetchOrgInfoForApps()); + + assertThat(vulnerabilityService.getVulnerabilityDistributionSummary("ag", "3"), is(notNullValue())); + assertThat(vulnerabilityService.getVulnerabilityDistributionSummary("ag", null), is(notNullValue())); + } + + @Test + public void getAgingSummaryTest() throws Exception { + + when(vulnerabilityRepository.getAgingSummary(anyString())).thenReturn(new ArrayList<>()); + assertThat(vulnerabilityService.getAgingSummary("ag"), is(notNullValue())); + } + + @Test + public void getAgingDistributionSummaryTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getAgingByApplication(anyString(), anyString(), anyString())) + .thenReturn(getApps()); + when(vulnerabilityRepository.fetchOrgInfoForApps()).thenReturn(fetchOrgInfoForApps()); + + assertThat(vulnerabilityService.getAgingDistributionSummary("ag", "3"), is(notNullValue())); + assertThat(vulnerabilityService.getAgingDistributionSummary("ag", null), is(notNullValue())); + } + + @Test + public void getAgingDistributionSummaryTest_Exception() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); + when(vulnerabilityRepository.getAgingByApplication(anyString(), anyString(), anyString())) + .thenReturn(getApps()); + when(vulnerabilityRepository.fetchOrgInfoForApps()).thenReturn(fetchOrgInfoForApps()); + assertThat(vulnerabilityService.getAgingDistributionSummary("ag", "3"), is(notNullValue())); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getAgingByApplication(anyString(), anyString(), anyString())) + .thenThrow(new Exception()); + assertThat(vulnerabilityService.getAgingDistributionSummary("ag", "3"), is(notNullValue())); + } + + @Test + public void getVulnerabilityByQidTest() throws Exception { + + Map vuln = new HashMap<>(); + vuln.put("qid", "123"); + vuln.put("vulntype", "type"); + vuln.put("severitylevel", "3"); + vuln.put("title", "test"); + vuln.put("category", ""); + vuln.put("lastservicemodificationdatetime", "1234"); + vuln.put("publisheddatetime", "123"); + vuln.put("patchable", "1"); + Map softwarelist = new HashMap<>(); + List> softwares = new ArrayList<>(); + Map innerMap = new HashMap<>(); + innerMap.put("test", "test"); + innerMap.put("test", "test"); + softwares.add(innerMap); + softwarelist.put("software", softwares); + vuln.put("softwarelist", softwarelist); + Map vendorreferencelist = new HashMap<>(); + List> vendorreference = new ArrayList<>(); + vendorreference.add(innerMap); + vendorreferencelist.put("vendorreference", vendorreference); + vuln.put("vendorreferencelist", vendorreferencelist); + vuln.put("diagnosis", "test"); + vuln.put("consequence", "test"); + vuln.put("solution", "test"); + Map bugtraqlist = new HashMap<>(); + List> bugtraq = new ArrayList<>(); + bugtraq.add(innerMap); + bugtraqlist.put("vendorreference", bugtraq); + vuln.put("bugtraqlist", bugtraqlist); + vuln.put("pciflag", 0); + Map pcireasons = new HashMap<>(); + List> pcireason = new ArrayList<>(); + pcireason.add(innerMap); + pcireasons.put("pcireason", pcireason); + vuln.put("pcireasons", pcireasons); + Map authtypelist = new HashMap<>(); + List> authtype = new ArrayList<>(); + authtype.add(innerMap); + authtypelist.put("authtype", authtype); + Map discovery = new HashMap<>(); + discovery.put("authtypelist", authtypelist); + discovery.put("additionalinfo", "Patch Available"); + vuln.put("discovery", discovery); + vuln.put("supportedmodules", "test"); + Map cvelist = new HashMap<>(); + List> cve = new ArrayList<>(); + cve.add(innerMap); + cvelist.put("cve", cve); + vuln.put("cvelist", cvelist); + Map cvss = new HashMap<>(); + cvss.put("base", 1); + cvss.put("temporal", 1); + vuln.put("cvssv3", cvss); + Map access = new HashMap<>(); + access.put("vector", 1); + cvss.put("access", access); + vuln.put("cvss", cvss); + + when(vulnerabilityRepository.getVulnerabilityByQid(anyString())).thenReturn(vuln); + assertThat(vulnerabilityService.getVulnerabilityByQid("id"), is(notNullValue())); + + vuln = new HashMap<>(); + discovery = new HashMap<>(); + discovery.put("additionalinfo", "test"); + vuln.put("discovery", discovery); + vuln.put("pciflag", 1); + + when(vulnerabilityRepository.getVulnerabilityByQid(anyString())).thenReturn(vuln); + assertThat(vulnerabilityService.getVulnerabilityByQid("id"), is(notNullValue())); + + when(vulnerabilityRepository.getVulnerabilityByQid(anyString())).thenReturn(new HashMap<>()); + assertThat(vulnerabilityService.getVulnerabilityByQid("id"), is(notNullValue())); + } + + @Test + public void getHighestLowestPerformersTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "test"); + when(vulnerabilityRepository.fetchOrgInfoForApps()).thenReturn(fetchOrgInfoForApps()); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", null, "org").size(), is(0)); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getAppsBySeverity(anyString(), anyString(), anyString())) + .thenReturn(new HashMap<>()); + when(vulnerabilityRepository.fetchOrgInfoForApps()).thenReturn(fetchOrgInfoForApps()); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", null, "org").size(), is(0)); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); + Map apps = new HashMap<>(); + apps.put("app1", 1L); + apps.put("app2", 2L); + apps.put("app3", 3L); + when(vulnerabilityRepository.getAppsBySeverity(anyString(), anyString(), anyString())).thenReturn(apps); + when(vulnerabilityRepository.fetchOrgInfoForApps()).thenReturn(fetchOrgInfoForApps()); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3", "org").size(), is(2)); + + when(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(anyString(), anyString(), anyString(), anyString(), + anyString())).thenReturn(getVulnByApp()); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3", Constants.APPLICATION).size(), is(2)); + + when(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(anyString(), anyString(), anyString(), anyString(), + anyString())).thenReturn(getVulnByEnv()); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3", Constants.ENVIRONMENT).size(), is(2)); + } + + @Test + public void getHighestLowestPerformersTest_Exception() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getAppsBySeverity(anyString(), anyString(), anyString())) + .thenReturn(new HashMap<>()); + when(vulnerabilityRepository.fetchOrgInfoForApps()).thenThrow(new Exception()); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3", "org").size(), is(0)); + + when(vulnerabilityRepository.getAppsBySeverity(anyString(), anyString(), anyString())) + .thenThrow(new Exception()); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3", "org").size(), is(0)); + + when(vulnerabilityRepository.getVulnerabilyAcrossAppAndEnv(anyString(), anyString(), anyString(), anyString(), + anyString())).thenThrow(new Exception()); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3", Constants.APPLICATION).size(), is(0)); + assertThat(vulnerabilityService.getHighestLowestPerformers("ag", "3", Constants.ENVIRONMENT).size(), is(0)); + } + + @Test + public void getDistributionSummaryByInfraTypeTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); + Map infraInfo = new HashMap<>(); + infraInfo.put(Constants.TOTAL_VULN_ASSETS, 1); + infraInfo.put(Constants.VULNEREBILITIES, 1); + infraInfo.put(Constants.UNIQUE_VULN_COUNT, 1); + when(vulnerabilityRepository.getDistributionSummaryByInfraType(anyString(), anyString(), anyString())) + .thenReturn(infraInfo); + assertThat(vulnerabilityService.getDistributionSummaryByInfraType("ag", null), is(notNullValue())); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getDistributionSummaryByInfraType(anyString(), anyString(), anyString())) + .thenReturn(infraInfo); + assertThat(vulnerabilityService.getDistributionSummaryByInfraType("ag", "3"), is(notNullValue())); + } + + @Test + public void getDistributionSummaryByInfraTypeTest_Exception() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getDistributionSummaryByInfraType(anyString(), anyString(), anyString())) + .thenThrow(new DataException()); + assertThatThrownBy(() -> vulnerabilityService.getDistributionSummaryByInfraType("ag", "3")) + .isInstanceOf(ServiceException.class); + } + + @Test + public void getDistributionSummaryByEnvTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); + + Map prodInfo = new HashMap<>(); + prodInfo.put("totalVulnerableAssets", 1L); + prodInfo.put(Constants.VULNEREBILITIES, 1L); + prodInfo.put("uniqueVulnCount", 1L); + + Map nonProdInfo = new HashMap<>(); + nonProdInfo.put("totalVulnerableAssets", 1L); + nonProdInfo.put(Constants.VULNEREBILITIES, 1L); + nonProdInfo.put("uniqueVulnCount", 1L); + + when(vulnerabilityRepository.getProdInfoByEnv(anyString(), anyString())).thenReturn(prodInfo); + when(vulnerabilityRepository.getNonProdInfoByEnv(anyString(), anyString())).thenReturn(nonProdInfo); + assertThat(vulnerabilityService.getDistributionSummaryByEnv("ag", "3"), is(notNullValue())); + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + + prodInfo = new HashMap<>(); + prodInfo.put("totalVulnerableAssets", 0L); + prodInfo.put(Constants.VULNEREBILITIES, 0L); + prodInfo.put("uniqueVulnCount", 0L); + + nonProdInfo = new HashMap<>(); + nonProdInfo.put("totalVulnerableAssets", 0L); + nonProdInfo.put(Constants.VULNEREBILITIES, 0L); + nonProdInfo.put("uniqueVulnCount", 0L); + + when(vulnerabilityRepository.getProdInfoByEnv(anyString(), anyString())).thenReturn(prodInfo); + when(vulnerabilityRepository.getNonProdInfoByEnv(anyString(), anyString())).thenReturn(nonProdInfo); + assertThat(vulnerabilityService.getDistributionSummaryByEnv("ag", ""), is(notNullValue())); + } + + @Test + public void getDistributionSummaryByEnvTest_Exception() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + Map prodInfo = new HashMap<>(); + prodInfo.put("totalVulnerableAssets", 1L); + prodInfo.put("uniqueVulnCount", 1L); + + Map nonProdInfo = new HashMap<>(); + nonProdInfo.put("totalVulnerableAssets", 1L); + nonProdInfo.put(Constants.VULNEREBILITIES, 1L); + nonProdInfo.put("uniqueVulnCount", 1L); + + when(vulnerabilityRepository.getProdInfoByEnv(anyString(), anyString())).thenReturn(prodInfo); + when(vulnerabilityRepository.getNonProdInfoByEnv(anyString(), anyString())).thenReturn(nonProdInfo); + assertThatThrownBy(() -> vulnerabilityService.getDistributionSummaryByEnv("ag", "3")) + .isInstanceOf(ServiceException.class); + } + + @Test + public void getDistributionSummaryByVulnTypeTest() throws Exception { + + when(vulnerabilityRepository.getDistributionSummaryByVulnType(anyString(), anyString())) + .thenReturn(new ArrayList<>()); + assertThat(vulnerabilityService.getDistributionSummaryByVulnType("ag", "3"), is(notNullValue())); + + when(vulnerabilityRepository.getDistributionSummaryByVulnType(anyString(), anyString())) + .thenReturn(new ArrayList<>()); + assertThat(vulnerabilityService.getDistributionSummaryByVulnType("ag", null), is(notNullValue())); + } + + @Test + public void getRemediationActionsSummaryTest() throws Exception { + + Map qids = new HashMap<>(); + qids.put("123~title~OS", 2); + qids.put("123~EOL/Obsolete~Infra", 2); + qids.put("11925~title~Infra", 2); + qids.put("370914~title~Infra", 2); + qids.put("123~Java Debug Wire Protocol~Infra", 0); + qids.put("123~Java JMX Server Insecure Configuration~Infra", 2); + qids.put("123~Java~Infra", 2); + qids.put("123~title~Infra", 2); + when(vulnerabilityRepository.getAllQidByAG(anyString(), anyString())).thenReturn(qids); + assertThat(vulnerabilityService.getRemediationActionsSummary("ag", null), is(notNullValue())); + } + + private List> getApps() { + + List> vulnApplications = new ArrayList<>(); + List> severityInfo = new ArrayList<>(); + + Map severity = new HashMap<>(); + severity.put(Constants.SEVERITY, "S3"); + severity.put(Constants.COUNT, 3); + severity.put("days", 3); + severityInfo.add(severity); + severity = new HashMap<>(); + severity.put(Constants.SEVERITY, "S4"); + severity.put(Constants.COUNT, 4); + severity.put("days", 4); + severityInfo.add(severity); + severity = new HashMap<>(); + severity.put(Constants.SEVERITY, "S5"); + severity.put(Constants.COUNT, 5); + severity.put("days", 5); + severityInfo.add(severity); + + Map vulnApp = new HashMap<>(); + vulnApp.put("application", "app1"); + vulnApp.put(Constants.SEV_INFO, severityInfo); + vulnApplications.add(vulnApp); + + vulnApp = new HashMap<>(); + vulnApp.put("application", "app2"); + vulnApp.put(Constants.SEV_INFO, severityInfo); + vulnApplications.add(vulnApp); + + vulnApp = new HashMap<>(); + vulnApp.put("application", "app3"); + vulnApp.put(Constants.SEV_INFO, severityInfo); + vulnApplications.add(vulnApp); + return vulnApplications; + } + + private List> getVulnByApp() { + + List> vulnEnvironments = new ArrayList<>(); + List> severityInfo = new ArrayList<>(); + + Map severity = new HashMap<>(); + severity.put("severitylevel", "3"); + severity.put("vulnInstanceCount", 3); + severityInfo.add(severity); + severity = new HashMap<>(); + + Map vulnApp = new HashMap<>(); + vulnApp.put("application", "app1"); + vulnApp.put(Constants.SEV_INFO, severityInfo); + vulnEnvironments.add(vulnApp); + + vulnApp = new HashMap<>(); + vulnApp.put("application", "app2"); + vulnApp.put(Constants.SEV_INFO, severityInfo); + vulnEnvironments.add(vulnApp); + + return vulnEnvironments; + } + + private List> getVulnByEnv() { + + List> vulnEnvironments = new ArrayList<>(); + List> severityInfo = new ArrayList<>(); + + Map severity = new HashMap<>(); + severity.put("severitylevel", "3"); + severity.put("vulnInstanceCount", 3); + severityInfo.add(severity); + severity = new HashMap<>(); + + Map vulnApp = new HashMap<>(); + vulnApp.put("environment", "env1"); + vulnApp.put(Constants.SEV_INFO, severityInfo); + vulnEnvironments.add(vulnApp); + + vulnApp = new HashMap<>(); + vulnApp.put("environment", "env2"); + vulnApp.put(Constants.SEV_INFO, severityInfo); + vulnEnvironments.add(vulnApp); + + return vulnEnvironments; + } + + private List> fetchOrgInfoForApps() { + List> apps = new ArrayList<>(); + Map app = new HashMap<>(); + app.put("appTag", "app1"); + app.put("list", "[{\"mgmntLevel\":\"2\",\"name\":\"svp1\"},{\"mgmntLevel\":\"3\",\"name\":\"vp1\"}]"); + apps.add(app); + + app = new HashMap<>(); + app.put("appTag", "app2"); + app.put("list", "[{\"mgmntLevel\":\"2\",\"name\":\"svp2\"},{\"mgmntLevel\":\"3\",\"name\":\"vp2\"}]"); + apps.add(app); + + app = new HashMap<>(); + app.put("appTag", "app3"); + app.put("list", "[{\"mgmntLevel\":\"4\",\"name\":\"sdir4\"}]"); + apps.add(app); + + app = new HashMap<>(); + app.put("appTag", "app5"); + app.put("list", "[{\"mgmntLevel\":\"2\",\"name\":\"svp5\"},{\"mgmntLevel\":\"5\",\"name\":\"dir5\"}]"); + apps.add(app); + + return apps; + } + + @Test + public void createTrendAnnotationTest() throws Exception { + + when(vulnerabilityRepository.createTrendAnnotation(any(TrendNote.class))).thenReturn(true); + assertThat(vulnerabilityService.createTrendAnnotation(new TrendNote()), is(true)); + } + + @Test + public void getTrendAnnotationTest() throws Exception { + + List> annotations = new ArrayList<>(); + Map annotation = new HashMap<>(); + annotation.put("ag", "ag"); + annotations.add(annotation); + + annotation = new HashMap<>(); + annotation.put("ag", ""); + annotations.add(annotation); + + when(vulnerabilityRepository.getTrendAnnotations(anyString(), any(Date.class))).thenReturn(annotations); + assertThat(vulnerabilityService.getTrendAnnotations("ag", new Date()).size(), is(2)); + } + + @Test + public void deleteTrendAnnotationTest() throws Exception { + + when(vulnerabilityRepository.deleteTrendAnnotation(anyString())).thenReturn(true); + assertThat(vulnerabilityService.deleteTrendAnnotation("noteId"), is(true)); + } + + @Test + public void getVulnerabilitySummaryByAssetsTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); + when(vulnerabilityRepository.getRunningInstancesCount(anyString(), anyString())).thenReturn(10L); + when(vulnerabilityRepository.getExemptedByRuleCount(anyString(), anyString())).thenReturn(1L); + + ResponseWithOrder responseWithOrder = new ResponseWithOrder(); + List> response = new ArrayList<>(); + LinkedHashMap obj = new LinkedHashMap<>(); + obj.put("assetsScanned", 5); + obj.put("failed", 2); + obj.put("passed", 2); + response.add(obj); + responseWithOrder.setResponse(response); + Map responseMap = new HashMap<>(); + responseMap.put("response", response); + ResponseEntity nonCompliancePolicyRuleresponse = ResponseUtils.buildSucessResponse(responseMap); + + when(complianceServiceClient.getNonCompliancePolicyByRule(any(Request.class))) + .thenReturn(nonCompliancePolicyRuleresponse); + // when(complianceServiceClient.getRulecompliance(any(Request.class))).thenReturn(responseWithOrder); + + Map compliant = new HashMap<>(); + compliant.put("S3", 2); + compliant.put("S4", 2); + + Map nonCompliant = new HashMap<>(); + nonCompliant.put("S3", 3); + nonCompliant.put("S4", 2); + nonCompliant.put("S5", 1); + + when(vulnerabilityRepository.getNonCompliantResourceIds(anyString())).thenReturn(new ArrayList<>()); + when(vulnerabilityRepository.getCompliantHostsBySeverity(anyString())).thenReturn(compliant); + when(vulnerabilityRepository.getUniqueHostBySeverity(anyString(), anyString())).thenReturn(nonCompliant); + + assertThat(vulnerabilityService.getVulnerabilitySummaryByAssets("ag").get(Constants.COUNT), is(20L)); + } + + @Test + public void getVulnerabilitySummaryByAssetsTest_Exception() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.getRunningInstancesCount(anyString(), anyString())).thenThrow(new DataException()); + assertThatThrownBy(() -> vulnerabilityService.getVulnerabilitySummaryByAssets("ag")) + .isInstanceOf(ServiceException.class); + } + + @Test + public void getVulnerabilityAssetsTrendTest() throws Exception { + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2,onpremserver"); + List assets = Arrays.asList("2018-10-01"); + when(vulnTrendGenerator.fetchAssetsDateRangesForEc2(anyString(), any(LocalDate.class))).thenReturn(assets); + when(vulnTrendGenerator.fetchAssetsDateRangesOnPrem(anyString(), any(LocalDate.class))).thenReturn(assets); + + assertThat(vulnerabilityService.getVulnerabilityAssetsTrend("ag", "3", new Date()).get(0).get("totalAssets"), + is(2L)); + + when(vulnTrendGenerator.fetchAssetsDateRangesForEc2(anyString(), any(LocalDate.class))) + .thenThrow(new DataException()); + when(vulnTrendGenerator.fetchAssetsDateRangesOnPrem(anyString(), any(LocalDate.class))) + .thenThrow(new DataException()); + + assertThat(vulnerabilityService.getVulnerabilityAssetsTrend("ag", "3", new Date()).size(), is(0)); + } + + @SuppressWarnings("unchecked") + @Test + public void vulnerabilityAssetCountTest() throws Exception { + + Map mustFilterMap = new HashMap<>(); + mustFilterMap.put("latest", "true"); + mustFilterMap.put("entity", "true"); + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + when(vulnerabilityRepository.vulnerabilityAssetsCount(anyString(), anyString(), anyMap(), anyInt(), anyInt(), + anyMap())).thenReturn(5); + assertThat(vulnerabilityService.vulnerabilityAssetCount("ag", mustFilterMap, "pac", "pro", 0, 0), is(5)); + } + + @SuppressWarnings("unchecked") + @Test + public void getAllVulnerabilitiesDetailsByAssetGroupTest() throws Exception { + + List> vulnerabilitiesDetails = new ArrayList>(); + Map map = new HashMap(); + Map map1 = new HashMap(); + Map mapf = new HashMap(); + map.put("ac", "66"); + map.put("acname", "second"); + map.put("res", "int"); + map.put("vp", "brt3"); + map.put("geid", "amigo"); + map1.put("rr", "345"); + map1.put("fd", "sec"); + map1.put("ij", "i"); + map1.put("cid", "b3"); + map1.put("mad", "mi"); + vulnerabilitiesDetails.add(map); + vulnerabilitiesDetails.add(map1); + + List> resourceIdDetails = new ArrayList>(); + List> abc = new ArrayList>(); + Map map2 = new HashMap(); + Map map3 = new HashMap(); + map2.put("s", "s3445"); + map2.put("ced", "866"); + map2.put("cif=", "1"); + map2.put("lna", "21.0"); + map2.put("cat", "pp"); + map3.put("ver", "fr"); + map3.put("ed", "cd85"); + map3.put("ifl=", "1"); + map3.put("ule", "2261.0"); + map3.put("sif", "app7"); + resourceIdDetails.add(map2); + resourceIdDetails.add(map3); + String assetGroup = "abc"; + Map mustFilter = new HashMap<>(); + Map mustTermsFilter = new HashMap<>(); + mustFilter.put("latest", "true"); + String targetType = "vulninfo"; + String searchText = ""; + int from = 0; + int size = 0; + List occurrenceFieldList = null; + + ReflectionTestUtils.setField(vulnerabilityService, "vulnTypes", "ec2"); + ReflectionTestUtils.setField(vulnerabilityService, "vulnOccurrenceFields", + "severity,_resourceid,pciflag,_vulnage,vulntype,title,classification,_status,qid,patchable,category"); + ReflectionTestUtils.setField(vulnerabilityService, "vulnResourceIdFields", + "tags.Name,accountid,accountname,tags.Environment,tags.Application,privateipaddress,instanceid,region,availabilityzone,imageid,platform,privatednsname,instancetype,subnetid,_resourceid,publicipaddress,publicdnsname,vpcid"); + ReflectionTestUtils.setField(vulnerabilityService, "vulnResourceIdFieldsBoth", + "tags.Name,accountid,accountname,tags.Environment,tags.Application,privateipaddress,instanceid,region,availabilityzone,imageid,platform,privatednsname,instancetype,subnetid,_resourceid,publicipaddress,publicdnsname,vpcid"); + when(vulnerabilityRepository.getAllVulnerabilitiesByAssetGroup(assetGroup, targetType, mustFilter, + occurrenceFieldList, from, size, mustTermsFilter)).thenReturn(vulnerabilitiesDetails); + + when(vulnerabilityRepository.getDetailsByResourceId(anyString(), anyMap(), anyList(), anyMap())) + .thenReturn(resourceIdDetails); + + assertThat(vulnerabilityService.getAllVulnerabilitiesDetailsByAssetGroup(assetGroup, mapf, "pac", "pro", + searchText, from, size), is(notNullValue())); + + size = 25; + + ReflectionTestUtils.setField(vulnerabilityService, "vulnOccurrenceFields", + "severity,_resourceid,pciflag,_vulnage,vulntype,title,classification,_status,qid,patchable,category"); + ReflectionTestUtils.setField(vulnerabilityService, "vulnResourceIdFields", + "tags.Name,accountid,accountname,tags.Environment,tags.Application,privateipaddress,instanceid,region,availabilityzone,imageid,platform,privatednsname,instancetype,subnetid,_resourceid,publicipaddress,publicdnsname,vpcid"); + + when(vulnerabilityRepository.getAllVulnerabilitiesByAssetGroup(assetGroup, targetType, mustFilter, + occurrenceFieldList, from, size, mustTermsFilter)).thenReturn(vulnerabilitiesDetails); + + when(vulnerabilityRepository.getDetailsByResourceId(anyString(), anyMap(), anyList(), anyMap())) + .thenReturn(resourceIdDetails); + + assertThat(vulnerabilityService.getAllVulnerabilitiesDetailsByAssetGroup(assetGroup, mapf, "pac", "pro", + searchText, from, size), is(notNullValue())); + + } + + @SuppressWarnings("unchecked") + /*@Test + public void getResourcedetailsByResourceIdTest() throws Exception { + + List resourceDetails = new ArrayList<>(); + String asset = "abc"; + boolean flag = false; + ReflectionTestUtils.setField(vulnerabilityService, "vulnResourceIdFields", + "tags.Name,accountid,accountname,tags.Environment,tags.Application,privateipaddress,instanceid,region,availabilityzone,imageid,platform,privatednsname,instancetype,subnetid,_resourceid,publicipaddress,publicdnsname,vpcid"); + ReflectionTestUtils.setField(vulnerabilityService, "vulnResourceIdFieldsBoth", + "tags.Name,accountid,accountname,tags.Environment,tags.Application,privateipaddress,instanceid,region,availabilityzone,imageid,platform,privatednsname,instancetype,subnetid,_resourceid,publicipaddress,publicdnsname,vpcid"); + when(vulnerabilityRepository.getDetailsByResourceId(anyString(), anyMap(), anyList(), anyMap())) + .thenReturn(resourceDetails); + assertThat(vulnerabilityService.getResourcedetailsByResourceId(asset, resourceDetails, flag), + is(notNullValue())); + }*/ + + @Test + public void getResourceIdTest() throws Exception { + + List> resourceIdDetails = new ArrayList>(); + Map map2 = new HashMap(); + Map map3 = new HashMap(); + map2.put("s", "s3445"); + map2.put("ced", "866"); + map2.put("cif=", "1"); + map2.put("lna", "21.0"); + map2.put("cat", "pp"); + map3.put("ver", "fr"); + map3.put("ed", "cd85"); + map3.put("ifl=", "1"); + map3.put("ule", "2261.0"); + map3.put("sif", "app7"); + resourceIdDetails.add(map2); + resourceIdDetails.add(map3); + + assertThat(vulnerabilityService.getResourceId(resourceIdDetails), is(notNullValue())); + } + + @Test + public void getCartesianProductTest() throws Exception { + + List> resourceIdDetails = new ArrayList>(); + List> vulnerabilityOccuranceList = new ArrayList>(); + + assertThat(vulnerabilityService.getCartesianProduct(vulnerabilityOccuranceList, resourceIdDetails), + is(notNullValue())); + } + +} diff --git a/api/pacman-api-vulnerability/src/test/resources/application.yml b/api/pacman-api-vulnerability/src/test/resources/application.yml new file mode 100644 index 000000000..11bacd496 --- /dev/null +++ b/api/pacman-api-vulnerability/src/test/resources/application.yml @@ -0,0 +1,5 @@ +spring: + data: + mongodb: + database: piggymetrics + port: 0 \ No newline at end of file diff --git a/api/pacman-api-vulnerability/src/test/resources/bootstrap.yml b/api/pacman-api-vulnerability/src/test/resources/bootstrap.yml new file mode 100644 index 000000000..ef86170bd --- /dev/null +++ b/api/pacman-api-vulnerability/src/test/resources/bootstrap.yml @@ -0,0 +1,3 @@ +eureka: + client: + enabled: false \ No newline at end of file diff --git a/api/pom.xml b/api/pom.xml index 721f98d1b..3f80ac676 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -1,56 +1,57 @@ - - - 4.0.0 - - com.tmobile.cloud - api - 1.0.0-SNAPSHOT - pom - - PacMan Api Projects - Build for T-Mobile PacMan Api Projects - https://github.com/tmobile/pacman/api - - - - Apache - https://github.com/tmobile/pacman/blob/master/LICENSE - repo - - - - - - PacMan Team - PacMan Team - pacbot@t-mobile.com - - - - - T-Mobile - https://www.t-mobile.com - - - - scm:git:git://github.com/tmobile/pacman.git - scm:git:ssh://github.com/tmobile/pacman.git - https://github.com/tmobile/pacman/tree/master - - - - - pacman-api-admin - pacman-api-asset - pacman-api-config - pacman-api-compliance - pacman-api-notifications - pacman-api-statistics - pacman-api-auth - - - - - + + + 4.0.0 + + com.tmobile.cloud + api + 1.0.0-SNAPSHOT + pom + + PacMan Api Projects + Build for T-Mobile PacMan Api Projects + https://github.com/tmobile/pacman/api + + + + Apache + https://github.com/tmobile/pacman/blob/master/LICENSE + repo + + + + + + PacMan Team + PacMan Team + pacbot@t-mobile.com + + + + + T-Mobile + https://www.t-mobile.com + + + + scm:git:git://github.com/tmobile/pacman.git + scm:git:ssh://github.com/tmobile/pacman.git + https://github.com/tmobile/pacman/tree/master + + + + + pacman-api-admin + pacman-api-asset + pacman-api-config + pacman-api-compliance + pacman-api-notifications + pacman-api-statistics + pacman-api-auth + pacman-api-vulnerability + + + + + diff --git a/commons/pac-api-commons/src/main/java/com/tmobile/pacman/api/commons/Constants.java b/commons/pac-api-commons/src/main/java/com/tmobile/pacman/api/commons/Constants.java index 048e2b292..43e9b51db 100644 --- a/commons/pac-api-commons/src/main/java/com/tmobile/pacman/api/commons/Constants.java +++ b/commons/pac-api-commons/src/main/java/com/tmobile/pacman/api/commons/Constants.java @@ -293,5 +293,9 @@ public interface Constants { String EVENTCATEGORY = "eventtypecategory"; String EVENTSTATUS = "eventstatus"; String FILTER_MANDATORY = "Filter is mandatory, pass the resourceid/docid/issueid/planid"; + String EC2_QUALYS_RULEID = "PacMan_Ec2InstanceScannedByQualys_version-1_Ec2-instance-scanned-by-qualys-API_ec2"; + String VIRTUALMACHINE = "virtualmachine"; + String VIRTUALMACHINE_QUALYS_RULEID = "PacMan_Ec2InstanceScannedByQualys_version-1_VmInstanceScannedByQualys_virtualmachine"; + String ONPREM_QUALYS_RULEID = "PacMan_Onprem-asset-scanned-by-qualys-API_version-1_OnpremassetscannedbyqualysAPI_onpremserver"; } diff --git a/installer/resources/lambda_rule_engine/files/rule_engine_cloudwatch_rules.json b/installer/resources/lambda_rule_engine/files/rule_engine_cloudwatch_rules.json index 35c0900c1..ee3587259 100644 --- a/installer/resources/lambda_rule_engine/files/rule_engine_cloudwatch_rules.json +++ b/installer/resources/lambda_rule_engine/files/rule_engine_cloudwatch_rules.json @@ -2440,6 +2440,28 @@ "modifiedDate": "2019-09-13", "severity": "low", "category": "security" + }, + { + "ruleId": "PacMan_Ec2InstanceScannedByQualys_version-1_Ec2-instance-scanned-by-qualys-API_ec2", + "ruleUUID": "aws_ec2_qualys_scanned_rule", + "policyId": "PacMan_Ec2InstanceScannedByQualys_version-1", + "ruleName": "Ec2InstanceScannedByQualysAPI", + "targetType": "ec2", + "assetGroup": "aws", + "alexaKeyword": "Ec2InstanceScannedByQualysAPI", + "ruleParams": "{\"params\":[{\"encrypt\":false,\"value\":\"30\",\"key\":\"target\"},{\"key\":\"esQualysUrl\",\"value\":\"\/aws_ec2\/qualysinfo\/_search\",\"isValueNew\":true,\"encrypt\":false},{\"key\":\"discoveredDaysRange\",\"value\":\"7\",\"isValueNew\":true,\"encrypt\":false},{\"key\":\"ruleKey\",\"value\":\"check-for-resource-scanned-by-qualys\",\"isValueNew\":true,\"encrypt\":false},{\"encrypt\":false,\"value\":\"high\",\"key\":\"severity\"},{\"encrypt\":false,\"value\":\"security\",\"key\":\"ruleCategory\"}],\"environmentVariables\":[],\"ruleId\":\"PacMan_Ec2InstanceScannedByQualys_version-1_Ec2-instance-scanned-by-qualys-API_ec2\",\"autofix\":false,\"alexaKeyword\":\"Ec2InstanceScannedByQualysAPI\",\"ruleRestUrl\":\"\",\"targetType\":\"ec2\",\"pac_ds\":\"aws\",\"policyId\":\"PacMan_Ec2InstanceScannedByQualys_version-1\",\"assetGroup\":\"aws\",\"ruleUUID\":\"aws_ec2_qualys_scanned_rule\",\"ruleType\":\"ManageRule\"}", + "ruleFrequency": "0 * * * ? *", + "ruleExecutable": "", + "ruleRestUrl": "", + "ruleType": "ManageRule", + "ruleArn": "arn:aws:events:us-east-1:***REMOVED***:rule/aws_ec2_qualys_scanned_rule", + "status": "ENABLED", + "userId": "ASGC", + "displayName": "Every EC2 instance should be scanned by Qualys vulnerability assessment tool atleast once a month", + "createdDate": "2019-09-18", + "modifiedDate": "2019-09-18", + "severity": "high", + "category": "security" } ] diff --git a/installer/resources/pacbot_app/files/DB.sql b/installer/resources/pacbot_app/files/DB.sql index 965001947..f6fc28bc9 100644 --- a/installer/resources/pacbot_app/files/DB.sql +++ b/installer/resources/pacbot_app/files/DB.sql @@ -81,6 +81,7 @@ SET @PACMAN_LOGIN_PASSWORD='$PACMAN_LOGIN_PASSWORD'; SET @CONFIG_CREDENTIALS='$CONFIG_CREDENTIALS'; SET @CONFIG_SERVICE_URL='$CONFIG_SERVICE_URL'; SET @PACBOT_AUTOFIX_RESOURCEOWNER_FALLBACK_MAILID='$PACBOT_AUTOFIX_RESOURCEOWNER_FALLBACK_MAILID'; +SET @CONTEXT_POSTFIX='$CONTEXT_POSTFIX'; CREATE TABLE IF NOT EXISTS `OmniSearch_Config` ( @@ -1308,11 +1309,11 @@ INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`t INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`targetType`,`assetGroup`,`alexaKeyword`,`ruleParams`,`ruleFrequency`,`ruleExecutable`,`ruleRestUrl`,`ruleType`,`ruleArn`,`status`,`userId`,`displayName`,`createdDate`,`modifiedDate`,`severity`,`category`) VALUES ('PacMan_CloudWatchEventsForAllAccounts_version-1_CloudWatchEventsForAllAccounts_account','aws_account_cloud_watch_events','PacMan_CloudWatchEventsForAllAccounts_version-1','CloudWatchEventsForAllAccounts','account','aws','CloudWatchEventsForAllAccounts','{"params":[{"encrypt":false,"value":"check-cloudwatch-event-rule","key":"ruleKey"},{"encrypt":false,"value":"role/pac_ro","key":"roleIdentifyingString"},{"encrypt":false,"value":"DO-NOT-DELETE-pacman-all-events-to-eventbus-in-ACCOUNTID","key":"ruleName"},{"encrypt":false,"value":"high","key":"severity"},{"encrypt":false,"value":"governance","key":"ruleCategory"}],"environmentVariables":[],"ruleId":"PacMan_CloudWatchEventsForAllAccounts_version-1_CloudWatchEventsForAllAccounts_account","autofix":false,"alexaKeyword":"CloudWatchEventsForAllAccounts","ruleRestUrl":"","targetType":"account","pac_ds":"aws","policyId":"PacMan_CloudWatchEventsForAllAccounts_version-1","assetGroup":"aws","ruleUUID":"aws_account_cloud_watch_events","ruleType":"ManageRule"}','0 0 ? * MON *','','','Manage Rule',concat('arn:aws:events:',@region,':',@account,':rule/aws_account_cloud_watch_events'),'ENABLED','ASGC','All Cloud watch events from all accounts should be sent to DEDICATED ACCOUNTID default event bus','2019-08-05','2019-08-05','high','governance'); INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`targetType`,`assetGroup`,`alexaKeyword`,`ruleParams`,`ruleFrequency`,`ruleExecutable`,`ruleRestUrl`,`ruleType`,`ruleArn`,`status`,`userId`,`displayName`,`createdDate`,`modifiedDate`,`severity`,`category`) VALUES ('PacMan_LowUtilizationAmazonEC2InstancesRule_version-1_LowUtilizationAmazonEC2InstancesRule_ec2','aws_ec2_low_utilization','PacMan_LowUtilizationAmazonEC2InstancesRule_version-1','LowUtilizationAmazonEC2InstancesRule','ec2','aws','LowUtilizationAmazonEC2InstancesRule','{"params":[{"encrypt":false,"value":"check-for-low-utilization-amazon-ec2-instance","key":"ruleKey"},{"encrypt":false,"value":"","key":"checkId"},{"encrypt":false,"value":"low","key":"severity"},{"isValueNew":true,"encrypt":false,"value":"costOptimization","key":"ruleCategory"},{"key":"esServiceURL","value":"/aws_checks/checks_resources/_search","isValueNew":true,"encrypt":false}],"environmentVariables":[],"ruleId":"PacMan_LowUtilizationAmazonEC2InstancesRule_version-1_LowUtilizationAmazonEC2InstancesRule_ec2","autofix":false,"alexaKeyword":"LowUtilizationAmazonEC2InstancesRule","ruleRestUrl":"","targetType":"ec2","pac_ds":"aws","policyId":"PacMan_LowUtilizationAmazonEC2InstancesRule_version-1","assetGroup":"aws","ruleUUID":"aws_ec2_low_utilization","ruleType":"ManageRule"}','0 0 ? * MON *','','','Manage Rule',concat('arn:aws:events:',@region,':',@account,':rule/aws_ec2_low_utilization'),'ENABLED','ASGC','Amazon EC2 instances should not have low utilization','2019-08-05','2019-08-05','high','governance'); INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`targetType`,`assetGroup`,`alexaKeyword`,`ruleParams`,`ruleFrequency`,`ruleExecutable`,`ruleRestUrl`,`ruleType`,`ruleArn`,`status`,`userId`,`displayName`,`createdDate`,`modifiedDate`,`severity`,`category`) VALUES ('PacMan_TaggingRule_version-1_VPNGatewayMandatoryTagging_vpngateway','aws_vpngateway_mandatory_tag_rule','PacMan_TaggingRule_version-1','VpnGatewayTaggingRule','vpngateway','aws','VpnGatewayTaggingRule','{"params":[{"key":"ruleKey","value":"check-for-missing-mandatory-tags","encrypt":false},{"key":"splitterChar","value":",","encrypt":false},{"key":"mandatoryTags","value":"Application,Environment,Stack,Role","encrypt":false},{"encrypt":false,"value":"low","key":"severity"},{"encrypt":false,"value":"tagging","key":"ruleCategory"}],"environmentVariables":[],"ruleId":"PacMan_TaggingRule_version-1_VPNGatewayMandatoryTagging_vpngateway","autofix":false,"alexaKeyword":"VPNGatewayTagging","ruleRestUrl":"","targetType":"vpngateway","pac_ds":"aws","policyId":"PacMan_TaggingRule_version-1","assetGroup":"aws","ruleUUID":"aws_vpngateway_mandatory_tag_rule","ruleType":"ManageRule"}','0 0 ? * MON *','','','Manage Rule',concat('arn:aws:events:',@region,':',@account,':rule/aws_vpngateway_mandatory_tag_rule'),'ENABLED','ASGC','VPNGateway should be tagged with mandatory tags','2019-08-05','2019-08-05','high','governance'); - INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`targetType`,`assetGroup`,`alexaKeyword`,`ruleParams`,`ruleFrequency`,`ruleExecutable`,`ruleRestUrl`,`ruleType`,`ruleArn`,`status`,`userId`,`displayName`,`createdDate`,`modifiedDate`,`severity`,`category`) VALUES ('PacMan_Ec2WithSeverityVulnerability_version-1_Ec2WithS3Vulnerability_ec2','aws_ec2_vuln_severity_rule','PacMan_Ec2WithSeverityVulnerability_version-1','Ec2WithS3VulnerabilityRule','ec2','aws','Ec2WithS3VulnerabilityRule','{"params":[{"encrypt":false,"value":"S3","key":"severityVulnValue"},{"key":"esResourceWithVulnInfoForSeverityUrl","value":"/aws_ec2/vulninfo/_search","isValueNew":true,"encrypt":false},{"key":"ruleKey","value":"check-for-resource-with-severity-vulnerabilities","isValueNew":true,"encrypt":false},{"encrypt":false,"value":"low","key":"severity"},{"encrypt":false,"value":"security","key":"ruleCategory"}],"environmentVariables":[],"ruleId":"PacMan_Ec2WithSeverityVulnerability_version-1_Ec2WithS3Vulnerability_ec2","autofix":false,"alexaKeyword":"Ec2WithS3Vulnerability","ruleRestUrl":"","targetType":"ec2","pac_ds":"aws","policyId":"PacMan_Ec2WithSeverityVulnerability_version-1","assetGroup":"aws","ruleUUID":"aws_ec2_vuln_severity_rule","ruleType":"ManageRule"}','0 0 ? * MON *','','','Manage Rule',concat('arn:aws:events:',@region,':',@account,':rule/aws_ec2_vuln_severity_rule'),'ENABLED','ASGC','Any Ec2 instance should not have S3 vulnerability','2019-08-05','2019-08-05','high','governance'); INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`targetType`,`assetGroup`,`alexaKeyword`,`ruleParams`,`ruleFrequency`,`ruleExecutable`,`ruleRestUrl`,`ruleType`,`ruleArn`,`status`,`userId`,`displayName`,`createdDate`,`modifiedDate`,`severity`,`category`) VALUES ('PacMan_Ec2WithSeverityVulnerability_version-1_Ec2WithS4Vulnerability_ec2','aws_ec2_vuln_s4_rule','PacMan_Ec2WithSeverityVulnerability_version-1','Ec2WithS4VulnerabilityRule','ec2','aws','Ec2WithS4VulnerabilityRule','{"params":[{"encrypt":false,"value":"S4","key":"severityVulnValue"},{"key":"esResourceWithVulnInfoForSeverityUrl","value":"/aws_ec2/vulninfo/_search","isValueNew":true,"encrypt":false},{"key":"ruleKey","value":"check-for-resource-with-severity-vulnerabilities","isValueNew":true,"encrypt":false},{"encrypt":false,"value":"low","key":"severity"},{"encrypt":false,"value":"security","key":"ruleCategory"}],"environmentVariables":[],"ruleId":"PacMan_Ec2WithSeverityVulnerability_version-1_Ec2WithS4Vulnerability_ec2","autofix":false,"alexaKeyword":"Ec2WithS4Vulnerability","ruleRestUrl":"","targetType":"ec2","pac_ds":"aws","policyId":"PacMan_Ec2WithSeverityVulnerability_version-1","assetGroup":"aws","ruleUUID":"aws_ec2_vuln_s4_rule","ruleType":"ManageRule"}','0 0 ? * MON *','','','Manage Rule',concat('arn:aws:events:',@region,':',@account,':rule/aws_ec2_vuln_s4_rule'),'ENABLED','ASGC','Any Ec2 instance should not have S4 vulnerability','2019-08-05','2019-08-05','high','governance'); INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`targetType`,`assetGroup`,`alexaKeyword`,`ruleParams`,`ruleFrequency`,`ruleExecutable`,`ruleRestUrl`,`ruleType`,`ruleArn`,`status`,`userId`,`displayName`,`createdDate`,`modifiedDate`,`severity`,`category`) VALUES ('PacMan_Ec2WithSeverityVulnerability_version-1_Ec2WithS5Vulnerability_ec2','aws_ec2_vuln_s5_rule','PacMan_Ec2WithSeverityVulnerability_version-1','Ec2WithS5VulnerabilityRule','ec2','aws','Ec2WithS5VulnerabilityRule','{"params":[{"encrypt":false,"value":"S5","key":"severityVulnValue"},{"key":"esResourceWithVulnInfoForSeverityUrl","value":"/aws_ec2/vulninfo/_search","isValueNew":true,"encrypt":false},{"key":"ruleKey","value":"check-for-resource-with-severity-vulnerabilities","isValueNew":true,"encrypt":false},{"encrypt":false,"value":"low","key":"severity"},{"encrypt":false,"value":"security","key":"ruleCategory"}],"environmentVariables":[],"ruleId":"PacMan_Ec2WithSeverityVulnerability_version-1_Ec2WithS5Vulnerability_ec2","autofix":false,"alexaKeyword":"Ec2WithS5Vulnerability","ruleRestUrl":"","targetType":"ec2","pac_ds":"aws","policyId":"PacMan_Ec2WithSeverityVulnerability_version-1","assetGroup":"aws","ruleUUID":"aws_ec2_vuln_s5_rule","ruleType":"ManageRule"}','0 0 ? * MON *','','','Manage Rule',concat('arn:aws:events:',@region,':',@account,':rule/aws_ec2_vuln_s5_rule'),'ENABLED','ASGC','Any Ec2 instance should not have S5 vulnerability','2019-08-05','2019-08-05','high','governance'); INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`targetType`,`assetGroup`,`alexaKeyword`,`ruleParams`,`ruleFrequency`,`ruleExecutable`,`ruleRestUrl`,`ruleType`,`ruleArn`,`status`,`userId`,`displayName`,`createdDate`,`modifiedDate`,`severity`,`category`) VALUES ('PacMan_Ec2PublicAccessPortWithS5Vulnerability_version-1_Ec2PublicAccessPortWithS5Vulnerability_ec2','aws_ec2_pub_vuln_s5_rule','PacMan_Ec2PublicAccessPortWithS5Vulnerability_version-1','Ec2PublicAccessPortWithS5Vuln','ec2','aws','Ec2PublicAccessPortWithS5Vuln','{"params":[{"encrypt":false,"value":"check-for-ec2-public-access-port-with-s5-vulnerabilities","key":"ruleKey"},{"encrypt":false,"value":"S5","key":"severityVulnValue"},{"encrypt":false,"value":"PacMan_EC2WithPublicIPAccess_version-1_Ec2WithPublicAccess_ec2","key":"ec2PortRuleId"},{"key":"esEc2WithVulnInfoForS5Url","value":"/aws_ec2/vulninfo/_search","isValueNew":true,"encrypt":false},{"key":"esEc2PubAccessPortUrl","value":"/aws/issue_ec2/_search","isValueNew":true,"encrypt":false},{"key":"esAppElbWithInstanceUrl","value":"/aws_appelb/appelb_instances/_search","isValueNew":true,"encrypt":false},{"key":"esClassicElbWithInstanceUrl","value":"/aws_classicelb/classicelb_instances/_search","isValueNew":true,"encrypt":false},{"key":"esAppElbPubAccessPortUrl","value":"/aws_appelb/issue_appelb/_search","isValueNew":true,"encrypt":false},{"key":"esClassicElbPubAccessPortUrl","value":"/aws_classicelb/issue_classicelb/_search","isValueNew":true,"encrypt":false},{"key":"appElbPortRuleId","value":"PacMan_ElbWithPublicAccess_version-1_ApplicationElbWithPublicAccess_appelb","isValueNew":true,"encrypt":false},{"key":"classicElbPortRuleId","value":"PacMan_ElbWithPublicAccess_version-1_ClassicElbWithPublicAccess_classicelb","isValueNew":true,"encrypt":false},{"encrypt":false,"value":"critical","key":"severity"},{"encrypt":false,"value":"security","key":"ruleCategory"}],"environmentVariables":[],"ruleId":"PacMan_Ec2PublicAccessPortWithS5Vulnerability_version-1_Ec2PublicAccessPortWithS5Vulnerability_ec2","autofix":false,"alexaKeyword":"Ec2PublicAccessPortWithS5Vulnerability","ruleRestUrl":"","targetType":"ec2","pac_ds":"aws","policyId":"PacMan_Ec2PublicAccessPortWithS5Vulnerability_version-1","assetGroup":"aws","ruleUUID":"aws_ec2_pub_vuln_s5_rule","ruleType":"ManageRule"}','0 0 ? * MON *','','','Manage Rule',concat('arn:aws:events:',@region,':',@account,':rule/aws_ec2_pub_vuln_s5_rule'),'ENABLED','ASGC','An Ec2 instance with remotely exploitable vulnerability (S5) should not be open to internet','2019-08-05','2019-08-05','high','governance'); +INSERT IGNORE INTO cf_RuleInstance (`ruleId`,`ruleUUID`,`policyId`,`ruleName`,`targetType`,`assetGroup`,`alexaKeyword`,`ruleParams`,`ruleFrequency`,`ruleExecutable`,`ruleRestUrl`,`ruleType`,`ruleArn`,`status`,`userId`,`displayName`,`createdDate`,`modifiedDate`,`severity`,`category`) VALUES ('PacMan_Ec2InstanceScannedByQualys_version-1_Ec2-instance-scanned-by-qualys-API_ec2','aws_ec2_qualys_scanned_rule','PacMan_Ec2InstanceScannedByQualys_version-1','Ec2InstanceScannedByQualysAPI','ec2','aws','Ec2InstanceScannedByQualysAPI','{"params":[{"encrypt":false,"value":"30","key":"target"},{"key":"esQualysUrl","value":"/aws_ec2/qualysinfo/_search","isValueNew":true,"encrypt":false},{"key":"discoveredDaysRange","value":"7","isValueNew":true,"encrypt":false},{"key":"ruleKey","value":"check-for-resource-scanned-by-qualys","isValueNew":true,"encrypt":false},{"encrypt":false,"value":"high","key":"severity"},{"encrypt":false,"value":"security","key":"ruleCategory"}],"environmentVariables":[],"ruleId":"PacMan_Ec2InstanceScannedByQualys_version-1_Ec2-instance-scanned-by-qualys-API_ec2","autofix":false,"alexaKeyword":"Ec2InstanceScannedByQualysAPI","ruleRestUrl":"","targetType":"ec2","pac_ds":"aws","policyId":"PacMan_Ec2InstanceScannedByQualys_version-1","assetGroup":"aws","ruleUUID":"aws_ec2_qualys_scanned_rule","ruleType":"ManageRule"}','0 0 ? * MON *','','','Manage Rule',concat('arn:aws:events:',@region,':',@account,':rule/aws_ec2_qualys_scanned_rule'),'ENABLED','ASGC','Every EC2 instance should be scanned by Qualys vulnerability assessment tool atleast once a month','2019-09-18','2019-09-18','high','security'); @@ -1780,6 +1781,10 @@ INSERT IGNORE INTO `pac_config_key_metadata` (`cfkey`, `description`) values('pa INSERT IGNORE INTO `pac_config_key_metadata` (`cfkey`, `description`) values('pacman.auto.fix.mail.template.columns.PacMan_UnusedElasticIpRule_version-1_UnusedElasticIpRule_elasticip','Description PlaceHolder'); INSERT IGNORE INTO `pac_config_key_metadata` (`cfkey`, `description`) values('pacman.auto.fix.common.email.notifications.PacMan_UnusedElasticIpRule_version-1_UnusedElasticIpRule_elasticip','Description PlaceHolder'); INSERT IGNORE INTO `pac_config_key_metadata` (`cfkey`, `description`) values('pacman.autofix.issue.creation.time.elapsed.PacMan_UnusedElasticIpRule_version-1_UnusedElasticIpRule_elasticip','Description PlaceHolder'); +INSERT IGNORE INTO pac_config_key_metadata (`cfkey`,`description`) VALUES ('service.url.vulnerability','Description PlaceHolder'); +INSERT IGNORE INTO pac_config_key_metadata (`cfkey`,`description`) VALUES ('vulnerability.application.occurance','Description PlaceHolder'); +INSERT IGNORE INTO pac_config_key_metadata (`cfkey`,`description`) VALUES ('vulnerability.application.resourcedetails','Description PlaceHolder'); +INSERT IGNORE INTO pac_config_key_metadata (`cfkey`,`description`) VALUES ('vulnerability.application.resourcedetailsboth','Description PlaceHolder'); @@ -2066,6 +2071,17 @@ INSERT IGNORE INTO `pac_config_properties` (`cfkey`, `value`, `application`, `pr INSERT IGNORE INTO `pac_config_properties` (`cfkey`, `value`, `application`, `profile`, `label`, `createdBy`, `createdDate`, `modifiedBy`, `modifiedDate`) values('pacman.autofix.rule.post.fix.message.PacMan_UnusedElasticIpRule_version-1_UnusedElasticIpRule_elasticip','PacBot has now automatically deleted the following list of Unassociated Elastic IP Addresses','rule','prd','latest',NULL,NULL,NULL,NULL); INSERT IGNORE INTO `pac_config_properties` (`cfkey`, `value`, `application`, `profile`, `label`, `createdBy`, `createdDate`, `modifiedBy`, `modifiedDate`) values('pacman.auto.fix.mail.template.columns.PacMan_UnusedElasticIpRule_version-1_UnusedElasticIpRule_elasticip','Resource Id,Account Id,Region,Group Name','rule','prd','latest',NULL,NULL,NULL,NULL); INSERT IGNORE INTO `pac_config_properties` (`cfkey`, `value`, `application`, `profile`, `label`, `createdBy`, `createdDate`, `modifiedBy`, `modifiedDate`) values('pacman.auto.fix.common.email.notifications.PacMan_UnusedElasticIpRule_version-1_UnusedElasticIpRule_elasticip','commonTemplate','rule','prd','latest',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO `pac_config_properties` (`cfkey`, `value`, `application`, `profile`, `label`, `createdBy`, `createdDate`, `modifiedBy`, `modifiedDate`) VALUES('service.url.vulnerability',concat(@PACMAN_HOST_NAME,'/api/vulnerability'),'api','prd','latest',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO pac_config_properties(`cfkey`,`value`,`application`,`profile`,`label`,`createdBy`,`createdDate`,`modifiedBy`,`modifiedDate`) VALUES ('api.services[6].name','Vulnerability Service','api','prd','latest',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO pac_config_properties(`cfkey`,`value`,`application`,`profile`,`label`,`createdBy`,`createdDate`,`modifiedBy`,`modifiedDate`) VALUES ('api.services[6].url','${PACMAN_HOST_NAME:http://localhost:8080}/api/vulnerability/v2/api-docs','api','prd','latest',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO pac_config_properties(`cfkey`,`value`,`application`,`profile`,`label`,`createdBy`,`createdDate`,`modifiedBy`,`modifiedDate`) VALUES ('api.services[6].version','2','api','prd','latest',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO pac_config_properties (`cfkey`,`value`,`application`,`profile`,`label`,`createdBy`,`createdDate`,`modifiedBy`,`modifiedDate`) VALUES ('server.servlet.context-path','/api/vulnerability','vulnerability-service','prd','latest',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO pac_config_properties (`cfkey`,`value`,`application`,`profile`,`label`,`createdBy`,`createdDate`,`modifiedBy`,`modifiedDate`) VALUES ('server.contextPath','/api/vulnerability${CONTEXT_POSTFIX}','vulnerability-service','prd','latest',NULL,NULL,NULL,NULL); + +INSERT IGNORE INTO pac_config_properties (`cfkey`,`value`,`application`,`profile`,`label`,`createdBy`,`createdDate`,`modifiedBy`,`modifiedDate`) VALUES ('vulnerability.application.occurance','severity,_resourceid,pciflag,_vulnage,vulntype,title,classification,_firstFound,_lastFound,qid,patchable,category','vulnerability-service','prd','latest',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO pac_config_properties (`cfkey`,`value`,`application`,`profile`,`label`,`createdBy`,`createdDate`,`modifiedBy`,`modifiedDate`) VALUES ('vulnerability.application.resourcedetails','tags.Name,accountid,accountname,tags.Environment,tags.Application,privateipaddress,instanceid,region,availabilityzone,imageid,platform,privatednsname,instancetype,subnetid,_resourceid,publicipaddress,publicdnsname,vpcid','vulnerability-service','prd','latest',NULL,NULL,NULL,NULL); +INSERT IGNORE INTO pac_config_properties (`cfkey`,`value`,`application`,`profile`,`label`,`createdBy`,`createdDate`,`modifiedBy`,`modifiedDate`) VALUES ('vulnerability.application.resourcedetailsboth','tags.Name,tags.Environment,tags.Application,ip_address,privateipaddress,_entitytype,_resourceid','vulnerability-service','prd','latest',NULL,NULL,NULL,NULL); + INSERT IGNORE INTO `Recommendation_Mappings`(`checkId`,`type`,`resourceInfo`,`_resourceId`,`monthlySavingsField`) values ('H7IgTzjTYb','volume','Volume ID','volumeid',NULL),('DAvU99Dc4C','volume','Volume ID','volumeid','Monthly Storage Cost'),('Qch7DwouX1','ec2','Instance ID','instanceid','Estimated Monthly Savings'),('1iG5NDGVre','sg','Security Group ID','groupid',NULL),('HCP4007jGY','sg','Security Group ID','groupid',NULL),('BueAdJ7NrP','s3','Bucket Name','name',NULL),('iqdCTZKCUp','classicelb','Load Balancer Name','loadbalancername',NULL),('R365s2Qddf','s3','Bucket Name','name',NULL),('Pfx0RwqBli','s3','Bucket Name','name',NULL),('a2sEc6ILx','classicelb','Load Balancer Name','loadbalancername',NULL),('xdeXZKIUy','classicelb','Load Balancer Name','loadbalancername',NULL),('CLOG40CDO8','asg','Auto Scaling Group Name','autoscalinggroupname',NULL),('7qGXsKIUw','classicelb','Load Balancer Name','loadbalancername',NULL),('hjLMh88uM8','classicelb','Load Balancer Name','loadbalancername','Estimated Monthly Savings'),('DqdJqYeRm5','iamuser','IAM User','username',NULL),('j3DFqYTe29','ec2','Instance ID','instanceid',NULL),('f2iK5R6Dep','rdsdb','DB Instance','dbinstanceidentifier',NULL),('1MoPEMsKx6','reservedinstance','Instance Type','instancetype','Estimated Monthly Savings'),('Ti39halfu8','rdsdb','DB Instance Name','dbinstanceidentifier','Estimated Monthly Savings (On Demand)'),('Wnwm9Il5bG','ec2','Instance ID','instanceid',NULL),('V77iOLlBqz','ec2','Instance ID','instanceid',NULL),('Z4AUBRNSmz','elasticip','IP Address','publicip',NULL),('8CNsSllI5v','asg','Auto Scaling Group Name','autoscalinggroupname',NULL),('N420c450f2','cloudfront','Distribution ID','id',NULL),('TyfdMXG69d','ec2','Instance ID','instanceid',NULL),('tfg86AVHAZ','sg','Group ID','groupid',NULL),('yHAGQJV9K5','ec2','Instance ID','instanceid',NULL),('S45wrEXrLz','vpnconnection','VPN ID','vpnconnectionid',NULL),('PPkZrjsH2q','volume','Volume ID','volumeid',NULL),('opQPADkZvH','rdsdb','DB Instance','dbinstanceidentifier',NULL),('796d6f3D83','s3','Bucket Name','name',NULL),('G31sQ1E9U','redshift','Cluster','clusteridentifier','Estimated Monthly Savings'),('xSqX82fQu','classicelb','Load Balancer Name','loadbalancername',NULL),('ZRxQlPsb6c','ec2','Instance ID','instanceid',NULL),('N430c450f2','cloudfront','Distribution ID','id',NULL),('4g3Nt5M1Th','virtualinterface','Gateway ID','virtualgatewayid',NULL),('0t121N1Ty3','directconnect','Connection ID','connectionid',NULL),('N425c450f2','cloudfront','Distribution ID','id',NULL),('xuy7H1avtl','rdscluster','Cluster','dbclusteridentifier',NULL),('1e93e4c0b5','reservedinstance','Reserved Instance ID','instanceid','Estimated Monthly Savings'),('51fC20e7I2','route53','Hosted Zone ID','hostedZoneId',NULL),('cF171Db240','route53','Hosted Zone ID','hostedZoneId',NULL),('Cb877eB72b','route53','Hosted Zone ID','hostedZoneId',NULL),('b73EEdD790','route53','Hosted Zone ID','hostedZoneId',NULL),('C056F80cR3','route53','Hosted Zone ID','hostedZoneId',NULL),('B913Ef6fb4','route53','Hosted Zone ID','hostedZoneId',NULL); diff --git a/jobs/pacman-awsrules/src/main/java/com/tmobile/cloud/awsrules/ec2/Ec2InstanceScannedByQualysRule.java b/jobs/pacman-awsrules/src/main/java/com/tmobile/cloud/awsrules/ec2/ResourceScannedByQualysRule.java similarity index 75% rename from jobs/pacman-awsrules/src/main/java/com/tmobile/cloud/awsrules/ec2/Ec2InstanceScannedByQualysRule.java rename to jobs/pacman-awsrules/src/main/java/com/tmobile/cloud/awsrules/ec2/ResourceScannedByQualysRule.java index dd1df58a2..ae99cda31 100644 --- a/jobs/pacman-awsrules/src/main/java/com/tmobile/cloud/awsrules/ec2/Ec2InstanceScannedByQualysRule.java +++ b/jobs/pacman-awsrules/src/main/java/com/tmobile/cloud/awsrules/ec2/ResourceScannedByQualysRule.java @@ -45,24 +45,20 @@ import com.tmobile.pacman.commons.rule.RuleResult; /** - * The Class Ec2InstanceScannedByQualysRule. + * The Class ResourceScannedByQualysRule. */ -@PacmanRule(key = "check-for-ec2-scanned-by-qualys", desc = "checks for Ec2 instance scanned by qualys,if not found then its an issue", severity = PacmanSdkConstants.SEV_HIGH, category = PacmanSdkConstants.GOVERNANCE) -public class Ec2InstanceScannedByQualysRule extends BaseRule { +@PacmanRule(key = "check-for-resource-scanned-by-qualys", desc = "checks for Ec2 instance or VM scanned by qualys,if not found then its an issue", severity = PacmanSdkConstants.SEV_HIGH, category = PacmanSdkConstants.GOVERNANCE) +public class ResourceScannedByQualysRule extends BaseRule { /** The Constant logger. */ - private static final Logger logger = LoggerFactory.getLogger(Ec2InstanceScannedByQualysRule.class); - + private static final Logger logger = LoggerFactory.getLogger(ResourceScannedByQualysRule.class); + /** * The method will get triggered from Rule Engine with following parameters. * * @param ruleParam ************* Following are the Rule Parameters*********

* - * ruleKey : check-for-ec2-scanned-by-qualys

- * - * severity : Enter the value of severity

- * - * ruleCategory : Enter the value of category

+ * ruleKey : check-for-resource-scanned-by-qualys

* * target : Enter the target days

* @@ -70,13 +66,12 @@ public class Ec2InstanceScannedByQualysRule extends BaseRule { * * esQualysUrl : Enter the qualys URL

* - * threadsafe : if true , rule will be executed on multiple threads

* @param resourceAttributes this is a resource in context which needs to be scanned this is provided by execution engine * @return the rule result */ public RuleResult execute(final Map ruleParam,Map resourceAttributes) { - logger.debug("========Ec2InstanceScannedByQualysRule started========="); + logger.debug("========ResourceScannedByQualysRule started========="); Annotation annotation = null; String instanceId = null; String severity = ruleParam.get(PacmanRuleConstants.SEVERITY); @@ -85,7 +80,7 @@ public RuleResult execute(final Map ruleParam,Map ruleParam,Map issue = new LinkedHashMap<>(); Gson gson = new Gson(); - if (resourceAttributes != null && PacmanRuleConstants.RUNNING_STATE.equalsIgnoreCase(resourceAttributes.get(PacmanRuleConstants.STATE_NAME))) { - instanceId = StringUtils.trim(resourceAttributes.get(PacmanRuleConstants.INSTANCEID)); + if (resourceAttributes != null && (PacmanRuleConstants.RUNNING_STATE.equalsIgnoreCase(resourceAttributes.get(PacmanRuleConstants.STATE_NAME)) || PacmanRuleConstants.RUNNING_STATE.equalsIgnoreCase(resourceAttributes.get(PacmanRuleConstants.STATUS)))) { + String entityType = resourceAttributes.get(PacmanRuleConstants.ENTITY_TYPE); + instanceId = StringUtils.trim(resourceAttributes.get(PacmanRuleConstants.RESOURCE_ID)); if(PacmanUtils.calculateLaunchedDuration(firstDiscoveredOn)>Long.parseLong(discoveredDaysRange)){ Map ec2ScannesByQualysMap = new HashMap<>(); try{ @@ -119,23 +115,23 @@ public RuleResult execute(final Map ruleParam,Map ruleParam,Map