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.kb.task;
  
  import java.io.File;
  import java.io.FileFilter;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.ConcurrentModificationException;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import com.google.gson.JsonSyntaxException;
  import com.jayway.jsonpath.Configuration;
  import com.jayway.jsonpath.JsonPath;
  
  import org.apache.logging.log4j.Logger;
  import org.eclipse.steady.ConstructChange;
  import org.eclipse.steady.backend.BackendConnectionException;
  import org.eclipse.steady.backend.BackendConnector;
  import org.eclipse.steady.kb.command.Command;
  import org.eclipse.steady.kb.model.Commit;
  import org.eclipse.steady.kb.model.Note;
  import org.eclipse.steady.kb.model.Vulnerability;
  import org.eclipse.steady.kb.util.ConstructSet;
  import org.eclipse.steady.kb.util.Metadata;
  import org.eclipse.steady.shared.enums.BugOrigin;
  import org.eclipse.steady.shared.enums.ContentMaturityLevel;
  import org.eclipse.steady.shared.json.JsonBuilder;
  
  import net.minidev.json.JSONObject;
  
  /**
   * <p>ImportVulnerability class.</p>
   */
  public class ImportVulnerability implements Task {
    private static final String OVERWRITE_OPTION = "o";
    private static final String DIRECTORY_OPTION = "d";
    private static final String VERBOSE_OPTION = "v";
  
    private static final Logger log = org.apache.logging.log4j.LogManager.getLogger();
    private BackendConnector backendConnector = null;
  
    /** {@inheritDoc} */
    public void execute(
        Vulnerability vuln, HashMap<String, Object> args, BackendConnector _backendConnector)
        throws BackendConnectionException, JsonSyntaxException, IOException {
      String vulnId = vuln.getVulnId();
      this.backendConnector = _backendConnector;
  
      Boolean overwrite = (Boolean) args.get(OVERWRITE_OPTION);
      if (!overwrite && getBackendConnector().isBugExisting(vulnId)) {
        log.info("Bug [{}] already exists in backend, analysis will be skipped", vulnId);
        return;
      }
  
      List<Commit> commits = new ArrayList<Commit>();
      File file = new File((String) args.get(DIRECTORY_OPTION));
  
      File commitDirs[] =
          file.listFiles(
              new FileFilter() {
                @Override
                public boolean accept(File file) {
                  return file.isDirectory();
                }
              });
  
      for (File commitDir : commitDirs) {
        String dir = commitDir.getAbsolutePath();
        Commit commit = null;
        commit = Metadata.getCommitMetadata(dir);
  
        if (commit != null) {
          commits.add(commit);
        }
      }
  
      Set<ConstructChange> changes = null;
      Map<String, Set<ConstructChange>> allChanges = new HashMap<String, Set<ConstructChange>>();
      for (Commit commit : commits) {
        changes = ConstructSet.identifyConstructChanges(commit, allChanges);
        if ((Boolean) args.get(VERBOSE_OPTION)) {
          for (ConstructChange chg : changes) {
            log.info(chg.toString());
          }
        }
      }
  
      final String json = toJSON(vuln, commits, allChanges);
      _backendConnector.uploadChangeList(vulnId, json);
    }
  
    /**
     * <p>
     * toJSON.
     * </p>
     *
     * @param _vulnerability a {@link org.eclipse.steady.kb.model.Vulnerability} object.
     * @param _commits {@link java.util.List}.
     * @param _allChanges a {@link java.util.Map}.
     * @return a {@link java.lang.String} object.
     */
    private String toJSON(
        Vulnerability _vulnerability,
        List<Commit> _commits,
        Map<String, Set<ConstructChange>> _allChanges)
        throws ConcurrentModificationException {
      final StringBuilder b = new StringBuilder();
      b.append(" { ");
      b.append(" \"bugId\" : \"").append(_vulnerability.getVulnId()).append("\", ");
  
      List<String> aliases = _vulnerability.getAliases();
      if (aliases != null && !aliases.isEmpty()) {
        b.append(" \"bugIdAlt\" : \"");
        boolean isFirstAliase = true;
        for (String aliase : aliases) {
          if (!isFirstAliase) {
            b.append(",");
          }
          isFirstAliase = false;
          b.append(aliase);
        }
  
        b.append("\", ");
      }
  
      b.append(" \"maturity\" : \"" + ContentMaturityLevel.DRAFT.toString() + "\", ");
      b.append(" \"origin\" : \"" + BugOrigin.PUBLIC.toString() + "\", ");
  
      List<Note> notes = _vulnerability.getNotes();
  
      Set<String> links = new HashSet<>();
      Set<String> descriptions = new HashSet<>();
      if (notes != null) {
        for (Note note : notes) {
          if (note.getText() != null && !note.getText().isEmpty()) {
            descriptions.add(JSONObject.escape(note.getText()).trim());
          }
  
          List<String> noteLinks = note.getLinks();
          if (noteLinks != null) links.addAll(noteLinks);
        }
      }
  
      String description = getCVEDescription(_vulnerability);
      // remove NVD description from descriptions from statement
      // write NVD description as description
      if (description != null) {
        description.trim();
        descriptions.remove(JSONObject.escape(description));
        b.append(" \"description\" : \"");
        b.append(JSONObject.escape(description));
        b.append("\" ,");
      }
      // if other descriptions available, put them as Alt
      if (!descriptions.isEmpty()) {
        b.append(" \"descriptionAlt\" : \"");
        int descSize = descriptions.size();
        Object[] descArr = descriptions.toArray();
        b.append(descSize);
        b.append(" descriptions from different sources:");
        for (int descNum = 0; descNum < descArr.length; descNum++) {
          b.append("[").append(descNum).append("] ").append(descArr[descNum]);
        }
        b.append("\" ,");
      }
  
      boolean nextLinks = false;
      if (links != null && !links.isEmpty()) {
        b.append(" \"reference\" : [");
        for (String link : links) {
          if (nextLinks) {
            b.append(",");
          }
          nextLinks = true;
          b.append(JsonBuilder.escape(link));
        }
  
        b.append("], ");
      }
  
      b.append(" \"constructChanges\" : [ ");
      int i = 0;
      final Set<ConstructChange> consol_ch =
          ConstructSet.getConsolidatedChanges(_commits, _allChanges);
      for (ConstructChange c : consol_ch) {
        b.append(c.toJSON());
        if (++i < consol_ch.size()) b.append(", ");
      }
      b.append(" ] } ");
      return b.toString();
    }
  
    private String getCVEDescription(Vulnerability _vulnerability) {
      String cveString;
      String vulnId = _vulnerability.getVulnId();
      try {
        cveString = getBackendConnector().getCVE(vulnId);
      } catch (BackendConnectionException e) {
        log.error("Error connecting to NVD service. {}", e.getCause(), e);
        return null;
      }
  
      /* TODO: check the nvd service. Always sends null even if the service is wrong/down or the CVE passed is wrong */
      if (cveString == null) {
        log.warn("Could not get the CVE description for bugId {}", vulnId);
        return null;
      }
  
      final Configuration conf = Configuration.defaultConfiguration();
      final Object document = conf.jsonProvider().parse(cveString);
  
      final String summary = JsonPath.read(document, "$.summary");
      if (summary == null) {
        log.warn("No description found for [{}]", vulnId);
      }
      return summary;
    }
  
    /** {@inheritDoc} */
    @Override
    public Command.NAME getCommandName() {
      return Command.NAME.IMPORT;
    }
  
    private BackendConnector getBackendConnector() {
      return backendConnector;
    }
  }
  