//////////////////////////////////////////////////////////////////////// // // Copyright (c) 2009-2013 Denim Group, Ltd. // // The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations // under the License. // // The Original Code is ThreadFix. // // The Initial Developer of the Original Code is Denim Group, Ltd. // Portions created by Denim Group, Ltd. are Copyright (C) // Denim Group, Ltd. All Rights Reserved. // // Contributor(s): Denim Group, Ltd. // //////////////////////////////////////////////////////////////////////// package com.denimgroup.threadfix.service; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.denimgroup.threadfix.data.dao.VulnerabilityDao; import com.denimgroup.threadfix.data.entities.Application; import com.denimgroup.threadfix.data.entities.Finding; import com.denimgroup.threadfix.data.entities.SurfaceLocation; import com.denimgroup.threadfix.data.entities.Vulnerability; @Service @Transactional(readOnly = true) public class VulnerabilityServiceImpl implements VulnerabilityService { private final SanitizedLogger log = new SanitizedLogger(VulnerabilityService.class); private VulnerabilityDao vulnerabilityDao = null; @Autowired public VulnerabilityServiceImpl(VulnerabilityDao vulnerabilityDao) { this.vulnerabilityDao = vulnerabilityDao; } @Override public Vulnerability loadVulnerability(int vulnerabilityId) { return vulnerabilityDao.retrieveById(vulnerabilityId); } @Override public List<Vulnerability> loadOpenVulnerabilities(Application application) { return vulnerabilityDao.retrieveAllActiveByApplication(application.getId()); } @Override @Transactional(readOnly = false) public void markListAsFalsePositive(List<Integer> vulnerabilityIdList) { markVulnListFalsePositiveValue(vulnerabilityIdList, true); } @Override @Transactional(readOnly = false) public void markListAsNotFalsePositive(List<Integer> vulnerabilityIdList) { markVulnListFalsePositiveValue(vulnerabilityIdList, false); } private void markVulnListFalsePositiveValue(List<Integer> vulnerabilityIdList, boolean falsePositiveValue) { if (vulnerabilityIdList == null || vulnerabilityIdList.size() == 0) return; List<Vulnerability> vulns = loadVulnerabilityList(vulnerabilityIdList); if (vulns == null || vulns.size() == 0) { log.warn("No vulns specified to mark."); return; } if (falsePositiveValue) log.info("About to mark " + vulns.size() + " Vulnerabilities as false positives."); else log.info("About to mark " + vulns.size() + " Vulnerabilities as not false positives."); int count = 0; for (Vulnerability vuln : vulns) { if (vuln != null) { count++; vuln.setIsFalsePositive(falsePositiveValue); storeVulnerability(vuln); } } if (count > 0) { String vulnText = " Vulnerabilities"; if (count == 1) vulnText = " Vulnerability"; if (falsePositiveValue) { log.info("Marked " + count + vulnText + " as false positives."); } else { log.info("Marked " + count + vulnText + " as not false positives."); } } else { log.warn("Failed to mark any vulnerabilities as false positives."); } } @Override public List<Vulnerability> loadSimilarVulnerabilities(Vulnerability vuln) { List<Vulnerability> vulnList = vulnerabilityDao.retrieveSimilarHashes(vuln); simplifyFindings(vulnList); return vulnList; } @Override public void simplifyFindings(List<Vulnerability> vulnList) { Finding finding = null; List<Finding> findingList = null; for (Vulnerability vuln : vulnList) { finding = new Finding(); finding.setSurfaceLocation(vuln.getSurfaceLocation()); findingList = new ArrayList<Finding>(); findingList.add(finding); vuln.setFindings(findingList); } } @Override public List<Vulnerability> loadAllByGenericVulnerabilityAndApp(Vulnerability vulnerability) { List<Vulnerability> vulnList = vulnerabilityDao .retrieveAllByGenericVulnerabilityAndApp(vulnerability); simplifyFindings(vulnList); return vulnList; } @Override public List<Vulnerability> loadVulnerabilityList(List<Integer> vulnerabilities) { List<Vulnerability> vulnList = new ArrayList<Vulnerability>(); Vulnerability vuln = null; for (Integer id : vulnerabilities) { vuln = vulnerabilityDao.retrieveById(id); if (vuln != null) { vulnList.add(vuln); } } return vulnList; } @Override @Transactional(readOnly = false) public void storeVulnerability(Vulnerability vulnerability) { vulnerabilityDao.saveOrUpdate(vulnerability); } // returns time differences (in days) in this order: // Opened, WAF rule generated, submitted to Defect Tracker, marked closed by // Defect Tracker, and found closed by scan @Override public String[] getTimeDifferences(Vulnerability vulnerability) { if (vulnerability == null) return null; String[] strArray = new String[5]; strArray[0] = "0"; strArray[1] = daysBetween(vulnerability.getOpenTime(), vulnerability.getWafRuleGeneratedTime()); strArray[2] = daysBetween(vulnerability.getOpenTime(), vulnerability.getDefectSubmittedTime()); strArray[3] = daysBetween(vulnerability.getOpenTime(), vulnerability.getDefectClosedTime()); strArray[4] = daysBetween(vulnerability.getOpenTime(), vulnerability.getCloseTime()); return strArray; } /** * TODO time this here and in the database and decide which to use */ @Override public String[] getAges(List<Vulnerability> vulnerabilities) { if (vulnerabilities == null || vulnerabilities.size() == 0) { return new String[]{}; } Calendar now = Calendar.getInstance(); String[] ages = new String[vulnerabilities.size()]; for (int index = 0; index < vulnerabilities.size(); index++) { if (vulnerabilities.get(index) == null) { // we should never get here ages[index] = "0"; } else { ages[index] = daysBetween(vulnerabilities.get(0).getOpenTime(), now); } } return ages; } // For now, return the days between two Calendar objects as a String to // simplify passing it to the jsp. private String daysBetween(Calendar startDate, Calendar endDate) { if (startDate == null || endDate == null) return ""; Calendar date = (Calendar) startDate.clone(); Long daysBetween = (long) 0; while (date.before(endDate)) { date.add(Calendar.DAY_OF_MONTH, 1); daysBetween++; } return daysBetween.toString(); } public SurfaceLocation getSurfaceLocationFromDynamicFinding(Vulnerability vulnerability) { if (vulnerability == null || vulnerability.getFindings() == null) return null; for (Finding finding : vulnerability.getFindings()) if (finding != null && !finding.getIsStatic() && finding.getSurfaceLocation() != null) return finding.getSurfaceLocation(); return null; } public List<Finding> getStaticFindings(Vulnerability vulnerability) { if (vulnerability == null || vulnerability.getFindings() == null) return null; List<Finding> staticFindingList = new ArrayList<Finding>(); for (Finding finding : vulnerability.getFindings()) { if (finding != null && finding.getIsStatic()) { if (finding.getDataFlowElements() != null && finding.getDataFlowElements().size() != 0) { Collections.sort(finding.getDataFlowElements()); staticFindingList.add(finding); } } } return staticFindingList; } @Override public List<Vulnerability> getFalsePositiveVulns(Application application) { return getVulnsFromAppWithFalsePositiveValue(application, true); } @Override public List<Vulnerability> getNonFalsePositiveVulns(Application application) { return getVulnsFromAppWithFalsePositiveValue(application, false); } private List<Vulnerability> getVulnsFromAppWithFalsePositiveValue(Application application, boolean value) { return vulnerabilityDao.getFalsePositiveVulnCount(application,value); } @Override @Transactional(readOnly=false) public void closeAll(List<Integer> vulnerabilityIds) { vulnerabilityDao.markAllClosed(vulnerabilityIds); } @Override @Transactional(readOnly=false) public void openAll(List<Integer> vulnerabilityIds) { vulnerabilityDao.markAllOpen(vulnerabilityIds); } @Override public boolean activeVulnerabilitiesExist() { return vulnerabilityDao.activeVulnerabilitiesExist(); } }