package com.denimgroup.threadfix.service.report;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRField;
import com.denimgroup.threadfix.data.dao.VulnerabilityDao;
import com.denimgroup.threadfix.data.entities.Vulnerability;
public class JasperCWEReport implements JRDataSource {
private List<Vulnerability> vulnerabilityList = new ArrayList<Vulnerability>();
private int index = 0;
private Map<String, Object> resultsHash = new HashMap<String, Object>();
private List<Map<String, Object>> listOfMaps = null;
public JasperCWEReport(List<Integer> applicationIdList, VulnerabilityDao vulnerabilityDao) {
if (vulnerabilityDao != null && applicationIdList != null)
this.vulnerabilityList = vulnerabilityDao.retrieveByApplicationIdList(applicationIdList);
listOfMaps = buildGenericVulnerabilityInformationArray();
index = -1;
}
@Override
public Object getFieldValue(JRField field) {
if (field == null) return null;
String name = field.getName();
if (name == null) return null;
if (resultsHash != null && resultsHash.containsKey(name))
return resultsHash.get(name);
else
return null;
}
@Override
public boolean next() {
if (listOfMaps != null && index < listOfMaps.size() - 1) {
if (index == -1) {
index = 0;
} else {
index++;
}
resultsHash = listOfMaps.get(index);
return true;
} else {
return false;
}
}
private List<Map<String, Object>> buildGenericVulnerabilityInformationArray() {
if (vulnerabilityList == null || vulnerabilityList.size() == 0)
return null;
// First we need to marshal the data from vulnerabilities into groups by generic vulnerability
Map<String, Map<String, Integer>> statsMap = new HashMap<String, Map<String, Integer>>();
Calendar now = Calendar.getInstance();
for (Vulnerability vulnerability : vulnerabilityList) {
if (vulnerability == null || vulnerability.getGenericVulnerability() == null ||
vulnerability.getGenericVulnerability().getName() == null || vulnerability.getIsFalsePositive())
continue;
String key = vulnerability.getGenericVulnerability().getName();
// initialize the generic vuln
if (!statsMap.containsKey(key)) {
statsMap.put(key, new HashMap<String, Integer>());
statsMap.get(key).put("numOpen", 0);
statsMap.get(key).put("numClosed", 0);
statsMap.get(key).put("totalAgeOpen", 0);
statsMap.get(key).put("totalTimeToClose", 0);
}
// bump up the correct stats
if (vulnerability.isActive()) {
statsMap.get(key).put("numOpen", statsMap.get(key).get("numOpen")+1);
statsMap.get(key).put("totalAgeOpen", statsMap.get(key).get("totalAgeOpen") + dateDiffInDays(vulnerability.getOpenTime(), now));
} else {
statsMap.get(key).put("numClosed", statsMap.get(key).get("numClosed")+1);
statsMap.get(key).put("totalTimeToClose", statsMap.get(key).get("totalTimeToClose") + dateDiffInDays(vulnerability.getOpenTime(), vulnerability.getCloseTime()));
}
}
// Then, to sort, we need a grouping of total vulns with all the associated vuln names
Map<Integer, List<String>> sortingHash = new HashMap<Integer, List<String>>();
for (String key : statsMap.keySet()) {
Integer total = statsMap.get(key).get("numOpen") + statsMap.get(key).get("numClosed");
if (sortingHash.get(total) == null)
sortingHash.put(total, new ArrayList<String>());
sortingHash.get(total).add(key);
}
List<Map<String, Object>> returnList = new ArrayList<Map<String, Object>>();
// moving to a SortedSet allows ordered addition by total number of vulnerabilities.
SortedSet<Integer> sortedSet = new TreeSet<Integer>();
sortedSet.addAll(sortingHash.keySet());
// then iterate through the original set of generic vulnerability data
// and calculate the correct figures.
for (Integer sortingHashKey : sortedSet) {
for (String statsMapKey : sortingHash.get(sortingHashKey)) {
Map<String, Object> genericVulnEntry = new HashMap<String,Object>();
genericVulnEntry.put("description", statsMapKey);
genericVulnEntry.put("total", Long.valueOf(sortingHashKey));
Map<String, Integer> statsMapEntry = statsMap.get(statsMapKey);
if (sortingHashKey == 0) {
genericVulnEntry.put("percentClosed", Long.valueOf(100));
} else {
Long percentClosed = (long) (100.0 * ((double)statsMapEntry.get("numClosed") / (double)sortingHashKey));
genericVulnEntry.put("percentClosed", percentClosed);
}
if (statsMapEntry.get("numOpen") == 0) {
genericVulnEntry.put("averageAgeOpen", Long.valueOf(0));
} else {
Long averageAgeOpen = (long) (statsMapEntry.get("totalAgeOpen") / statsMapEntry.get("numOpen"));
genericVulnEntry.put("averageAgeOpen",averageAgeOpen);
}
if (statsMapEntry.get("numClosed") == 0) {
genericVulnEntry.put("averageTimeToClose", Long.valueOf(0));
} else {
Long averageTimeToClose = (long) (statsMapEntry.get("totalTimeToClose") / statsMapEntry.get("numClosed"));
genericVulnEntry.put("averageTimeToClose",averageTimeToClose);
}
// the 0 as the first argument bumps all the other items up one index.
returnList.add(0,genericVulnEntry);
}
}
return returnList;
}
private Integer dateDiffInDays(Calendar firstDate, Calendar secondDate) {
if (firstDate == null || secondDate == null)
return 0;
Calendar earlierDate = null, laterDate = null;
if (firstDate.compareTo(secondDate) == 0) {
return 0;
} else if (firstDate.compareTo(secondDate) < 0) {
earlierDate = firstDate;
laterDate = secondDate;
} else {
earlierDate = secondDate;
laterDate = firstDate;
}
Integer days = 0;
earlierDate.add(Calendar.DAY_OF_MONTH, 1);
while (earlierDate.compareTo(laterDate) < 0) {
days += 1;
earlierDate.add(Calendar.DAY_OF_MONTH, 1);
}
return days;
}
}