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.patcheval;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.logging.log4j.Logger;
  import org.eclipse.steady.backend.BackendConnector;
  import org.eclipse.steady.java.bytecode.ConstructBytecodeASTManager;
  import org.eclipse.steady.java.sign.ASTSignatureChange;
  import org.eclipse.steady.java.sign.gson.ASTSignatureChangeDeserializer;
  import org.eclipse.steady.patcheval.representation.ArtifactResult2;
  import org.eclipse.steady.patcheval.representation.ConstructPathAssessment2;
  import org.eclipse.steady.patcheval.utils.CSVHelper2;
  import org.eclipse.steady.python.sign.PythonConstructDigest;
  import org.eclipse.steady.python.sign.PythonConstructDigestDeserializer;
  import org.eclipse.steady.shared.enums.ProgrammingLanguage;
  import org.eclipse.steady.shared.json.JacksonUtil;
  import org.eclipse.steady.shared.json.model.LibraryId;
  
  import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
  
  /**
   * <p>ByteCodeComparator class.</p>
   */
  public class ByteCodeComparator implements Runnable {
  
    private static final Logger log = org.apache.logging.log4j.LogManager.getLogger();
  
    private final ArtifactResult2 ar;
    Map<Class<?>, StdDeserializer<?>> custom_deserializers =
        new HashMap<Class<?>, StdDeserializer<?>>();
    String bugId;
  
    /**
     * <p>Constructor for ByteCodeComparator.</p>
     *
     * @param ar a {@link org.eclipse.steady.patcheval.representation.ArtifactResult2} object.
     * @param _b a {@link java.lang.String} object.
     */
    public ByteCodeComparator(ArtifactResult2 ar, String _b) {
      this.ar = ar;
      this.bugId = _b;
      custom_deserializers.put(ASTSignatureChange.class, new ASTSignatureChangeDeserializer());
      custom_deserializers.put(PythonConstructDigest.class, new PythonConstructDigestDeserializer());
    }
  
    /** {@inheritDoc} */
    @Override
    public void run() {
  
      Integer done_comparisons = 0;
  
      boolean toRewrite = false;
      for (ConstructPathAssessment2 cpa2 : ar.getConstructPathAssessments()) {
        done_comparisons = 0;
        if (cpa2.getQnameInJar()
            && BugLibManager.bytecodes.containsKey(cpa2.getConstruct().concat(cpa2.getPath()))) {
          // first check that the construct is not in a nested classes otherwise the bytecode
          // comparison does not work
          // because the decompiler  in use (Procyon) does not keep the outer class in the decompiled
          // inner class
          // (i.e., the decompiled ClassA$ClassB.class contains public static class ClassB instead of
          // public static class ClassA$ClassB)
          // right now we check if a construct is in a nested class in the most dumb way: if the
          // string contains $
          if (!cpa2.getConstruct().contains("$")) {
  
            ConstructBytecodeASTManager mgr =
                BugLibManager.bytecodes.get(cpa2.getConstruct().concat(cpa2.getPath()));
  
            // only compare if the number of comparisons tobedone is > than the one performed in the
            // previous run (which was stored in the csv)
            // this is done to avoid that comparisons are done for every run even if no equalities in
            // the bytecode is found
            // the check on the cardinality is an approx, however the number of sources equalities
            // should never decreases thus this check seems adeguate
            Integer comparisonsToBeDone = mgr.getLidsSize();
  
            if (cpa2.getConstructType() != null
                && (cpa2.getDoneComparisons() == null
                    || !cpa2.getDoneComparisons().equals(comparisonsToBeDone))) {
  
              log.info(
                  "["
                      + comparisonsToBeDone
                      + "] comparisons to be done for construct path "
                      + cpa2.getConstruct()
                      + " : "
                      + cpa2.getPath()
                      + " in archive ["
                      + ar.toString()
                      + "]");
              List<LibraryId> vList = new ArrayList<LibraryId>();
              List<LibraryId> fList = new ArrayList<LibraryId>();
  
              // retrieve the bytecode of the currently analyzed library
              String ast_current =
                  BackendConnector.getInstance()
                      .getAstForQnameInLib(
                          null,
                          ar.getGroup()
                              + "/"
                              + ar.getArtifact()
                              + "/"
                              + ar.getVersion()
                              + "/"
                              + cpa2.getConstructType().toString()
                              + "/"
                              + cpa2.getConstruct(),
                          false,
                          ProgrammingLanguage.JAVA);
  
              if (ast_current != null) {
  
                // libid whose source was found equal to vuln
                for (LibraryId l : mgr.getVulnLids()) {
  
                  log.info(
                      "Artifact ["
                          + ar.toString()
                          + "] vuln comparison number ["
                          + done_comparisons
                          + "/"
                          + comparisonsToBeDone
                          + "]");
                  // check if it is equal to bytecode of each c,p in lid found equal to vuln
  
                  // retrieve bytecode of the known to be vulnerable library
                  String ast_lid = mgr.getVulnAst(l);
  
                  if (ast_lid != null) {
                    // check if the ast's diff is empty
  
                    String body = "[" + ast_lid + "," + ast_current + "]";
                    String editV = BackendConnector.getInstance().getAstDiff(null, body);
                    ASTSignatureChange scV =
                        (ASTSignatureChange)
                            JacksonUtil.asObject(
                                editV, custom_deserializers, ASTSignatureChange.class);
                    /* */
                    done_comparisons++;
                    toRewrite = true;
                    log.info(
                        "size to vulnerable lib "
                            + l.toString()
                            + " is ["
                            + scV.getModifications().size()
                            + "]");
                    if (scV.getModifications().size() == 0) {
  
                      // check that there isn't also a construct = to vuln
  
                      log.info(
                          "LID ["
                              + ar.toString()
                              + "] equal to vuln based on AST bytecode comparison with  ["
                              + l.toString()
                              + "]");
                      // cpa2.addLibsSameBytecode(l);
                      vList.add(l);
                    }
                  }
                }
  
                // libid whose source was found equal to fix
                for (LibraryId l : mgr.getFixedLids()) {
  
                  log.info(
                      "Artifact ["
                          + ar.toString()
                          + "] fixed comparison number ["
                          + done_comparisons
                          + "/"
                          + comparisonsToBeDone
                          + "]");
  
                  // retrieve bytecode of the known to be vulnerable library
                  String ast_lid = mgr.getFixedAst(l);
  
                  if (ast_lid != null) {
                    // check if the ast's diff is empty
  
                    String body = "[" + ast_lid + "," + ast_current + "]";
                    String editV = BackendConnector.getInstance().getAstDiff(null, body);
                    ASTSignatureChange scV =
                        (ASTSignatureChange)
                            JacksonUtil.asObject(
                                editV, custom_deserializers, ASTSignatureChange.class);
                    /* */
                    done_comparisons++;
                    toRewrite = true;
                    log.info(
                        "size to fixed lib "
                            + l.toString()
                            + " is ["
                            + scV.getModifications().size()
                            + "]");
                    if (scV.getModifications().size() == 0) {
  
                      log.info(
                          "LID ["
                              + ar.toString()
                              + "] equal to fix based on AST bytecode comparison with  ["
                              + l.toString()
                              + "]");
                      //	cpa2.addLibsSameBytecode(l);
                      fList.add(l);
                    }
                  }
                }
  
                cpa2.setDoneComparisons(done_comparisons);
                if (vList.size() > 0 && !(fList.size() > 0)) {
                  cpa2.setLibsSameBytecode(vList);
                  cpa2.setdToV(0);
                } else if (fList.size() > 0 && !(vList.size() > 0)) {
                  cpa2.setLibsSameBytecode(fList);
                  cpa2.setdToF(0);
                } else if (vList.size() > 0 && fList.size() > 0) {
                  log.info("found equalities both to vuln and fixed!");
                } else if (vList.size() == 0 && fList.size() == 0) {
                  log.info("no equalities found");
                }
              }
            }
          }
        }
      }
      if (toRewrite) {
        CSVHelper2.rewriteLineInCSV(bugId, ar);
      }
    }
  }
  