text: # Copyright 2019 Google LLC
  #
  # 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.
  
  import json
  from datetime import datetime
  from flask import current_app
  
  from data.database import DEFAULT_DATABASE
  from data.models import Vulnerability
  
  db = DEFAULT_DATABASE
  
  
  def getVulnerability(filter_by):
      if not isinstance(filter_by, dict):
          current_app.logger.error("Received invalid filter.")
          return None
  
      if "cve_id" in filter_by:
          vulnerability = Vulnerability.get_by_cve_id(filter_by["cve_id"])
      elif "commit_hash" in filter_by:
          vulnerability = Vulnerability.get_by_commit_hash(
              filter_by["commit_hash"])
      else:
          current_app.logger.error("Invalid filter option received.")
          return None
      return vulnerability
  
  
  class VulnerabilityView:
      def read_data_from_commit(self, vulnerability, master_commit=None):
          if not master_commit:
              return True
  
          if not self.preview:
              for repo_file in master_commit.repository_files:
                  relevant_file_path = "./" + repo_file.file_path
                  self.relevant_files.append(relevant_file_path)
  
          self.annotated = vulnerability.has_annotations
  
          # TODO: Refactor this complete class. It has become quite large and unreadable.
          if not self.preview:
              if master_commit.tree_cache:
                  tree_cache = json.loads(master_commit.tree_cache)
                  if "commit" in tree_cache:
                      commit_data = tree_cache["commit"]
                      self.parent_commit = commit_data["parent_hash"]
                      commit_date = datetime.fromtimestamp(commit_data["date"])
                      master_commit_date = commit_date.strftime("%Y-%m-%d")
                      master_commit_message = commit_data["message"]
                      master_commit_stats = commit_data["stats"]
                      master_commit_files = commit_data["files"]
  
                      # relevant_files_copy = [f for f in self.relevant_files]
                      for patched_files in master_commit_files:
                          relevant_file_path = "./" + patched_files["path"]
                          if relevant_file_path not in self.relevant_files:
                              patch_stats = "(" + patched_files["status"]
                              if patched_files["additions"] > 0:
                                  patch_stats += ", +" + str(
                                      patched_files["additions"])
                              if patched_files["deletions"] > 0:
                                  patch_stats += ", -" + str(
                                      patched_files["deletions"])
                              patch_stats += ")"
                              self.relevant_files.append(relevant_file_path +
                                                         " " + patch_stats)
              for commit in vulnerability.commits:
                  if commit is master_commit:
                      continue
                  self.known_commits.append(commit)
              self.patch_links = [c.commit_link for c in self.known_commits]
          return True
  
      def read_data_from_vulnerability(self, vulnerability=None):
          if vulnerability is None:
              return True
  
          self.id = vulnerability.id
          self.date_created = vulnerability.date_created.strftime("%Y-%m-%d")
          self.vcdb_exists = True
          if vulnerability.comment:
              self.comment = vulnerability.comment
  
          if self.read_data_from_commit(vulnerability,
                                        vulnerability.master_commit):
              self.master_commit = vulnerability.master_commit
  
          return True
  
      def read_data_from_nvd(self, nvd=None):
          if nvd is None:
              return True
  
          # Always set the following attributes.
          self.id = nvd.cve_id
          self.cve_id = nvd.cve_id
          if not self.preview and nvd.cwes:
              self.cwes = nvd.cwes
  
          self.date_created = nvd.published_date.strftime("%Y-%m-%d")
  
          if not self.preview:
              if nvd.has_patch():
                  all_patches = nvd.get_patches()
                  remaining_patches = set(all_patches) - set(self.patch_links)
                  # TODO: Refactor the redundant master commit removal below.
                  if self.master_commit:
                      remaining_patches -= set([self.master_commit.commit_link])
                  self.patch_links += remaining_patches
  
              all_links = nvd.get_links()
              remaining_links = set(all_links) - set(self.patch_links)
              if self.master_commit:
                  remaining_links -= set([self.master_commit.commit_link])
              self.link_references += list(remaining_links)
  
          # Only set if not already set (avoid accidental overwrites).
          if self.comment is None:
              self.comment = nvd.description
          if self.products is None:
              self.products = nvd.get_products()
          if self.score is None:
              self.score = nvd.score
  
          return True
  
      def __init__(self, vulnerability, nvd, preview=False):
          self.vulnerability = None
          self.nvd = None
          self.id = None
          self.cve_id = None
          self.date_created = None
          self.vcdb_exists = False
          self.comment = None
          self.master_commit = None
          self.relevant_files = []
          self.products = None
          self.score = None
          self.cwes = []
          self.known_commits = []
          self.patch_links = []
          self.link_references = []
          self.is_open_source = False
          self.annotated = False
  
          self.parent_commit = None
          self.master_commit_stats = None
          self.master_commit_message = None
          self.master_commit_files = None
          self.master_commit_date = None
  
          self.preview = preview
  
          if self.read_data_from_vulnerability(vulnerability):
              self.vulnerability = vulnerability
          if self.read_data_from_nvd(nvd):
              self.nvd = nvd
  