All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sootup.java.bytecode.inputlocation.PathBasedAnalysisInputLocation Maven / Gradle / Ivy

There is a newer version: 1.3.0
Show newest version
package sootup.java.bytecode.inputlocation;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalNotification;
import com.googlecode.dex2jar.tools.Dex2jarCmd;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.jar.Attributes;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import sootup.core.IdentifierFactory;
import sootup.core.frontend.AbstractClassSource;
import sootup.core.frontend.ClassProvider;
import sootup.core.inputlocation.AnalysisInputLocation;
import sootup.core.inputlocation.FileType;
import sootup.core.model.SourceType;
import sootup.core.types.ClassType;
import sootup.core.util.PathUtils;
import sootup.core.util.StreamUtils;
import sootup.core.views.View;
import sootup.java.bytecode.frontend.AsmJavaClassProvider;
import sootup.java.bytecode.frontend.AsmModuleSource;
import sootup.java.core.JavaModuleIdentifierFactory;
import sootup.java.core.JavaModuleInfo;
import sootup.java.core.JavaSootClass;
import sootup.java.core.ModuleInfoAnalysisInputLocation;
import sootup.java.core.signatures.ModuleSignature;
import sootup.java.core.types.JavaClassType;
import sootup.java.core.types.ModuleJavaClassType;

/*-
 * #%L
 * Soot
 * %%
 * Copyright (C) 2018-2020 Manuel Benz, Christian Brüggemann, Markus Schmidt and others
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

/**
 * Base class for {@link PathBasedAnalysisInputLocation}s that can be located by a {@link Path}
 * object.
 *
 * @author Manuel Benz created on 22.05.18
 * @author Kaustubh Kelkar updated on 30.07.2020
 */
public class PathBasedAnalysisInputLocation implements AnalysisInputLocation {
  protected Path path;

  /**
   * Variable to track if user has specified the SourceType. By default, it will be set to false.
   */
  private SourceType srcType = null;

  /**
   * Variable to store AnalysisInputLocation, which can be DirectoryBasedAnalysisInputLocation,
   * WarArchiveAnalysisInputLocation, MultiReleaseJarAnalysisInputLocation,
   * ArchiveBasedAnalysisInputLocation
   */
  PathBasedAnalysisInputLocation pathBasedAnalysisInputLocationObj;

  public PathBasedAnalysisInputLocation getPathBasedAnalysisInputLocationObj() {
    return pathBasedAnalysisInputLocationObj;
  }

  public PathBasedAnalysisInputLocation(@Nonnull Path path) {
    this.path = path;
  }

  public PathBasedAnalysisInputLocation(@Nonnull Path path, @Nullable SourceType srcType) {
    if (Files.isDirectory(path)) {
      pathBasedAnalysisInputLocationObj = new DirectoryBasedAnalysisInputLocation(path, srcType);
    } else if (PathUtils.isArchive(path)) {

      if (PathUtils.hasExtension(path, FileType.WAR)) {
        pathBasedAnalysisInputLocationObj = new WarArchiveAnalysisInputLocation(path, srcType);
      } else if (isMultiReleaseJar(path)) { // check if mainfest contains multi release flag
        pathBasedAnalysisInputLocationObj = new MultiReleaseJarAnalysisInputLocation(path, srcType);
      } else if (PathUtils.hasExtension(path, FileType.APK)) {
        pathBasedAnalysisInputLocationObj = new ApkAnalysisInputLocation(path, srcType);
      } else {
        pathBasedAnalysisInputLocationObj = new ArchiveBasedAnalysisInputLocation(path, srcType);
      }
    } else {
      throw new IllegalArgumentException(
          "Path '"
              + path.toAbsolutePath()
              + "' has to be pointing to the root of a class container, e.g. directory, jar, zip, apk, war etc.");
    }
  }

  /**
   * The method sets the value of the variable srcType.
   *
   * @param srcType the source type for the path can be Library, Application, Phantom.
   */
  public void setSpecifiedAsBuiltInByUser(@Nonnull SourceType srcType) {
    this.srcType = srcType;
  }

  /**
   * Create or find a class source for a given type.
   *
   * @param type The type of the class to be found.
   * @param view
   * @return The source entry for that class.
   */
  @Nonnull
  @Override
  public Optional> getClassSource(
      @Nonnull ClassType type, @Nonnull View view) {
    return pathBasedAnalysisInputLocationObj.getClassSource(type, view);
  }

  /**
   * Scan the input location and create ClassSources for every compilation / interpretation unit.
   *
   * @param view
   * @return The source entries.
   */
  @Nonnull
  @Override
  public Collection> getClassSources(
      @Nonnull View view) {
    return pathBasedAnalysisInputLocationObj.getClassSources(view);
  }

  @Override
  public SourceType getSourceType() {
    return srcType;
  }

  private static boolean isMultiReleaseJar(Path path) {
    try {
      FileInputStream inputStream = new FileInputStream(path.toFile());
      JarInputStream jarStream = new JarInputStream(inputStream);
      Manifest mf = jarStream.getManifest();

      if (mf == null) {
        return false;
      }

      Attributes attributes = mf.getMainAttributes();

      String value = attributes.getValue("Multi-Release");

      return Boolean.parseBoolean(value);
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

    return false;
  }

  @Nonnull
  Collection> walkDirectory(
      @Nonnull Path dirPath,
      @Nonnull IdentifierFactory factory,
      @Nonnull ClassProvider classProvider) {
    try {
      final FileType handledFileType = classProvider.getHandledFileType();
      final String moduleInfoFilename = JavaModuleIdentifierFactory.MODULE_INFO_FILE + ".class";
      return Files.walk(dirPath)
          .filter(
              filePath ->
                  PathUtils.hasExtension(filePath, handledFileType)
                      && !filePath.toString().endsWith(moduleInfoFilename))
          .flatMap(
              p ->
                  StreamUtils.optionalToStream(
                      Optional.of(
                          classProvider.createClassSource(this, p, factory.fromPath(dirPath, p)))))
          .collect(Collectors.toList());

    } catch (IOException e) {
      throw new IllegalArgumentException(e);
    }
  }

  @Nonnull
  protected Optional> getClassSourceInternal(
      @Nonnull JavaClassType signature,
      @Nonnull Path path,
      @Nonnull ClassProvider classProvider) {

    Path pathToClass =
        path.resolve(
            path.getFileSystem()
                .getPath(
                    signature.getFullyQualifiedName().replace('.', '/')
                        + "."
                        + classProvider.getHandledFileType().getExtension()));

    if (!Files.exists(pathToClass)) {
      return Optional.empty();
    }

    return Optional.of(classProvider.createClassSource(this, pathToClass, signature));
  }

  private static class DirectoryBasedAnalysisInputLocation extends PathBasedAnalysisInputLocation {

    private DirectoryBasedAnalysisInputLocation(@Nonnull Path path, @Nullable SourceType srcType) {
      super(path);
      super.setSpecifiedAsBuiltInByUser(srcType);
    }

    @Override
    @Nonnull
    public Collection> getClassSources(
        @Nonnull View view) {
      return walkDirectory(path, view.getIdentifierFactory(), new AsmJavaClassProvider(view));
    }

    @Override
    @Nonnull
    public Optional> getClassSource(
        @Nonnull ClassType type, @Nonnull View view) {
      return getClassSourceInternal((JavaClassType) type, path, new AsmJavaClassProvider(view));
    }
  }

  public static class MultiReleaseJarAnalysisInputLocation extends ArchiveBasedAnalysisInputLocation
      implements ModuleInfoAnalysisInputLocation {

    @Nonnull private final int[] availableVersions;

    @Nonnull
    private final Map> moduleInfoMap =
        new HashMap<>();

    @Nonnull
    private final Map>> inputLocations =
        new HashMap<>();

    @Nonnull
    private final List> baseInputLocations = new ArrayList<>();

    boolean isResolved = false;

    private MultiReleaseJarAnalysisInputLocation(@Nonnull Path path, @Nullable SourceType srcType) {
      super(path, srcType);

      int[] tmp;
      try {
        FileSystem fs = fileSystemCache.get(path);
        final Path archiveRoot = fs.getPath("/");
        tmp =
            Files.list(archiveRoot.getFileSystem().getPath("/META-INF/versions/"))
                .map(dir -> dir.getFileName().toString().replace("/", ""))
                .mapToInt(Integer::new)
                .sorted()
                .toArray();
      } catch (IOException | ExecutionException e) {
        e.printStackTrace();
        tmp = new int[] {};
      }
      availableVersions = tmp;

      discoverInputLocations(srcType);
    }

    /** Discovers all input locations for different java versions in this multi release jar */
    private void discoverInputLocations(@Nullable SourceType srcType) {
      FileSystem fs = null;
      try {
        fs = fileSystemCache.get(path);
      } catch (ExecutionException e) {
        e.printStackTrace();
      }
      final Path archiveRoot = fs.getPath("/");
      final String moduleInfoFilename = JavaModuleIdentifierFactory.MODULE_INFO_FILE + ".class";

      baseInputLocations.add(new PathBasedAnalysisInputLocation(archiveRoot, srcType));

      String sep = archiveRoot.getFileSystem().getSeparator();

      if (!isResolved) {

        for (int i = availableVersions.length - 1; i >= 0; i--) {
          inputLocations.put(availableVersions[i], new ArrayList<>());

          final Path versionRoot =
              archiveRoot
                  .getFileSystem()
                  .getPath("/META-INF/versions/" + availableVersions[i] + sep);

          // only versions >= 9 support java modules
          if (availableVersions[i] > 8) {
            moduleInfoMap.put(availableVersions[i], new HashMap<>());
            try (DirectoryStream stream = Files.newDirectoryStream(versionRoot)) {
              for (Path entry : stream) {

                Path mi = path.resolve(moduleInfoFilename);

                if (Files.exists(mi)) {
                  JavaModuleInfo moduleInfo = new AsmModuleSource(mi);
                  ModuleSignature moduleSignature = moduleInfo.getModuleSignature();
                  JavaModulePathAnalysisInputLocation inputLocation =
                      new JavaModulePathAnalysisInputLocation(
                          versionRoot.toString(), versionRoot.getFileSystem());

                  inputLocations.get(availableVersions[i]).add(inputLocation);
                  moduleInfoMap.get(availableVersions[i]).put(moduleSignature, moduleInfo);
                }

                if (Files.isDirectory(entry)) {
                  mi = versionRoot.resolve(moduleInfoFilename);

                  if (Files.exists(mi)) {
                    JavaModuleInfo moduleInfo = new AsmModuleSource(mi);
                    ModuleSignature moduleSignature = moduleInfo.getModuleSignature();
                    JavaModulePathAnalysisInputLocation inputLocation =
                        new JavaModulePathAnalysisInputLocation(
                            versionRoot.toString(), versionRoot.getFileSystem());

                    inputLocations.get(availableVersions[i]).add(inputLocation);
                    moduleInfoMap.get(availableVersions[i]).put(moduleSignature, moduleInfo);
                  }
                  // else TODO [bh] can we have automatic modules here?
                }
              }
            } catch (IOException e) {
              e.printStackTrace();
            }
          }

          // if there was no module or the version is not > 8, we just add a directory based input
          // location
          if (inputLocations.get(availableVersions[i]).size() == 0) {
            inputLocations
                .get(availableVersions[i])
                .add(new PathBasedAnalysisInputLocation(versionRoot, srcType));
          }
        }
      }

      isResolved = true;
    }

    @Override
    @Nonnull
    public Optional> getClassSource(
        @Nonnull ClassType type, @Nonnull View view) {

      Collection> il =
          getBestMatchingInputLocationsRaw(view.getProject().getLanguage().getVersion());

      Collection> baseIl = getBaseInputLocations();

      if (type instanceof ModuleJavaClassType) {
        il =
            il.stream()
                .filter(location -> location instanceof ModuleInfoAnalysisInputLocation)
                .collect(Collectors.toList());
        baseIl =
            baseIl.stream()
                .filter(location -> location instanceof ModuleInfoAnalysisInputLocation)
                .collect(Collectors.toList());
      } else {
        il =
            il.stream()
                .filter(location -> !(location instanceof ModuleInfoAnalysisInputLocation))
                .collect(Collectors.toList());
        baseIl =
            baseIl.stream()
                .filter(location -> !(location instanceof ModuleInfoAnalysisInputLocation))
                .collect(Collectors.toList());
      }

      Optional> foundClass =
          il.stream()
              .map(location -> location.getClassSource(type, view))
              .filter(Optional::isPresent)
              .limit(1)
              .map(Optional::get)
              .findAny();

      if (foundClass.isPresent()) {
        return foundClass;
      } else {
        return baseIl.stream()
            .map(location -> location.getClassSource(type, view))
            .filter(Optional::isPresent)
            .limit(1)
            .map(Optional::get)
            .findAny();
      }
    }

    @Nonnull
    @Override
    public Collection> getModulesClassSources(
        @Nonnull ModuleSignature moduleSignature, @Nonnull View view) {
      return inputLocations.get(view.getProject().getLanguage().getVersion()).stream()
          .filter(location -> location instanceof ModuleInfoAnalysisInputLocation)
          .map(
              location ->
                  ((ModuleInfoAnalysisInputLocation) location)
                      .getModulesClassSources(moduleSignature, view))
          .flatMap(Collection::stream)
          .collect(Collectors.toList());
    }

    /**
     * Returns the best matching input locations or the base input location.
     *
     * @param javaVersion version to find best match to
     * @return best match or base input locations
     */
    private Collection> getBestMatchingInputLocationsRaw(
        int javaVersion) {
      for (int i = availableVersions.length - 1; i >= 0; i--) {

        if (availableVersions[i] > javaVersion) continue;

        return new ArrayList<>(inputLocations.get(availableVersions[i]));
      }

      return getBaseInputLocations();
    }

    private Collection> getBaseInputLocations() {
      return baseInputLocations;
    }

    @Override
    @Nonnull
    public Collection> getClassSources(
        @Nonnull View view) {
      Collection> il =
          getBestMatchingInputLocationsRaw(view.getProject().getLanguage().getVersion());

      Collection> result =
          il.stream()
              .map(location -> location.getClassSources(view))
              .flatMap(Collection::stream)
              .collect(Collectors.toList());

      if (il != getBaseInputLocations()) {

        Collection> baseSources =
            getBaseInputLocations().stream()
                .map(location -> location.getClassSources(view))
                .flatMap(Collection::stream)
                .collect(Collectors.toList());

        baseSources.forEach(
            cs -> {
              // do not add duplicate class sources
              if (result.stream()
                  .noneMatch(
                      bestMatchCS ->
                          bestMatchCS
                              .getClassType()
                              .getFullyQualifiedName()
                              .equals(cs.getClassType().getFullyQualifiedName()))) {
                result.add(cs);
              }
            });
      }

      return result;
    }

    @Nonnull
    @Override
    public Optional getModuleInfo(ModuleSignature sig, View view) {
      return Optional.ofNullable(
          moduleInfoMap.get(view.getProject().getLanguage().getVersion()).get(sig));
    }

    @Nonnull
    @Override
    public Set getModules(View view) {
      return inputLocations.get(view.getProject().getLanguage().getVersion()).stream()
          .filter(e -> e instanceof ModuleInfoAnalysisInputLocation)
          .map(e -> ((ModuleInfoAnalysisInputLocation) e).getModules(view))
          .flatMap(Set::stream)
          .collect(Collectors.toSet());
    }

    @Override
    public boolean equals(Object o) {
      if (!(o instanceof PathBasedAnalysisInputLocation)) {
        return false;
      }
      return path.equals(((PathBasedAnalysisInputLocation) o).path);
    }

    @Override
    public int hashCode() {
      return path.hashCode();
    }
  }

  private static class ApkAnalysisInputLocation extends ArchiveBasedAnalysisInputLocation {

    private ApkAnalysisInputLocation(@Nonnull Path path, @Nullable SourceType srcType) {
      super(path, srcType);
      String jarPath = dex2jar(path);
      this.path = Paths.get(jarPath);
    }

    private String dex2jar(Path path) {
      String apkPath = path.toAbsolutePath().toString();
      String outDir = "./tmp/";
      int start = apkPath.lastIndexOf(File.separator);
      int end = apkPath.lastIndexOf(".apk");
      String outputFile = outDir + apkPath.substring(start + 1, end) + ".jar";
      Dex2jarCmd.main("-f", apkPath, "-o", outputFile);
      return outputFile;
    }
  }

  private static class ArchiveBasedAnalysisInputLocation extends PathBasedAnalysisInputLocation {

    // We cache the FileSystem instances as their creation is expensive.
    // The Guava Cache is thread-safe (see JavaDoc of LoadingCache) hence this
    // cache can be safely shared in a static variable.
    protected static final LoadingCache fileSystemCache =
        CacheBuilder.newBuilder()
            .removalListener(
                (RemovalNotification removalNotification) -> {
                  try {
                    removalNotification.getValue().close();
                  } catch (IOException e) {
                    throw new RuntimeException(
                        "Could not close file system of " + removalNotification.getKey(), e);
                  }
                })
            .expireAfterAccess(1, TimeUnit.SECONDS)
            .build(
                CacheLoader.from(
                    path -> {
                      try {
                        return FileSystems.newFileSystem(
                            Objects.requireNonNull(path), (ClassLoader) null);
                      } catch (IOException e) {
                        throw new RuntimeException("Could not open file system of " + path, e);
                      }
                    }));

    private ArchiveBasedAnalysisInputLocation(@Nonnull Path path, @Nullable SourceType srcType) {
      super(path);
      super.setSpecifiedAsBuiltInByUser(srcType);
    }

    @Override
    @Nonnull
    public Optional> getClassSource(
        @Nonnull ClassType type, @Nonnull View view) {
      try {
        FileSystem fs = fileSystemCache.get(path);
        final Path archiveRoot = fs.getPath("/");
        return getClassSourceInternal(
            (JavaClassType) type, archiveRoot, new AsmJavaClassProvider(view));
      } catch (ExecutionException e) {
        throw new RuntimeException("Failed to retrieve file system from cache for " + path, e);
      }
    }

    @Override
    @Nonnull
    public Collection> getClassSources(
        @Nonnull View view) {
      // we don't use the filesystem cache here as it could close the filesystem after the timeout
      // while we are still iterating
      try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) {
        final Path archiveRoot = fs.getPath("/");
        return walkDirectory(
            archiveRoot, view.getProject().getIdentifierFactory(), new AsmJavaClassProvider(view));
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
  }

  private static final class WarArchiveAnalysisInputLocation
      extends DirectoryBasedAnalysisInputLocation {
    public List> containedInputLocations = new ArrayList<>();
    public static int maxAllowedBytesToExtract =
        1024 * 1024 * 500; // limit of extracted file size to protect against archive bombs

    private WarArchiveAnalysisInputLocation(@Nonnull Path warPath, @Nullable SourceType srcType) {
      super(
          Paths.get(
              System.getProperty("java.io.tmpdir")
                  + File.separator
                  + "sootOutput"
                  + "-war"
                  + warPath.hashCode()
                  + "/"),
          srcType);
      extractWarFile(warPath, path);

      Path webInfPath = path.resolve("WEB-INF");
      // directorystructre as specified in SRV.9.5 of
      // https://download.oracle.com/otn-pub/jcp/servlet-2.4-fr-spec-oth-JSpec/servlet-2_4-fr-spec.pdf?AuthParam=1625059899_16c705c72f7db7f85a8a7926558701fe
      Path classDir = webInfPath.resolve("classes");
      if (Files.exists(classDir)) {
        containedInputLocations.add(new DirectoryBasedAnalysisInputLocation(classDir, srcType));
      }

      Path libDir = webInfPath.resolve("lib");
      if (Files.exists(libDir)) {
        try {
          Files.walk(libDir)
              .filter(f -> PathUtils.hasExtension(f, FileType.JAR))
              .forEach(
                  f ->
                      containedInputLocations.add(
                          new ArchiveBasedAnalysisInputLocation(f, srcType)));
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    }

    @Override
    @Nonnull
    public Collection> getClassSources(
        @Nonnull View view) {

      Set> foundClasses = new HashSet<>();

      for (AnalysisInputLocation inputLoc : containedInputLocations) {
        foundClasses.addAll(inputLoc.getClassSources(view));
      }
      return foundClasses;
    }

    @Override
    @Nonnull
    public Optional> getClassSource(
        @Nonnull ClassType type, @Nonnull View view) {

      for (AnalysisInputLocation inputLocation : containedInputLocations) {
        final Optional> classSource =
            inputLocation.getClassSource(type, view);
        if (classSource.isPresent()) {
          return classSource;
        }
      }

      return Optional.empty();
    }

    /**
     * Extracts the war file at the temporary location to analyze underlying class and jar files
     *
     * 

[ms] hint: extracting is necessary to access nested (zip)filesystems with java8/java9 * runtime - nested (zip)filesystems would work with java11 runtime (maybe java10) * * @param warFilePath The path to war file to be extracted */ protected void extractWarFile(Path warFilePath, final Path destDirectory) { int extractedSize = 0; try { File dest = destDirectory.toFile(); if (!dest.exists()) { if (!dest.mkdir()) { throw new RuntimeException( "Could not create the directory to extract Warfile: " + destDirectory); } dest.deleteOnExit(); } ZipInputStream zis = new ZipInputStream(new FileInputStream(warFilePath.toString())); ZipEntry zipEntry; while ((zipEntry = zis.getNextEntry()) != null) { Path filepath = destDirectory.resolve(zipEntry.getName()); final File file = filepath.toFile(); file.deleteOnExit(); if (zipEntry.isDirectory()) { file.mkdir(); } else { byte[] incomingValues = new byte[4096]; int readBytesZip; if (file.exists()) { // compare contents -> does it contain the extracted war already? int readBytesExistingFile; final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); byte[] bisBuf = new byte[4096]; while ((readBytesZip = zis.read(incomingValues)) != -1) { if (extractedSize > maxAllowedBytesToExtract) { throw new RuntimeException( "The extracted warfile exceeds the size of " + maxAllowedBytesToExtract + " byte. Either the file is a big archive (-> increase PathBasedAnalysisInputLocation.WarArchiveInputLocation.maxAllowedBytesToExtract) or maybe it contains an archive bomb."); } readBytesExistingFile = bis.read(bisBuf, 0, readBytesZip); if (readBytesExistingFile != readBytesZip) { throw new RuntimeException( "Can't extract File \"" + file + "\" as it already exists and has a different size."); } else if (!Arrays.equals(bisBuf, incomingValues)) { throw new RuntimeException( "Can't extract File \"" + file + "\" as it already exists and has a different content which we can't override."); } extractedSize += readBytesZip; } } else { BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); while ((readBytesZip = zis.read(incomingValues)) != -1) { if (extractedSize > maxAllowedBytesToExtract) { throw new RuntimeException( "The extracted warfile exceeds the size of " + maxAllowedBytesToExtract + " byte. Either the file is a big archive or maybe it contains an archive bomb."); } bos.write(incomingValues, 0, readBytesZip); extractedSize += readBytesZip; } bos.close(); } } zis.closeEntry(); } } catch (IOException e) { throw new RuntimeException(e); } } /** * Parses the web.xml file to search for the servlet-class classes in the extracted directory * after the war file is extracted * *

[ms] helps to set entrypoints for analyses automatically (later) * * @param extractedWARPath The path where the war file is extracted Adds the classes associated * to servlet-class in a {@link ArrayList} of {@link String} */ @Nonnull public List retrieveServletClasses(String extractedWARPath) { List classesInXML = new ArrayList<>(); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(new File(extractedWARPath + "/WEB-INF/web.xml")); document.getDocumentElement().normalize(); NodeList nList = document.getElementsByTagName("servlet"); for (int temp = 0; temp < nList.getLength(); temp++) { Node node = nList.item(temp); if (node.getNodeType() == Node.ELEMENT_NODE) { Element eElement = (Element) node; classesInXML.add( eElement.getElementsByTagName("servlet-class").item(0).getTextContent()); } } } catch (ParserConfigurationException | SAXException | IOException e) { throw new RuntimeException(e); } return classesInXML; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy