code: /**
   * This file is part of Eclipse Steady.
   *
   * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * SPDX-License-Identifier: Apache-2.0
   * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
   */
  package org.eclipse.steady.backend.rest;
  
  import java.util.List;
  
  import javax.persistence.EntityNotFoundException;
  
  import org.eclipse.steady.backend.cve.Cve;
  import org.eclipse.steady.backend.cve.CveReader2;
  import org.eclipse.steady.backend.model.Bug;
  import org.eclipse.steady.backend.model.view.Views;
  import org.eclipse.steady.backend.repo.BugRepository;
  import org.eclipse.steady.backend.util.ServiceWrapper;
  import org.eclipse.steady.shared.connectivity.ServiceConnectionException;
  import org.eclipse.steady.shared.enums.CoverageStatus;
  import org.eclipse.steady.shared.util.StopWatch;
  import org.eclipse.steady.shared.util.VulasConfiguration;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.http.HttpStatus;
  import org.springframework.http.ResponseEntity;
  import org.springframework.web.bind.annotation.CrossOrigin;
  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.RestController;
  
  import com.fasterxml.jackson.annotation.JsonIgnore;
  import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  import com.fasterxml.jackson.annotation.JsonInclude;
  import com.fasterxml.jackson.annotation.JsonView;
  
  /**
   * RESTful interface for application information.
   */
  @RestController
  @CrossOrigin(origins = "*")
  @RequestMapping(path = "/coverage")
  public class CoverageController {
  
    private static Logger log = LoggerFactory.getLogger(CoverageController.class);
  
    /** Constant <code>BROWSE_ISSUE_URL="vulas.shared.jira.browseIssueUrl"</code> */
    public static final String BROWSE_ISSUE_URL = "vulas.shared.jira.browseIssueUrl";
    /** Constant <code>CREATE_ISSUE_URL="vulas.shared.jira.createIssueUrl"</code> */
    public static final String CREATE_ISSUE_URL = "vulas.shared.jira.createIssueUrl";
    /** Constant <code>PROJECT_ID="vulas.shared.jira.projectId"</code> */
    public static final String PROJECT_ID = "vulas.shared.jira.projectId";
    /** Constant <code>COMPONENT_ID="vulas.shared.jira.componentId"</code> */
    public static final String COMPONENT_ID = "vulas.shared.jira.componentId";
  
    private final BugRepository bugRepository;
  
    @Autowired
    CoverageController(BugRepository bugRepository) {
      this.bugRepository = bugRepository;
    }
  
    /**
     * <p>isCovered.</p>
     *
     * @return 404 {@link HttpStatus#NOT_FOUND} if bug with given bug ID does not exist, 200 {@link HttpStatus#OK} if the bug is found
     * @param bugid a {@link java.lang.String} object.
     */
    @RequestMapping(
        value = "/{bugid}",
        method = RequestMethod.GET,
        produces = {"application/json;charset=UTF-8"})
    @JsonView(Views.BugDetails.class)
    public ResponseEntity<CoverageStatusResponse> isCovered(@PathVariable String bugid) {
      final String bugid_uc = bugid.toUpperCase();
      final StopWatch sw = new StopWatch("Check coverage of bug [" + bugid_uc + "]").start();
  
      final String browse_issue =
          VulasConfiguration.getGlobal().getConfiguration().getString(BROWSE_ISSUE_URL, null);
      final String create_issue =
          VulasConfiguration.getGlobal().getConfiguration().getString(CREATE_ISSUE_URL, null);
  
      // Create response and set basic info
      final CoverageStatusResponse response = new CoverageStatusResponse(bugid_uc);
  
      // Check whether the bug is already in the database
      try {
        final List<Bug> bugs = this.bugRepository.findCoverageByBugId(bugid_uc);
        sw.lap("Completed database query for bug [" + bugid_uc + "]", true);
        final Bug b = BugRepository.FILTER.findOne(bugs);
  
        // It is, let's fill the response fields
        response.status = CoverageStatus.COVERED;
        response.setDescription(b.getDescription());
        response.setActionUrl(
            VulasConfiguration.getGlobal()
                .getConfiguration()
                .getString(VulasConfiguration.HOMEPAGE, "n/a"));
        response.setActionText(
            "Learn how to detect vulnerable open-source software in your application");
      } catch (EntityNotFoundException enfe) {
        try {
  
          final Cve cve = CveReader2.read(bugid_uc);
          if (cve != null) response.setDescription(cve.getSummary());
  
          // It is not in the database, let's ask Jira whether there's already a ticket
          final JiraSearchResponse jira_search_response =
              ServiceWrapper.getInstance().searchJira(bugid_uc);
          sw.lap("Completed Jira query", true);
  
          // Found in Jira
          if (jira_search_response != null && jira_search_response.getTotal() > 0) {
            final JiraIssue issue = jira_search_response.getIssues()[0];
  
            // Set link to Jira ticket (if possible)
            if (browse_issue != null && !browse_issue.equals("")) {
              response.setActionUrl(browse_issue + issue.getKey());
              response.setActionText(
                  "See Jira ticket "
                      + issue.getKey()
                      + " (status: "
                      + issue.getFields().getStatus().getName()
                      + ").");
            }
  
            // Out of scope
            if (issue.getFields().getStatus().getName().equals("Closed")) {
              response.status = CoverageStatus.OUT_OF_SCOPE;
            }
            // In discussion
            else {
              response.status = CoverageStatus.OPEN;
            }
          }
          // All other cases
          else {
            response.status = CoverageStatus.UNKNOWN;
            if (create_issue != null && !create_issue.equals("")) {
              response.setActionUrl(create_issue + bugid_uc);
              response.setActionText(
                  "Create a Jira ticket if this CVE affects an open-source software component"
                      + " developed in Java or Python.");
            }
          }
  
        } catch (ServiceConnectionException e) {
          response.status = CoverageStatus.ERROR;
          response.setErrorText(e.getLocalizedMessage());
          response.setActionUrl(null);
          response.setActionText(null);
        } catch (Exception e) {
          sw.stop(e);
          return new ResponseEntity<CoverageStatusResponse>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
      } catch (Exception e) {
        sw.stop(e);
        return new ResponseEntity<CoverageStatusResponse>(HttpStatus.INTERNAL_SERVER_ERROR);
      }
  
      sw.stop();
      return new ResponseEntity<CoverageStatusResponse>(response, HttpStatus.OK);
    }
  
    @JsonInclude(JsonInclude.Include.ALWAYS)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class JiraSearchResponse {
      private int total;
      private JiraIssue[] issues;
  
      public int getTotal() {
        return total;
      }
  
      public void setTotal(int total) {
        this.total = total;
      }
  
      public JiraIssue[] getIssues() {
        return issues.clone();
      }
  
      public void setIssues(JiraIssue[] issues) {
        this.issues = issues.clone();
      }
    }
  
    @JsonInclude(JsonInclude.Include.ALWAYS)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class JiraIssue {
      private int id;
      private String key;
      private JiraIssueFields fields;
  
      public int getId() {
        return id;
      }
  
      public void setId(int id) {
        this.id = id;
      }
  
      public String getKey() {
        return key;
      }
  
      public void setKey(String key) {
        this.key = key;
      }
  
      public JiraIssueFields getFields() {
        return fields;
      }
  
      public void setFields(JiraIssueFields fields) {
        this.fields = fields;
      }
    }
  
    @JsonInclude(JsonInclude.Include.ALWAYS)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class JiraIssueFields {
      private String summary;
      private JiraIssueStatus status;
  
      public String getSummary() {
        return summary;
      }
  
      public void setSummary(String summary) {
        this.summary = summary;
      }
  
      public JiraIssueStatus getStatus() {
        return status;
      }
  
      public void setStatus(JiraIssueStatus status) {
        this.status = status;
      }
    }
  
    @JsonInclude(JsonInclude.Include.ALWAYS)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class JiraIssueStatus {
      private String name;
  
      public String getName() {
        return name;
      }
  
      public void setName(String name) {
        this.name = name;
      }
    }
  
    @JsonInclude(JsonInclude.Include.ALWAYS)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class CoverageStatusResponse {
      private String bug;
      private String description;
      private CoverageStatus status;
      private String actionUrl;
      private String actionText;
      private String errorText;
  
      public CoverageStatusResponse(String bug) {
        super();
        this.bug = bug;
      }
  
      public String getBug() {
        return bug;
      }
  
      public void setBug(String bug) {
        this.bug = bug;
      }
  
      public String getDescription() {
        return description;
      }
  
      public void setDescription(String description) {
        this.description = description;
      }
  
      @JsonIgnore
      public CoverageStatus getStatus() {
        return status;
      }
  
      public void setStatus(CoverageStatus status) {
        this.status = status;
      }
  
      public byte getStatusCode() {
        return this.status.getStatusCode();
      }
  
      public String getStatusText() {
        return this.status.getText();
      }
  
      public String getStatusDescription() {
        return this.status.getDescription();
      }
  
      public String getActionUrl() {
        return actionUrl;
      }
  
      public void setActionUrl(String actionUrl) {
        this.actionUrl = actionUrl;
      }
  
      public String getActionText() {
        return actionText;
      }
  
      public void setActionText(String actionText) {
        this.actionText = actionText;
      }
  
      public String getErrorText() {
        return errorText;
      }
  
      public void setErrorText(String errorText) {
        this.errorText = errorText;
      }
  
      @Override
      public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((bug == null) ? 0 : bug.hashCode());
        result = prime * result + ((status == null) ? 0 : status.hashCode());
        return result;
      }
  
      @Override
      public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        CoverageStatusResponse other = (CoverageStatusResponse) obj;
        if (bug == null) {
          if (other.bug != null) return false;
        } else if (!bug.equals(other.bug)) return false;
        if (status != other.status) return false;
        return true;
      }
    }
  }
  