//////////////////////////////////////////////////////////////////////// // // 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.defects; import java.util.List; import java.util.Map; import com.denimgroup.threadfix.data.entities.ChannelType; import com.denimgroup.threadfix.data.entities.Defect; import com.denimgroup.threadfix.data.entities.Finding; import com.denimgroup.threadfix.data.entities.SurfaceLocation; import com.denimgroup.threadfix.data.entities.Vulnerability; import com.denimgroup.threadfix.service.SanitizedLogger; /** * An abstract class providing a base implementation of a defect tracker. This * class should be extended by platform specific trackers. * * @author jraim * @author mcollins * */ public abstract class AbstractDefectTracker { protected String url, username, password, projectName, projectId, lastError; protected final static String LOGIN_FAILURE = "Invalid username / password combination"; protected final static String BAD_CONFIGURATION = "Your configuration is invalid: check your URL."; public final static String INVALID_CERTIFICATE = "The indicated server has an invalid or self-signed certificate."; public final static String BAD_URL = "The defect tracker URL is not valid."; public final static String IO_ERROR = "There were problems communicating with the defect tracker server."; // Common log for all Defect Tracker Exporters. protected final SanitizedLogger log = new SanitizedLogger(this.getClass()); /** * Take information from a list of vulnerabilities and the DefectMetadata bean and * create a Defect in the tracking system. * * @param vulnerabilities * @param metadata * @return the native ID of the new defect. ThreadFix will handle the rest. */ public abstract String createDefect(List<Vulnerability> vulnerabilities, DefectMetadata metadata); /** * Calculate and return the URL for the bug given the bug ID and the endpoint URL. Should be simple. * * @param endpointURL * @param bugID * @return the URL for the bug */ public abstract String getBugURL(String endpointURL, String bugID); /** * * Given a list of defects, check them over and return a map with the defects as keys * and a boolean representing the open status of the defect. To set a more specific open status * for the Defects, use the Defect.setStatus() method. * * TODO possibly re-architect this * * @param defectList * @return A map with keys from the input list and boolean outputs for open status */ public abstract Map<Defect, Boolean> getMultipleDefectStatus(List<Defect> defectList); /** * Return a list of available product names. The credentials and URL need to be set * for this method to work. * * @return a comma separated string of available product names */ public abstract String getProductNames(); /** * Given the name of the project as the projectName field, return its ID. * If the ID is not important, just implement this method and return null. * * @return */ public abstract String getProjectIdByName(); /** * ProjectMetadata is comprised of 5 List<String> objects. * Set as many or as few of them as are required. They are: * statuses, components, severities, versions and priorities. * These choices will be presented to the user and the choices will come back * in the DefectMetadata bean for the createDefect() method. * * @see ProjectMetadata * @return a ProjectMetadata bean */ public abstract ProjectMetadata getProjectMetadata(); /** * This method is called after a failed defect submission in an attempt to try to diagnose errors. * If this functionality is not important, returning a String literal will be fine. * * @return */ public abstract String getTrackerError(); /** * Check the username and password fields against the url field for valid credentials. * * @return */ public abstract boolean hasValidCredentials(); /** * Given a project name, url, and username / password, check the project name. * * @return */ public abstract boolean hasValidProjectName(); /** * Check the URL for validity. * */ public abstract boolean hasValidUrl(); /** * @param vulnerabilities * @param metadata * @return */ protected String makeDescription(List<Vulnerability> vulnerabilities, DefectMetadata metadata) { StringBuilder stringBuilder = new StringBuilder(); String preamble = metadata.getPreamble(); if (preamble != null && !"".equals(preamble)) { stringBuilder.append("General information\n"); stringBuilder.append(preamble); stringBuilder.append('\n'); } int vulnIndex = 0; if (vulnerabilities != null) { for (Vulnerability vulnerability : vulnerabilities) { if (vulnerability.getGenericVulnerability() != null && vulnerability.getSurfaceLocation() != null) { stringBuilder.append("Vulnerability[" + vulnIndex + "]:\n" + vulnerability.getGenericVulnerability().getName() + '\n' + "CWE-ID: " + vulnerability.getGenericVulnerability().getId() + '\n' + "http://cwe.mitre.org/data/definitions/" + vulnerability.getGenericVulnerability().getId() + ".html" + '\n'); SurfaceLocation surfaceLocation = vulnerability.getSurfaceLocation(); stringBuilder.append("Vulnerability attack surface location:\n" + "URL: " + surfaceLocation.getUrl() + "\n" + "Parameter: " + surfaceLocation.getParameter()); addNativeIds(vulnerability, stringBuilder); stringBuilder.append("\n\n"); vulnIndex++; } } } return stringBuilder.toString(); } private void addNativeIds(Vulnerability vulnerability, StringBuilder builder) { List<Finding> findings = vulnerability.getFindings(); if (findings != null && !findings.isEmpty()) { for (Finding finding : findings) { if (finding != null && finding.getScan() != null && finding.getScan().getApplicationChannel() != null && finding.getScan().getApplicationChannel().getChannelType() != null && finding.getScan().getApplicationChannel().getChannelType().getName() != null) { String channelName = finding.getScan().getApplicationChannel().getChannelType().getName(); if (ChannelType.NATIVE_ID_SCANNERS.contains(channelName)) { builder.append("\n" + channelName + " ID: " + finding.getNativeId()); } } } } } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getProjectName() { return projectName; } public void setProjectName(String projectName) { this.projectName = projectName; } public String getProjectId() { return projectId; } public void setProjectId(String projectId) { this.projectId = projectId; } public String getLastError() { return lastError; } public void setLastError(String lastError) { this.lastError = lastError; } }