////////////////////////////////////////////////////////////////////////
//
// 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.webapp.controller;
import java.util.Calendar;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.denimgroup.threadfix.data.entities.Application;
import com.denimgroup.threadfix.data.entities.Finding;
import com.denimgroup.threadfix.data.entities.Permission;
import com.denimgroup.threadfix.data.entities.SurfaceLocation;
import com.denimgroup.threadfix.data.entities.Vulnerability;
import com.denimgroup.threadfix.data.entities.VulnerabilityComment;
import com.denimgroup.threadfix.service.ApplicationService;
import com.denimgroup.threadfix.service.PermissionService;
import com.denimgroup.threadfix.service.SanitizedLogger;
import com.denimgroup.threadfix.service.VulnerabilityCommentService;
import com.denimgroup.threadfix.service.VulnerabilityService;
@Controller
@RequestMapping("/organizations/{orgId}/applications/{appId}/vulnerabilities")
public class VulnerabilityController {
private VulnerabilityService vulnerabilityService;
private ApplicationService applicationService;
private VulnerabilityCommentService vulnerabilityCommentService;
private PermissionService permissionService;
private final SanitizedLogger log = new SanitizedLogger(VulnerabilityController.class);
@Autowired
public VulnerabilityController(PermissionService permissionService,
ApplicationService applicationService,
VulnerabilityService vulnerabilityService,
VulnerabilityCommentService vulnerabilityCommentService) {
this.vulnerabilityService = vulnerabilityService;
this.vulnerabilityCommentService = vulnerabilityCommentService;
this.permissionService = permissionService;
this.applicationService = applicationService;
}
public VulnerabilityController(){}
// switched to not show our guess as to static surfaceLocations - only show
// surfaceLocation for dynamic scans.
@RequestMapping(value = "/{vulnerabilityId}", method = RequestMethod.GET)
public String detail(@PathVariable("appId") int appId, @PathVariable("orgId") int orgId,
@PathVariable("vulnerabilityId") int vulnerabilityId, Model model) {
if (!permissionService.isAuthorized(Permission.READ_ACCESS,orgId,appId)) {
return "403";
}
Vulnerability vulnerability = vulnerabilityService.loadVulnerability(vulnerabilityId);
checkResourceNotFound(vulnerability, vulnerabilityId, appId);
String[] times = vulnerabilityService.getTimeDifferences(vulnerability);
SurfaceLocation surfaceLocation = vulnerabilityService
.getSurfaceLocationFromDynamicFinding(vulnerability);
List<Finding> staticFindingList = vulnerabilityService.getStaticFindings(vulnerability);
// if there's only one static finding, display its data flow elements.
Finding singleStaticFinding = null;
if (staticFindingList != null && staticFindingList.size() == 1) {
singleStaticFinding = staticFindingList.get(0);
staticFindingList = null;
}
permissionService.addPermissions(model, orgId, appId, Permission.CAN_MODIFY_VULNERABILITIES);
List<VulnerabilityComment> comments = vulnerabilityCommentService.loadAllForVuln(vulnerabilityId);
model.addAttribute("singleStaticFinding", singleStaticFinding);
model.addAttribute("comments", comments);
model.addAttribute("staticFindingList", staticFindingList);
model.addAttribute("surfaceLocation", surfaceLocation);
model.addAttribute("vulnerability", vulnerability);
model.addAttribute("timeArray", times);
return "applications/vulnerability";
}
// switched to not show our guess as to static surfaceLocations - only show
// surfaceLocation for dynamic scans.
@RequestMapping(value = "/{vulnerabilityId}/addComment", method = RequestMethod.POST)
public String addComment(@PathVariable("appId") int appId, @PathVariable("orgId") int orgId,
@RequestParam("comments") String comments, Model model,
@PathVariable("vulnerabilityId") int vulnerabilityId) {
Vulnerability vulnerability = vulnerabilityService.loadVulnerability(vulnerabilityId);
checkResourceNotFound(vulnerability, vulnerabilityId, appId);
String result = vulnerabilityCommentService.addCommentToVuln(comments, vulnerabilityId);
if (result == null) result = "The submitted comment was invalid.";
model.addAttribute("vulnerability", vulnerability);
if (!result.equals(VulnerabilityCommentService.VALID)) {
model.addAttribute("commentError", result);
model.addAttribute("contentPage", "applications/forms/vulnCommentForm.jsp");
return "ajaxFailureHarness";
}
List<VulnerabilityComment> commentList = vulnerabilityCommentService.loadAllForVuln(vulnerabilityId);
model.addAttribute("comments", commentList);
model.addAttribute("contentPage", "applications/vulnComments.jsp");
return "ajaxSuccessHarness";
}
@RequestMapping(value = "/{vulnerabilityId}/defect", method = RequestMethod.GET)
public String viewDefect(@PathVariable("appId") int appId, @PathVariable("orgId") int orgId,
@PathVariable("vulnerabilityId") int vulnerabilityId, ModelMap model) {
if (!permissionService.isAuthorized(Permission.READ_ACCESS,orgId,appId)) {
return "403";
}
Vulnerability vulnerability = vulnerabilityService.loadVulnerability(vulnerabilityId);
checkResourceNotFound(vulnerability, vulnerabilityId, appId);
if (vulnerability.getDefect() == null) {
log.warn("The requested Vulnerability did not have an associated Defect, returning to the Vulnerability page.");
return "redirect:/organizations/" + orgId + "/applications/" + appId + "/vulnerabilities/" + vulnerabilityId;
}
model.addAttribute("defect", vulnerability.getDefect());
return "applications/defects";
}
@RequestMapping(value = "/{vulnerabilityId}/close", method = RequestMethod.GET)
public String closeVulnerability(@PathVariable("vulnerabilityId") int vulnerabilityId,
@PathVariable("appId") int appId, @PathVariable("orgId") int orgId, ModelMap model) {
if (!permissionService.isAuthorized(Permission.CAN_MODIFY_VULNERABILITIES, orgId, appId)) {
return "403";
}
Vulnerability vulnerability = vulnerabilityService.loadVulnerability(vulnerabilityId);
checkResourceNotFound(vulnerability, vulnerabilityId, appId);
vulnerability.closeVulnerability(null, Calendar.getInstance());
vulnerability.setFoundByScanner(false);
vulnerabilityService.storeVulnerability(vulnerability);
return "redirect:/organizations/" + orgId + "/applications/" + appId + "/vulnerabilities/" + vulnerabilityId;
}
@RequestMapping(value = "/{vulnerabilityId}/open", method = RequestMethod.GET)
public String openVulnerability(@PathVariable("vulnerabilityId") int vulnerabilityId,
@PathVariable("appId") int appId, @PathVariable("orgId") int orgId, ModelMap model) {
if (!permissionService.isAuthorized(Permission.CAN_MODIFY_VULNERABILITIES, orgId, appId)) {
return "403";
}
Vulnerability vulnerability = vulnerabilityService.loadVulnerability(vulnerabilityId);
checkResourceNotFound(vulnerability, vulnerabilityId, appId);
vulnerability.setActive(true);
vulnerabilityService.storeVulnerability(vulnerability);
return "redirect:/organizations/" + orgId + "/applications/" + appId + "/vulnerabilities/" + vulnerabilityId;
}
@RequestMapping(value = "/{vulnerabilityId}/markFalsePositive", method = RequestMethod.GET)
public String markFalsePositive(@PathVariable("vulnerabilityId") int vulnerabilityId,
@PathVariable("appId") int appId, @PathVariable("orgId") int orgId, ModelMap model) {
if (!permissionService.isAuthorized(Permission.CAN_MODIFY_VULNERABILITIES, orgId, appId)) {
return "403";
}
Vulnerability vulnerability = vulnerabilityService.loadVulnerability(vulnerabilityId);
checkResourceNotFound(vulnerability, vulnerabilityId, appId);
vulnerability.setIsFalsePositive(true);
vulnerabilityService.storeVulnerability(vulnerability);
return "redirect:/organizations/" + orgId + "/applications/" + appId + "/vulnerabilities/" + vulnerabilityId;
}
@RequestMapping(value = "/{vulnerabilityId}/markNotFalsePositive", method = RequestMethod.GET)
public String markNotFalsePositive(@PathVariable("vulnerabilityId") int vulnerabilityId,
@PathVariable("appId") int appId, @PathVariable("orgId") int orgId, ModelMap model) {
if (!permissionService.isAuthorized(Permission.CAN_MODIFY_VULNERABILITIES, orgId, appId)) {
return "403";
}
Vulnerability vulnerability = vulnerabilityService.loadVulnerability(vulnerabilityId);
checkResourceNotFound(vulnerability, vulnerabilityId, appId);
vulnerability.setIsFalsePositive(false);
vulnerabilityService.storeVulnerability(vulnerability);
return "redirect:/organizations/" + orgId + "/applications/" + appId + "/vulnerabilities/" + vulnerabilityId;
}
@RequestMapping(value = "/{vulnerabilityId}/mergeFindings", method = RequestMethod.GET)
public String mergeFinding(@PathVariable("vulnerabilityId") int vulnerabilityId, ModelMap model,
@PathVariable("appId") int appId, @PathVariable("orgId") int orgId) {
if (!permissionService.isAuthorized(Permission.CAN_MODIFY_VULNERABILITIES, orgId, appId)) {
return "403";
}
Vulnerability vulnerability = vulnerabilityService.loadVulnerability(vulnerabilityId);
checkResourceNotFound(vulnerability, vulnerabilityId, appId);
if (vulnerability.getFindings() != null
&& vulnerability.getFindings().size() != 0
&& vulnerability.getFindings().size() != 1) {
List<Finding> findings = vulnerability.getFindings();
model.addAttribute(findings);
}
model.addAttribute(vulnerability);
return "/applications/vulnerability";
}
private void checkResourceNotFound(Vulnerability vuln, int vulnId, int appId) {
Application application = applicationService.loadApplication(appId);
if (vuln == null || application == null || !application.isActive()) {
log.warn(ResourceNotFoundException.getLogMessage("Vulnerability", vulnId));
throw new ResourceNotFoundException();
}
}
}