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.repo;
  
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  import java.util.TreeSet;
  
  import javax.persistence.EntityNotFoundException;
  import javax.persistence.PersistenceException;
  import javax.validation.constraints.NotNull;
  
  import org.eclipse.steady.backend.model.AffectedConstructChange;
  import org.eclipse.steady.backend.model.AffectedLibrary;
  import org.eclipse.steady.backend.model.Bug;
  import org.eclipse.steady.backend.model.ConstructChange;
  import org.eclipse.steady.backend.model.ConstructId;
  import org.eclipse.steady.backend.model.Library;
  import org.eclipse.steady.backend.model.LibraryId;
  import org.eclipse.steady.backend.model.VulnerableDependency;
  import org.eclipse.steady.shared.enums.AffectedVersionSource;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.beans.factory.annotation.Autowired;
  
  /**
   * <p>AffectedLibraryRepositoryImpl class.</p>
   */
  public class AffectedLibraryRepositoryImpl implements AffectedLibraryRepositoryCustom {
  
    private static Logger log = LoggerFactory.getLogger(AffectedLibraryRepositoryImpl.class);
  
    /**
     * Required by {@link AffectedLibraryRepositoryImpl#customSave(Bug)}.
     */
    @Autowired BugRepository bugRepository;
  
    @Autowired ConstructChangeRepository ccRepository;
  
    @Autowired ConstructIdRepository cidRepository;
  
    /**
     * Required by {@link LibraryRepositoryImpl#customSave(Library)}.
     */
    @Autowired LibraryIdRepository libidRepository;
  
    @Autowired LibraryRepository libRepository;
  
    /**
     * Required by {@link LibraryRepositoryImpl#customSave(Library)}.
     */
    @Autowired AffectedLibraryRepository affLibRepository;
  
    @Autowired ApplicationRepository appRepository;
    /**
     * <p>customSave.</p>
     *
     * @return the saved affected libraries
     * @param _bug a {@link org.eclipse.steady.backend.model.Bug} object.
     * @param _aff_libs an array of {@link org.eclipse.steady.backend.model.AffectedLibrary} objects.
     * @throws javax.persistence.PersistenceException if any.
     */
    public List<AffectedLibrary> customSave(Bug _bug, AffectedLibrary[] _aff_libs)
        throws PersistenceException {
  
      AffectedLibrary managed_aff_lib = null;
      List<AffectedLibrary> libs = new ArrayList<AffectedLibrary>();
  
      for (AffectedLibrary provided_aff_lib : _aff_libs) {
  
        // Does it already exist?
        try {
          provided_aff_lib.setBugId(_bug);
          if (provided_aff_lib.getLibraryId() != null) {
            managed_aff_lib =
                AffectedLibraryRepository.FILTER.findOne(
                    this.affLibRepository.findByBugAndLibraryIdAndSource(
                        provided_aff_lib.getBugId(),
                        provided_aff_lib.getLibraryId().getMvnGroup(),
                        provided_aff_lib.getLibraryId().getArtifact(),
                        provided_aff_lib.getLibraryId().getVersion(),
                        provided_aff_lib.getSource()));
  
          } else if (provided_aff_lib.getLib() != null) {
            managed_aff_lib =
                AffectedLibraryRepository.FILTER.findOne(
                    this.affLibRepository.findByBugAndLibAndSource(
                        provided_aff_lib.getBugId(),
                        provided_aff_lib.getLib(),
                        provided_aff_lib.getSource()));
          }
          // (SP, 10.12.2018: in case the assessment flag is equal to the existing, we skip the saving
          // of the affected app and keep the existing one.
          if (provided_aff_lib.getAffected() != null
                  && managed_aff_lib.getAffected() != null
                  && provided_aff_lib.getAffected().equals(managed_aff_lib.getAffected())
              || (provided_aff_lib.getAffected() == null && managed_aff_lib.getAffected() == null))
            continue;
          else provided_aff_lib.setId(managed_aff_lib.getId());
  
          provided_aff_lib.setCreatedAt(managed_aff_lib.getCreatedAt());
        } catch (EntityNotFoundException e1) {
          // Create
          log.info("Creating new affected library  " + provided_aff_lib);
        }
        provided_aff_lib.setModifiedAt(Calendar.getInstance());
  
        // Update refs to independent entities
        provided_aff_lib = this.saveNestedLibraryId(provided_aff_lib);
  
        // Update refs to existing lib (lib must have been analyzed before -> should already exist)
        provided_aff_lib = this.updateNestedLib(provided_aff_lib);
  
        // update refs to construct changes
        provided_aff_lib = this.updateConstructChanges(provided_aff_lib, _bug);
  
        log.debug(provided_aff_lib.toString(true));
  
        // Save
        try {
          managed_aff_lib = this.affLibRepository.save(provided_aff_lib);
          libs.add(managed_aff_lib);
  
          // Update vulnChange timestamp for apps with construct changes among its dependencies'
          // constructs
          // this needs to be done after the bug has been created as we need the construct changes to
          // exist in the database to avoid querying by fields (lang, type, qname)
          appRepository.refreshVulnChangebyAffLib(managed_aff_lib);
  
        } catch (Exception e) {
          throw new PersistenceException(
              "Error while saving lib " + provided_aff_lib + ": " + e.getMessage());
        }
      }
  
      return libs;
    }
  
    private AffectedLibrary saveNestedLibraryId(@NotNull AffectedLibrary _aff_lib) {
      final LibraryId provided_libid = _aff_lib.getLibraryId();
      LibraryId managed_libid = null;
      if (provided_libid != null) {
        try {
          managed_libid =
              LibraryIdRepository.FILTER.findOne(
                  this.libidRepository.findBySecondaryKey(
                      provided_libid.getMvnGroup(),
                      provided_libid.getArtifact(),
                      provided_libid.getVersion()));
        } catch (EntityNotFoundException e) {
          managed_libid = this.libidRepository.save(provided_libid);
        }
        _aff_lib.setLibraryId(managed_libid);
      }
      return _aff_lib;
    }
  
    private AffectedLibrary updateNestedLib(@NotNull AffectedLibrary _aff_lib) {
      final Library provided_lib = _aff_lib.getLib();
      Library managed_lib = null;
      if (provided_lib != null) {
        try {
          managed_lib =
              LibraryRepository.FILTER.findOne(
                  this.libRepository.findByDigest(provided_lib.getDigest()));
  
        } catch (EntityNotFoundException e) {
          throw new PersistenceException(
              "Error while saving lib " + provided_lib + ": " + e.getMessage());
        }
        _aff_lib.setLib(managed_lib);
      }
      return _aff_lib;
    }
  
    private AffectedLibrary updateConstructChanges(
        @NotNull AffectedLibrary _aff_lib, @NotNull Bug _bug) {
      Collection<AffectedConstructChange> aff_cc_list = _aff_lib.getAffectedcc();
      ConstructChange managed_cc = null;
      if (aff_cc_list != null) {
        for (AffectedConstructChange aff_cc : aff_cc_list) {
          // update construct change
          ConstructChange provided_cc = aff_cc.getCc();
          try {
            ConstructId managed_cid =
                ConstructIdRepository.FILTER.findOne(
                    this.cidRepository.findConstructId(
                        provided_cc.getConstructId().getLang(),
                        provided_cc.getConstructId().getType(),
                        provided_cc.getConstructId().getQname()));
            managed_cc =
                ConstructChangeRepository.FILTER.findOne(
                    this.ccRepository.findByRepoPathCommitCidBug(
                        provided_cc.getRepo(),
                        provided_cc.getRepoPath(),
                        provided_cc.getCommit(),
                        managed_cid,
                        _bug));
          } catch (EntityNotFoundException e) {
            throw new PersistenceException(
                "Error while saving affected construct change "
                    + provided_cc
                    + ": "
                    + e.getMessage());
          }
          aff_cc.setCc(managed_cc);
          // update libId same byte code
          Collection<LibraryId> provided_libIds = aff_cc.getSameBytecodeLids();
          Collection<LibraryId> managed_libIds = new ArrayList<LibraryId>();
          LibraryId managed_libid = null;
          if (provided_libIds != null) {
            for (LibraryId l : provided_libIds) {
              try {
                managed_libid =
                    LibraryIdRepository.FILTER.findOne(
                        this.libidRepository.findBySecondaryKey(
                            l.getMvnGroup(), l.getArtifact(), l.getVersion()));
              } catch (EntityNotFoundException e) {
                managed_libid = this.libidRepository.save(l);
              }
              managed_libIds.add(managed_libid);
            }
            aff_cc.setSameBytecodeLids(managed_libIds);
          }
        }
      } else _aff_lib.setAffectedcc(new ArrayList<AffectedConstructChange>());
      return _aff_lib;
    }
  
    /** {@inheritDoc} */
    public void computeAffectedLib(TreeSet<VulnerableDependency> _vdList) {
      for (VulnerableDependency vd : _vdList) {
        this.computeAffectedLib(vd, vd.getDep().getLib());
      }
    }
  
    /** {@inheritDoc} */
    public void computeAffectedLib(VulnerableDependency _vd, Library _lib) {
      // check existence of AffectedLibraries for the outer library in the case of rebundled libs
      // (required to mark FP entries for the outer library)
      Boolean rebundled = (_vd.getDep().getLib().equals(_lib)) ? false : true;
      Boolean avForRebundled = null;
      if (rebundled) {
        avForRebundled =
            this.affLibRepository.isBugLibAffected(
                _vd.getBug().getBugId(), _vd.getDep().getLib().getDigest());
        if (avForRebundled == null && _vd.getDep().getLib().getLibraryId() != null) {
          avForRebundled =
              this.affLibRepository.isBugLibIdAffected(
                  _vd.getBug().getBugId(), _vd.getDep().getLib().getLibraryId());
        }
      }
  
      if (avForRebundled != null) {
        _vd.setAffectedVersion((avForRebundled) ? 1 : 0);
        _vd.setAffectedVersionConfirmed(1);
        return;
      }
  
      // check affected for the actual library containing the vulnerable code
      Boolean avByLib =
          this.affLibRepository.isBugLibAffected(_vd.getBug().getBugId(), _lib.getDigest());
  
      boolean found = false;
      if (avByLib != null) {
        _vd.setAffectedVersion((avByLib) ? 1 : 0);
        _vd.setAffectedVersionConfirmed(1);
        found = true;
      } else if (_lib.getLibraryId() != null) {
        AffectedLibraryRepositoryImpl.log.debug(
            "look for affected for bug ["
                + _vd.getBug().getBugId()
                + "] and lib ["
                + _lib.getLibraryId()
                + "]");
        Boolean avByLibId =
            this.affLibRepository.isBugLibIdAffected(_vd.getBug().getBugId(), _lib.getLibraryId());
        if (avByLibId != null) {
          _vd.setAffectedVersion((avByLibId) ? 1 : 0);
          _vd.setAffectedVersionConfirmed(1);
          found = true;
        }
      }
      if (!found) {
        _vd.setAffectedVersionConfirmed(0);
        _vd.setAffectedVersion(
            1); // when the confirmed flag is 0, the value of affected-version is irrelevant but we
        // set it to 1 so that the UI doesn't filter it out when filtering out historical
        // vulnerabilities
      }
    }
  
    /** {@inheritDoc} */
    public List<AffectedLibrary> getAffectedLibraries(
        Bug _bug, AffectedVersionSource _source, Boolean _onlyWellknown) {
      if (_source == null && !_onlyWellknown) return this.affLibRepository.findByBug(_bug);
      else if (_source != null && !_onlyWellknown)
        return this.affLibRepository.findByBugAndSource(_bug, _source);
      else if (_source != null && _onlyWellknown)
        return this.affLibRepository.findWellKnownByBugAndSource(_bug, _source);
      else return this.affLibRepository.findWellKnownByBug(_bug);
    }
  
    /** {@inheritDoc} */
    public List<AffectedLibrary> getResolvedAffectedLibraries(Bug _bug, Boolean _onlyWellknown) {
      List<AffectedLibrary> al_list = null;
      if (_onlyWellknown) al_list = this.affLibRepository.findWellKnownByBug(_bug);
      else al_list = this.affLibRepository.findByBug(_bug);
      Set<LibraryId> llist = new HashSet<LibraryId>();
      for (AffectedLibrary al : al_list) {
        if (!al.getSource().equals(AffectedVersionSource.TO_REVIEW) && al.getLibraryId() != null) {
          llist.add(al.getLibraryId());
        }
      }
      al_list = new ArrayList<AffectedLibrary>();
      for (LibraryId l : llist) {
        al_list.add(this.affLibRepository.findResolvedAffectedLibrary(_bug.getBugId(), l));
      }
      return al_list;
    }
  }
  