//////////////////////////////////////////////////////////////////////// // // 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.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.denimgroup.threadfix.data.dao.DefectDao; import com.denimgroup.threadfix.data.dao.VulnerabilityDao; import com.denimgroup.threadfix.data.entities.Application; import com.denimgroup.threadfix.data.entities.Defect; import com.denimgroup.threadfix.data.entities.Vulnerability; import com.denimgroup.threadfix.service.defects.AbstractDefectTracker; import com.denimgroup.threadfix.service.defects.DefectMetadata; import com.denimgroup.threadfix.service.defects.DefectTrackerFactory; @Service @Transactional(readOnly = false) public class DefectServiceImpl implements DefectService { private DefectDao defectDao = null; private VulnerabilityDao vulnerabilityDao = null; private ApplicationService applicationService = null; private final SanitizedLogger log = new SanitizedLogger(DefectService.class); @Autowired public DefectServiceImpl(DefectDao defectDao, VulnerabilityDao vulnerabilityDao, ApplicationService applicationService) { this.defectDao = defectDao; this.vulnerabilityDao = vulnerabilityDao; this.applicationService = applicationService; } @Override public List<Defect> loadAll() { return defectDao.retrieveAll(); } @Override public Defect loadDefect(int defectId) { return defectDao.retrieveById(defectId); } @Override public Defect loadDefect(String nativeId) { return defectDao.retrieveByNativeId(nativeId); } @Override @Transactional(readOnly = false) public void storeDefect(Defect defect) { defectDao.saveOrUpdate(defect); } @Override @Transactional(readOnly = false) public Defect createDefect(List<Vulnerability> allVulns, String summary, String preamble, String component, String version, String severity, String priority, String status) { if (allVulns == null || allVulns.size() == 0 || allVulns.get(0) == null || allVulns.get(0).getApplication() == null) { log.warn("Null input, exiting."); return null; } Vulnerability vuln = allVulns.get(0); Application application = vuln.getApplication(); if (application != null) { applicationService.decryptCredentials(application); } AbstractDefectTracker dt = DefectTrackerFactory.getTracker(application); if (dt == null) { log.warn("Unable to load Defect Tracker."); return null; } String editedSummary = summary, editedPreamble = preamble; // TODO handle error cases better. if (editedSummary == null || editedSummary.equals("")) { if (vuln.getGenericVulnerability() != null && vuln.getSurfaceLocation() != null) { editedSummary = createMessage(vuln); } else { editedSummary = "No editedSummary could be parsed."; } } if (editedPreamble == null || editedPreamble.equals("")) { if (vuln.getGenericVulnerability() != null && vuln.getSurfaceLocation() != null) { editedPreamble = createMessage(vuln); } else { editedPreamble = "No editedPreamble could be parsed."; } } List<Vulnerability> vulnsWithoutDefects = new ArrayList<Vulnerability>(); for (Vulnerability vulnerability : allVulns) if (vulnerability.getDefect() == null) vulnsWithoutDefects.add(vulnerability); if (vulnsWithoutDefects.size() == 0) { log.warn("All the vulnerabilities already had defects, exiting."); return null; } String defectTrackerName = null; if (application != null && application.getDefectTracker() != null && application.getDefectTracker().getDefectTrackerType() != null && application.getDefectTracker().getDefectTrackerType().getName() != null) defectTrackerName = application.getDefectTracker().getDefectTrackerType().getName(); if (defectTrackerName != null) log.info("About to submit a defect to " + defectTrackerName + "."); else log.info("About to submit a defect to the defect tracker."); String defectId = dt.createDefect(vulnsWithoutDefects, new DefectMetadata(editedSummary, editedPreamble, component, version, severity, priority, status)); if (defectId != null) { Defect defect = new Defect(); defect.setNativeId(defectId); defect.setVulnerabilities(vulnsWithoutDefects); defect.setApplication(application); defect.setStatus(status); defect.setDefectURL(dt.getBugURL( application.getDefectTracker().getUrl(), defectId)); defectDao.saveOrUpdate(defect); for (Vulnerability vulnerability : vulnsWithoutDefects) { vulnerability.setDefect(defect); vulnerability.setDefectSubmittedTime(Calendar.getInstance()); vulnerabilityDao.saveOrUpdate(vulnerability); } if (defectTrackerName != null) log.info("Successfully submitted defect to " + defectTrackerName + "."); else log.info("Successfully submitted defect."); return defect; } if (defectTrackerName != null) log.warn("There was an error submitting the defect to " + defectTrackerName + "."); else log.warn("There was an error submitting the defect."); return null; } private String createMessage(Vulnerability vuln) { if (vuln.getGenericVulnerability() != null && vuln.getSurfaceLocation() != null) { return vuln.getGenericVulnerability().getName() + " at " + vuln.getSurfaceLocation().getPath(); } else { return ""; } } // TODO make these error messages better @Override public String getErrorMessage(List<Vulnerability> vulns) { String noVulnsError = "No vulnerabilities were passed."; String noDefectTrackerError = "No defect tracker could be found - " + "check to see that you have entered your information."; String allVulnsAlreadyInSystem = "All the vulnerabilities were already in the system."; String defaultTrackerError = "There was an error connecting with the tracking system."; if (vulns == null || vulns.size() == 0) return noVulnsError; Vulnerability vuln = vulns.get(0); if (vuln == null || vuln.getApplication() == null) return noDefectTrackerError; Application application = vuln.getApplication(); if (application != null) { applicationService.decryptCredentials(application); } AbstractDefectTracker dt = DefectTrackerFactory.getTracker(application); if (dt == null) return noDefectTrackerError; List<Vulnerability> vulnList = new ArrayList<Vulnerability>(); for (Vulnerability vulnerability : vulns) if (vulnerability.getDefect() == null) vulnList.add(vulnerability); if (vulnList.size() == 0) return allVulnsAlreadyInSystem; String trackerError = dt.getTrackerError(); if (trackerError == null || trackerError.trim().equals("")) { return defaultTrackerError; } else { return trackerError; } } @Override @Transactional(readOnly = false) public void updateVulnsFromDefectTracker(Application application) { int numUpdated = 0; if (application == null) { log.warn("Application wasn't found, exiting."); return; } if (application != null) { applicationService.decryptCredentials(application); } AbstractDefectTracker dt = DefectTrackerFactory.getTracker(application); if (dt == null) { log.warn("Unable to load Defect Tracker, exiting."); return; } if (application.getDefectList() == null || application.getDefectList().size() == 0) { log.warn("No Defects found, updating information is " + "only useful after creating Defects. Exiting."); return; } Map<Defect, Boolean> defectMap = dt.getMultipleDefectStatus( application.getDefectList()); if (defectMap == null) { log.warn("There was an error retrieving information from the " + "Defect Tracker, exiting."); return; } log.info("About to update vulnerability information from the defect tracker."); for (Defect defect : defectMap.keySet()) { if (defect != null && defect.getVulnerabilities() != null && defectMap.containsKey(defect)) { for (Vulnerability vuln : defect.getVulnerabilities()) { Boolean defectOpenStatus = defectMap.get(defect); if (vuln.isActive() && defectOpenStatus != null && !defectOpenStatus.booleanValue()) { if (vuln.getDefectClosedTime() == null) { vuln.setDefectClosedTime(Calendar.getInstance()); vulnerabilityDao.saveOrUpdate(vuln); numUpdated += 1; } } } } } if (numUpdated == 0) { log.info("No vulnerabilities were updated. " + "This could just mean that no issues were closed."); } else { log.info("Updated information for " + numUpdated + " vulnerabilities."); } } @Override public void deleteByDefectTrackerId(Integer defectTrackerId) { log.info("Deleting Defects connected to the Defect Tracker with the ID " + defectTrackerId); defectDao.deleteByDefectTrackerId(defectTrackerId); } @Override public void deleteByApplicationId(Integer applicationId) { log.info("Deleting Defects connected to the Application with the ID " + applicationId); defectDao.deleteByApplicationId(applicationId); } }