////////////////////////////////////////////////////////////////////////
//
// 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();
}
}