Skip to content

Commit

Permalink
Merge pull request #35 from ls1intum/feature/wala-implementation
Browse files Browse the repository at this point in the history
`Architectural`: Add Wala Framework Setup
  • Loading branch information
MarkusPaulsen authored Nov 28, 2024
2 parents 951ddd6 + b7ae64b commit e1910f7
Show file tree
Hide file tree
Showing 71 changed files with 3,096 additions and 661 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@
<artifactId>byte-buddy-agent</artifactId>
<version>1.14.19</version>
</dependency>
<dependency>
<groupId>com.ibm.wala</groupId>
<artifactId>com.ibm.wala.core</artifactId>
<version>1.6.7</version>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down Expand Up @@ -282,6 +287,7 @@
</execution>
</executions>
<configuration>
<failOnError>false</failOnError>
<tags>
<tag>
<name>apiNote</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* </p>
*
* @version 2.0.0
* @author Markus Paulsen
* @see <a href="https://refactoring.guru/design-patterns/abstract-factory">Abstract Factory Design Pattern</a>
* @since 2.0.0
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package de.tum.cit.ase.ares.api.architecture;

import com.tngtech.archunit.core.domain.JavaClasses;
import de.tum.cit.ase.ares.api.architecture.java.JavaArchitectureMode;

import javax.annotation.Nonnull;

/**
* Interface for defining architecture security test cases in various programming languages.
* Interface for architecture security test case configurations across various programming languages.
* <p>
* This interface serves as an abstract product in the Abstract Factory Design Pattern,
* outlining the required methods for generating and executing architecture test cases
* tailored to any programming languages.
* This interface serves as the abstract product in the Abstract Factory Design Pattern,
* requiring the implementation of methods for generating and executing architecture test case
* files tailored to any programming languages.
* </p>
*
* @version 2.0.0
Expand All @@ -27,7 +30,7 @@ public interface ArchitectureSecurityTestCase {
*
* @return a {@link String} representing the content of the architecture test case file.
*/
String writeArchitectureTestCase();
@Nonnull String writeArchitectureTestCase(@Nonnull String architectureMode);

/**
* Executes the architecture test case using the provided set of Java classes.
Expand All @@ -37,7 +40,8 @@ public interface ArchitectureSecurityTestCase {
* to handle any language-specific execution details required to run the test case.
* </p>
*
* @param classes a set of {@link JavaClasses} representing the codebase to which the test case will be applied.
* @param architectureMode the {@link JavaArchitectureMode} specifying the architecture testing mode to be used.
*/
void executeArchitectureTestCase(JavaClasses classes);
// TODO: Change this from JavaArchitectureMode architectureMode to @Nonnull String architectureMode
void executeArchitectureTestCase(JavaArchitectureMode architectureMode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package de.tum.cit.ase.ares.api.architecture.java;

//<editor-fold desc="Imports">

import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.core.java11.Java9AnalysisScopeReader;
import com.ibm.wala.ipa.callgraph.*;
import com.ibm.wala.ipa.callgraph.impl.DefaultEntrypoint;
import com.ibm.wala.ipa.callgraph.impl.Util;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.ipa.cha.ClassHierarchyFactory;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeReference;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import de.tum.cit.ase.ares.api.architecture.java.wala.ReachabilityChecker;
import de.tum.cit.ase.ares.api.util.FileTools;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;

import static de.tum.cit.ase.ares.api.aop.java.instrumentation.advice.JavaInstrumentationAdviceToolbox.localize;
//</editor-fold>

/**
* Utility class to build a call graph from a class path.
*/
public class CallGraphBuilderUtils {

private CallGraphBuilderUtils() {
throw new SecurityException(localize("security.general.utility.initialization", CallGraphBuilderUtils.class.getName()));
}

/**
* Class file importer to import the class files.
* This is used to import the class files from the URL.
*/
private static final ClassFileImporter classFileImporter;

private static final ClassHierarchy classHierarchy;

private static final AnalysisScope scope;

static {
try {
// Create a class file importer
classFileImporter = new ClassFileImporter();

// Create an analysis scope
scope = Java9AnalysisScopeReader.instance.makeJavaBinaryAnalysisScope(
System.getProperty("java.class.path"),
// File translates the path name for Windows and Unix
new File("src/main/java/de/tum/cit/ase/ares/api/architecture/java/wala/exclusions.txt")
);

// Build the class hierarchy
classHierarchy = ClassHierarchyFactory.make(scope);
} catch (ClassHierarchyException | IOException e) {
throw new SecurityException(localize("security.architecture.class.hierarchy.error")); // $NON-NLS-1$
}
}


/**
* Try to resolve the class by the given type name.
*
* Ignore jrt URLs as they cause infinite loops and are not needed for the analysis for ArchUnit
*
* @param typeName The type name of the class to resolve.
* @return The resolved class if it exists.
*/
public static Optional<JavaClass> tryResolve(String typeName) {
List<String> ignoredTypeNames = List.of(
// Advice definition uses Reflection and therefor should not be resolved
"de.tum.cit.ase.ares.api.aop.java.aspectj.adviceandpointcut.JavaAspectJFileSystemAdviceDefinitions"
);
// Advice definition uses Reflection and therefor should not be resolved
if (ignoredTypeNames.contains(typeName)) {
return Optional.empty();
}
// TODO: Check if FileTools supports this approach
return Optional.ofNullable(CallGraphBuilderUtils.class.getResource("/" + typeName.replace(".", "/") + ".class"))
.map(location -> classFileImporter
.withImportOption(loc -> !loc.contains("jrt"))
.importUrl(location))
.map(imported -> {
try {
return imported.get(typeName);
} catch (IllegalArgumentException e) {
return null; // Return null so that Optional.empty() is created
}
});
}

/**
* Get the immediate subclasses of the given type name.
*
* @param typeName The type name of the class to get the immediate subclasses.
* @return The immediate subclasses of the given type name.
*/
public static Set<JavaClass> getImmediateSubclasses(String typeName) {
TypeReference reference = TypeReference.find(ClassLoaderReference.Application, convertTypeName(typeName));
if (reference == null) {
return Collections.emptySet();
}
IClass clazz = classHierarchy.lookupClass(reference);
if (clazz == null) {
return Collections.emptySet();
}
return classHierarchy
.getImmediateSubclasses(clazz)
.stream()
.map(IClass::getName)
.map(Object::toString)
.map(CallGraphBuilderUtils::tryResolve)
.filter(Optional::isPresent)
.map(Optional::get).collect(Collectors.toSet());
}

/**
* Convert the type name to the format that can be used in the class file.
*
* @param typeName The type name to convert.
* @return The converted type name.
*/
public static String convertTypeName(String typeName) {
if (typeName == null || typeName.isEmpty()) {
throw new SecurityException(localize("security.architecture.class.type.resolution.error"));
}
return "L" + typeName.replace('.', '/');
}

/**
* Build a call graph from a class path and the passed in predicate
*/
public static CallGraph buildCallGraph(String classPathToAnalyze) {
try {
// Create a list to store entry points
List<DefaultEntrypoint> customEntryPoints = ReachabilityChecker.getEntryPointsFromStudentSubmission(classPathToAnalyze, classHierarchy);

// Create AnalysisOptions for call graph
AnalysisOptions options = new AnalysisOptions(scope, customEntryPoints);

// Create call graph builder (n-CFA, context-sensitive, etc.)
CallGraphBuilder<InstanceKey> builder = Util.makeZeroCFABuilder(Language.JAVA, options, new AnalysisCacheImpl(), classHierarchy);

// Generate the call graph
return builder.makeCallGraph(options, null);
} catch (CallGraphBuilderCancelException e) {
throw new SecurityException(localize("security.architecture.build.call.graph.error")); //$NON-NLS-1$
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package de.tum.cit.ase.ares.api.architecture.java;

import de.tum.cit.ase.ares.api.util.FileTools;

import java.nio.file.Path;

import static de.tum.cit.ase.ares.api.localization.Messages.localized;

/**
* Constants for the paths storing the methods that are not allowed to be used in the Java architecture.
* These methods are used to create the rules for the architecture tests.
* The paths are used to read the methods from the files.
*/
public class FileHandlerConstants {

//<editor-fold desc="Java ArchUnit Methods">
public static final Path ARCHUNIT_FILESYSTEM_INTERACTION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "file-system-access-methods.txt");
public static final Path ARCHUNIT_NETWORK_ACCESS_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "network-access-methods.txt");
public static final Path ARCHUNIT_JVM_TERMINATION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "jvm-termination-methods.txt");
public static final Path ARCHUNIT_REFLECTION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "reflection-methods.txt");
public static final Path ARCHUNIT_COMMAND_EXECUTION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "command-execution-methods.txt");
public static final Path ARCHUNIT_THREAD_MANIPULATION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "thread-manipulation-methods.txt");
public static final Path ARCHUNIT_SERIALIZATION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "serializable-methods.txt");
//</editor-fold>

//<editor-fold desc="WALA Methods">
public static final Path WALA_CLASSLOADER_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "wala", "methods", "classloader.txt");
public static final Path WALA_FILESYSTEM_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "wala", "methods", "file-system-access-methods.txt");
public static final Path WALA_NETWORK_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "wala", "methods", "network-access-methods.txt");
public static final Path WALA_JVM_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "wala", "methods", "jvm-termination-methods.txt");
public static final Path WALA_REFLECTION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "wala", "methods", "reflection-methods.txt");
public static final Path WALA_COMMAND_EXECUTION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "wala", "methods", "command-execution-methods.txt");
public static final Path WALA_SERIALIZATION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "wala", "methods", "serializable-methods.txt");
public static final Path WALA_THREAD_MANIPULATION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "wala", "methods", "thread-manipulation.txt");
//</editor-fold>



private FileHandlerConstants() {
throw new SecurityException(localized("security.general.utility.initialization", FileHandlerConstants.class.getName()));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.tum.cit.ase.ares.api.architecture.java.archunit;
package de.tum.cit.ase.ares.api.architecture.java;

/**
* Supported architecture test cases in Java programming language.
Expand All @@ -7,7 +7,7 @@
* @version 2.0.0
* @since 2.0.0
*/
public enum JavaArchUnitTestCaseSupported {
public enum JavaArchitecturalTestCaseSupported {
/**
* Architecture test case for the file system interaction.
*/
Expand All @@ -27,5 +27,22 @@ public enum JavaArchUnitTestCaseSupported {
/**
* Architecture test case for the package import.
*/
PACKAGE_IMPORT
PACKAGE_IMPORT,
/**
* Architecture test case for the premature jvm termination.
*/
TERMINATE_JVM,
/**
* Architecture test case for the reflection.
*/
REFLECTION,
/**
* Architecture test case for the serialization.
*/
SERIALIZATION,
/**
* Architecture test case for the class loading.
*/
CLASS_LOADING,

}
Loading

0 comments on commit e1910f7

Please sign in to comment.