Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Architectural: Add Wala Framework Setup #35

Merged
merged 48 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d8b74e1
Add Wala Framework Setup
sarpsahinalp Oct 21, 2024
18533d7
Merge branch 'main' into feature/wala-implementation
sarpsahinalp Oct 21, 2024
a0ba1d9
Updated archunit tests
sarpsahinalp Oct 28, 2024
c17e30b
Changes in class resolver
sarpsahinalp Oct 29, 2024
ead7c0c
Updated archunit tests
sarpsahinalp Oct 31, 2024
54da5b5
Fix thread creation stuff for archunit
sarpsahinalp Oct 31, 2024
9e49868
Update archunit and wala implementation
sarpsahinalp Oct 31, 2024
4dd34ef
Improve wala tests
sarpsahinalp Oct 31, 2024
f2c0515
Merge branch 'main' into feature/wala-implementation
Oct 31, 2024
a525684
Feedback addressed coderabbit
sarpsahinalp Oct 31, 2024
6762c4d
Changes
sarpsahinalp Nov 3, 2024
82bf8d6
Changes in class resolver
sarpsahinalp Nov 4, 2024
72cfc94
Refactoring of architectural tests
sarpsahinalp Nov 5, 2024
a0a18f1
Fix failing tests
sarpsahinalp Nov 12, 2024
b60311c
Merge remote-tracking branch 'origin/feature/wala-implementation' int…
sarpsahinalp Nov 12, 2024
e257cc2
Add Wala enum for the architecture mode
sarpsahinalp Nov 19, 2024
ee10233
1. Refactor and add a strategy pattern for dynamic allocation of the …
sarpsahinalp Nov 19, 2024
157e936
1. Implemented the test cases for WalaSecurityTestCases
sarpsahinalp Nov 20, 2024
094935e
1. Add german translation for reasons
sarpsahinalp Nov 21, 2024
fc60a59
Macos specific file methods
sarpsahinalp Nov 24, 2024
69ca695
Add linux methods
sarpsahinalp Nov 24, 2024
2335c97
Merge remote-tracking branch 'origin/feature/wala-implementation' int…
sarpsahinalp Nov 24, 2024
f01a37d
Add windows methods to analyze
sarpsahinalp Nov 24, 2024
6b95748
Add methods for Wala framework
sarpsahinalp Nov 24, 2024
824732a
1. Refactor methods, parse methods for Wala framework
sarpsahinalp Nov 25, 2024
3150e2a
Narrowed structural differences between AOP and Architecture
Nov 26, 2024
26b18a5
Narrowed structural differences between AOP and Architecture
Nov 26, 2024
e76c20f
Update src/main/resources/de/tum/cit/ase/ares/api/localization/messag…
sarpsahinalp Nov 26, 2024
aab978f
Update src/main/resources/de/tum/cit/ase/ares/api/localization/messag…
sarpsahinalp Nov 26, 2024
23cb0b3
Update src/main/java/de/tum/cit/ase/ares/api/architecture/Architectur…
sarpsahinalp Nov 26, 2024
eba498f
Update src/main/java/de/tum/cit/ase/ares/api/architecture/java/wala/J…
sarpsahinalp Nov 26, 2024
3b80a18
Improve error messages
sarpsahinalp Nov 26, 2024
934b429
Merge remote-tracking branch 'origin/feature/wala-implementation' int…
sarpsahinalp Nov 26, 2024
3e29c27
Add german translation
sarpsahinalp Nov 26, 2024
6eb557c
Refactoring
sarpsahinalp Nov 26, 2024
6cb9ec1
Narrowed structural differences between Versions of Architecture tests
Nov 26, 2024
79c9c17
Narrowed structural differences between Versions of Architecture tests
Nov 26, 2024
452dae9
Narrowed structural differences between Versions of Architecture tests
Nov 26, 2024
3ced159
Narrowed structural differences between Versions of Architecture tests
Nov 26, 2024
78d52ca
Narrowed structural differences between Versions of Architecture tests
Nov 26, 2024
4950299
Narrowed structural differences between Versions of Architecture tests
Nov 26, 2024
536233d
Change file constant names
sarpsahinalp Nov 26, 2024
64c9a0c
Add localization in ReachabilityChecker
sarpsahinalp Nov 26, 2024
8b8880f
Change german messages to pass the tests
sarpsahinalp Nov 27, 2024
1aba388
Add comment functionality in text files
sarpsahinalp Nov 27, 2024
10df859
Add comment functionality in text files
sarpsahinalp Nov 27, 2024
013b0e7
Add tests for the wala framework fix changes
sarpsahinalp Nov 27, 2024
b7ae64b
Address the changes!
sarpsahinalp Nov 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,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 @@ -591,7 +596,9 @@
<version>3.3.0</version>
<configuration>
<forkCount>1</forkCount>
<argLine>-javaagent:/home/sarps/IdeaProjects/Ares2/target/ares-2.0.0-SNAPSHOT-agent.jar -Xbootclasspath/a:${user.home}/.m2/repository/org/aspectj/aspectjrt/${aspectj.version}/aspectjrt-${aspectj.version}.jar</argLine>
<argLine>-javaagent:/home/sarps/IdeaProjects/Ares2/target/ares-2.0.0-SNAPSHOT-agent.jar
-Xbootclasspath/a:${user.home}/.m2/repository/org/aspectj/aspectjrt/${aspectj.version}/aspectjrt-${aspectj.version}.jar
</argLine>
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
</configuration>
</plugin>
</plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
package de.tum.cit.ase.ares.api.architecture.java.archunit.postcompile;

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

import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.core.java11.Java9AnalysisScopeReader;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.cha.CHACallGraph;
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 java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
//</editor-fold>

/**
Expand All @@ -23,6 +38,25 @@ private CustomClassResolver() {
*/
private static final ClassFileImporter classFileImporter = new ClassFileImporter();

private static final ClassHierarchy classHierarchy;

static {
try {
AnalysisScope scope = Java9AnalysisScopeReader.instance.makeJavaBinaryAnalysisScope(
System.getProperty("java.class.path"),
null
);
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved

// Build the class hierarchy
long start = System.currentTimeMillis();
classHierarchy = ClassHierarchyFactory.makeWithRoot(scope);
System.out.println("Class hierarchy built in " + (System.currentTimeMillis() - start) + "ms");
} catch (ClassHierarchyException | IOException e) {
throw new SecurityException("Could not create class hierarchy for student submission", e); // $NON-NLS-1$
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
}
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
}


/**
* Try to resolve the class by the given type name.
*
Expand All @@ -44,4 +78,39 @@ public static Optional<JavaClass> tryResolve(String typeName) {
return Optional.empty();
}
}

/**
* Get the immediate subclasses of the given type name.
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
* @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) {
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
TypeReference reference = TypeReference.find(ClassLoaderReference.Application, convertTypeName(typeName));
if (reference == null) {
return Collections.emptySet();
}
IClass clazz = classHierarchy.lookupClass(TypeReference.find(ClassLoaderReference.Application, convertTypeName(typeName)));
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
if (clazz == null) {
return Collections.emptySet();
}
return classHierarchy
.getImmediateSubclasses(clazz)
.stream()
.map(iClass -> tryResolve(iClass.getName().toString()))
.filter(Optional::isPresent)
.map(Optional::get).collect(Collectors.toSet());
}
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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 IllegalArgumentException("Type name cannot be null or empty");
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
}
return "L" + typeName.replace('.', '/');
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public boolean test(JavaAccess<?> javaAccess) {

//<editor-fold desc="Thread Creation related rule">
public static final ArchRule NO_CLASSES_SHOULD_CREATE_THREADS = createNoClassShouldHaveMethodRule(
"creates threads",
"manipulates threads",
FileHandlerConstants.JAVA_THREAD_CREATION_METHODS
);
//</editor-fold>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.tum.cit.ase.ares.api.architecture.java.archunit.postcompile;

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

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaAccess;
Expand All @@ -9,16 +10,18 @@
import com.tngtech.archunit.lang.ConditionEvents;
import com.tngtech.archunit.lang.SimpleConditionEvent;
import com.tngtech.archunit.thirdparty.com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.HashSet;
import java.util.Collections;

import static com.tngtech.archunit.lang.ConditionEvent.createMessage;
import static com.tngtech.archunit.thirdparty.com.google.common.base.Preconditions.checkNotNull;
import static com.tngtech.archunit.thirdparty.com.google.common.collect.Iterables.getLast;
import static de.tum.cit.ase.ares.api.architecture.java.archunit.postcompile.CustomClassResolver.getImmediateSubclasses;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
//</editor-fold>
Expand All @@ -27,6 +30,7 @@
* Checks that a class transitively accesses methods that match a given predicate.
*/
public class TransitivelyAccessesMethodsCondition extends ArchCondition<JavaClass> {
private static final Logger log = LoggerFactory.getLogger(TransitivelyAccessesMethodsCondition.class);
//<editor-fold desc="Attributes">
/**
* Predicate to match the accessed methods
Expand All @@ -40,6 +44,7 @@ public class TransitivelyAccessesMethodsCondition extends ArchCondition<JavaClas
//</editor-fold>

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

/**
* @param conditionPredicate Predicate to match the accessed methods
*/
Expand All @@ -54,17 +59,15 @@ public TransitivelyAccessesMethodsCondition(DescribedPredicate<? super JavaAcces
*/
@Override
public void check(JavaClass item, ConditionEvents events) {
boolean hastTransitiveAccess = false;
long start = System.currentTimeMillis();
for (JavaAccess<?> target : item.getAccessesFromSelf()) {
List<JavaAccess<?>> dependencyPath = transitiveAccessPath.findPathTo(target);
if (!dependencyPath.isEmpty()) {
events.add(newTransitiveAccessPathFoundEvent(target, dependencyPath));
hastTransitiveAccess = true;
}
}
if (!hastTransitiveAccess) {
events.add(newNoTransitiveDependencyPathFoundEvent(item));
}
long end = System.currentTimeMillis();
log.info("Transitive access check took {} ms for class {}", end - start, item.getFullName());
}

/**
Expand All @@ -85,13 +88,6 @@ private static ConditionEvent newTransitiveAccessPathFoundEvent(JavaAccess<?> ja
return SimpleConditionEvent.satisfied(javaClass, createMessage(javaClass, message));
}

/**
* @return a violated event if no transitive dependency path was found
*/
private static ConditionEvent newNoTransitiveDependencyPathFoundEvent(JavaClass javaClass) {
return SimpleConditionEvent.violated(javaClass, createMessage(javaClass, "does not transitively depend on any matching class"));
}

private class TransitiveAccessPath {
/**
* @return some outgoing transitive dependency path to the supplied class or empty if there is none
Expand Down Expand Up @@ -131,26 +127,23 @@ private boolean addAccessesToPathFrom(

/**
* We are currently using CHA, which resolves all the subclasses of a given class
*
* @return all accesses to the same target as the supplied item that are not in the analyzed classes
*/
private Set<JavaAccess<?>> getDirectAccessTargetsOutsideOfAnalyzedClasses(JavaAccess<?> item) {
// Get all subclasses of the target owner including the target owner
JavaClass resolvedTarget = resolveTargetOwner(item.getTargetOwner());

// Match the accesses to the target
Set<JavaClass> subclasses = resolvedTarget.getSubclasses().stream().map(this::resolveTargetOwner).collect(toSet());
subclasses.add(resolvedTarget);

/**
* If the number of subclasses is more than 20, return an empty set.
* These classes are always generic interfaces or abstract classes
* TODO: Check if this is also the case for foreign packages
* We are currently using CHA, which resolves all the subclasses of a given class
* @return all accesses to the same target as the supplied item that are not in the analyzed classes
* Resolves immediate subclasses of the target class to find potential method implementations
* @param item The access to analyze + * @return all accesses to the same target as the supplied item from immediate subclasses
*/
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
if (subclasses.size() > 20 || isExceptionOrError(resolvedTarget)) {
return Collections.emptySet();
}
Set<JavaClass> directSubclasses = new HashSet<>(getImmediateSubclasses(item.getTargetOwner().getFullName()));
directSubclasses.add(resolvedTarget);

return subclasses.stream()
return directSubclasses.stream()
.map(javaClass ->
getAccessesFromClass(javaClass, item.getTarget().getFullName().substring(item.getTargetOwner().getFullName().length())))
.flatMap(Set::stream)
Expand All @@ -172,9 +165,5 @@ private JavaClass resolveTargetOwner(JavaClass targetOwner) {
Optional<JavaClass> resolvedTarget = CustomClassResolver.tryResolve(targetOwner.getFullName());
return resolvedTarget.orElse(targetOwner);
}

private boolean isExceptionOrError(JavaClass javaClass) {
return javaClass.isAssignableTo(Exception.class) || javaClass.isAssignableTo(Error.class);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package de.tum.cit.ase.ares.api.architecture.java.wala;

import com.ibm.wala.classLoader.Language;
import com.ibm.wala.core.java11.Java9AnalysisScopeReader;
import com.ibm.wala.core.util.config.AnalysisScopeReader;
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.shrike.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

public class JavaWalaSecurityTestCase {

private static final Logger log = LoggerFactory.getLogger(JavaWalaSecurityTestCase.class);

public static CallGraph buildCallGraph() {
try {
long startTime = System.currentTimeMillis();

AnalysisScope scope = Java9AnalysisScopeReader.instance.makeJavaBinaryAnalysisScope(
System.getProperty("java.class.path"),
null
);
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved

// Build the class hierarchy
ClassHierarchy cha = ClassHierarchyFactory.makeWithRoot(scope);

cha.getImmediateSubclasses(cha.lookupClass(TypeReference.find(ClassLoaderReference.Application, "java.lang.Object")));

// Create a list to store entry points
List<DefaultEntrypoint> customEntryPoints = ReachabilityChecker.getEntryPointsFromStudentSubmission("target/classes", cha);

sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
// Create AnalysisOptions for call graph
AnalysisOptions options = new AnalysisOptions(scope, customEntryPoints);
options.setTraceStringConstants(false);
options.setHandleZeroLengthArray(false);
options.setUseLexicalScopingForGlobals(false);
options.setUseStacksForLexicalScoping(false);
options.setReflectionOptions(AnalysisOptions.ReflectionOptions.NONE);

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

log.info("Building call graph...");
// Generate the call graph
CallGraph callGraph = builder.makeCallGraph(options, null);
long endTime = System.currentTimeMillis();

log.info("Building call graph took {} ms", endTime - startTime);

List<CGNode> violatingMethods = ReachabilityChecker.isReachable(callGraph, callGraph.getEntrypointNodes().iterator(), node -> node.getMethod().getSignature().startsWith("sun.nio.fs.UnixFileSystemProvider") || node.getMethod().getSignature().startsWith("java.lang.CLassLoader.findResource"));
log.info("Violating methods: {}", violatingMethods);
return callGraph;
} catch (CallGraphBuilderCancelException | ClassHierarchyException | IOException e) {
throw new SecurityException("Error building call graph", e); //$NON-NLS-1$
}
}

// Write the CallGraph to a DOT file
public static void writeCallGraphToDot(CallGraph callGraph, String filePath) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
writer.write("digraph G {\n");

// Iterate through the nodes in the call graph
for (CGNode node : callGraph) {
// Write the node representation
writer.write(" \"" + node.getMethod().getSignature() + "\";\n");

// Iterate through the edges (call sites) from the current node
for (Iterator<CGNode> it = callGraph.getSuccNodes(node); it.hasNext(); ) {
CGNode successor = it.next();
// Write the edge representation
writer.write(" \"" + node.getMethod().getSignature() + "\" -> \"" + successor.getMethod().getSignature() + "\";\n");
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
}
}
writer.write("}\n");
}
}

public static void main(String[] args) throws IOException {
CallGraph callGraph = buildCallGraph();
writeCallGraphToDot(callGraph, "callgraph.dot");
}
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
sarpsahinalp marked this conversation as resolved.
Show resolved Hide resolved
}
Loading