Skip to content

Commit 24457a3

Browse files
Merge pull request #41 from ls1intum/chore/fixes-testing-session
Improved the performance of the Wala framework and added caching mech…
2 parents a3e384d + 3aee418 commit 24457a3

File tree

38 files changed

+595
-578
lines changed

38 files changed

+595
-578
lines changed

.github/workflows/maven.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Checkout Repository
1919
uses: actions/checkout@v4
2020

21-
- name: Set up JDK 22
21+
- name: Set up JDK 21
2222
uses: actions/setup-java@v4
2323
with:
2424
java-version: '21'
@@ -34,4 +34,4 @@ jobs:
3434
run: mvn clean package -DskipTests
3535

3636
- name: Test
37-
run: mvn test
37+
run: mvn test -f pom.xml

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>de.tum.cit.ase</groupId>
66
<artifactId>ares</artifactId>
7-
<version>2.0.0-Beta-2</version>
7+
<version>2.0.0-Beta-4</version>
88
<properties>
99
<maven.compiler.source>21</maven.compiler.source>
1010
<arch.unit.version>1.3.0</arch.unit.version>

pom123.xml

10 Bytes
Binary file not shown.

src/main/java/de/tum/cit/ase/ares/api/aop/java/aspectj/adviceandpointcut/JavaAspectJFileSystemAdviceDefinitions.aj

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.aspectj.lang.JoinPoint;
55
import java.io.File;
66
import java.lang.reflect.Field;
77
import java.lang.reflect.InaccessibleObjectException;
8+
import java.nio.file.Files;
89
import java.nio.file.InvalidPathException;
910
import java.nio.file.Path;
1011

@@ -204,7 +205,7 @@ public aspect JavaAspectJFileSystemAdviceDefinitions {
204205
String[] allowedPaths = (String[]) getValueFromSettings(
205206
switch (action) {
206207
case "read" -> "pathsAllowedToBeRead";
207-
case "write" -> "pathsAllowedToBeOverwritten";
208+
case "overwrite" -> "pathsAllowedToBeOverwritten";
208209
case "execute" -> "pathsAllowedToBeExecuted";
209210
case "delete" -> "pathsAllowedToBeDeleted";
210211
default -> throw new IllegalArgumentException("Unknown action: " + action);
@@ -249,7 +250,7 @@ public aspect JavaAspectJFileSystemAdviceDefinitions {
249250
de.tum.cit.ase.ares.api.aop.java.aspectj.adviceandpointcut.JavaAspectJFileSystemPointcutDefinitions.fileSystemProviderWriteMethods() ||
250251
de.tum.cit.ase.ares.api.aop.java.aspectj.adviceandpointcut.JavaAspectJFileSystemPointcutDefinitions.printWriterInitMethods() ||
251252
de.tum.cit.ase.ares.api.aop.java.aspectj.adviceandpointcut.JavaAspectJFileSystemPointcutDefinitions.desktopExecuteMethods() {
252-
checkFileSystemInteraction("write", thisJoinPoint);
253+
checkFileSystemInteraction("overwrite", thisJoinPoint);
253254
}
254255

255256
before():

src/main/java/de/tum/cit/ase/ares/api/aop/java/aspectj/adviceandpointcut/JavaAspectJFileSystemPointcutDefinitions.aj

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public aspect JavaAspectJFileSystemPointcutDefinitions {
77
pointcut fileReadMethods():
88
(call(* java.io.File.canRead(..)) ||
99
call(java.io.File.new(..)) ||
10+
call(java.io.RandomAccessFile.new(..)) ||
1011
call(* java.io.File.exists(..)) ||
1112
call(* java.io.File.getFreeSpace(..)) ||
1213
call(* java.io.File.getTotalSpace(..)) ||
@@ -21,6 +22,7 @@ public aspect JavaAspectJFileSystemPointcutDefinitions {
2122

2223
pointcut fileWriteMethods():
2324
(call(* java.io.File.canWrite(..)) ||
25+
call(java.io.RandomAccessFile.new(..)) ||
2426
call(* java.io.File.createNewFile(..)) ||
2527
call(* java.io.File.createTempFile(..)) ||
2628
call(* java.io.File.setExecutable(..)) ||

src/main/java/de/tum/cit/ase/ares/api/aop/java/instrumentation/advice/JavaInstrumentationAdviceToolbox.java

+17-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import java.lang.reflect.InaccessibleObjectException;
66
import java.lang.reflect.InvocationTargetException;
77
import java.lang.reflect.Method;
8+
import java.nio.file.Files;
89
import java.nio.file.InvalidPathException;
910
import java.nio.file.Path;
11+
import java.util.List;
1012

1113
/**
1214
* Utility class for the Java instrumentation advice.
@@ -20,6 +22,9 @@
2022
* enforce security policies at runtime and block unauthorized file interactions.
2123
*/
2224
public class JavaInstrumentationAdviceToolbox {
25+
private static List<String> fileSystemIgnoreAttributes = List.of("java.io.File.delete");
26+
private static List<String> fileSystemIgnoreParameter = List.of();
27+
2328
//<editor-fold desc="Constructor">
2429

2530
/**
@@ -144,7 +149,11 @@ private static Path variableToPath(Object variableValue) {
144149
}
145150
} else if (variableValue instanceof String string) {
146151
try {
147-
return Path.of(string).normalize().toAbsolutePath();
152+
if (Files.exists(Path.of(string).normalize().toAbsolutePath())) {
153+
return Path.of(string).normalize().toAbsolutePath();
154+
} else {
155+
throw new InvalidPathException(string, localize("security.advice.transform.path.exception"));
156+
}
148157
} catch (InvalidPathException e) {
149158
throw new InvalidPathException(string, localize("security.advice.transform.path.exception"));
150159
}
@@ -242,8 +251,11 @@ public static void checkFileSystemInteraction(
242251
final String fullMethodSignature = declaringTypeName + "." + methodName + methodSignature;
243252
String illegallyReadingMethod = allowedPaths == null ? null : checkIfCallstackCriteriaIsViolated(restrictedPackage, allowedClasses);
244253
if (illegallyReadingMethod != null) {
245-
String illegallyReadPath = (parameters == null || parameters.length == 0) ? null : checkIfVariableCriteriaIsViolated(parameters, allowedPaths);
246-
if (illegallyReadPath == null) {
254+
String illegallyReadPath = null;
255+
if (!fileSystemIgnoreParameter.contains(fullMethodSignature + "." + methodName)) {
256+
illegallyReadPath = (parameters == null || parameters.length == 0) ? null : checkIfVariableCriteriaIsViolated(parameters, allowedPaths);
257+
}
258+
if (illegallyReadPath == null && !fileSystemIgnoreAttributes.contains(fullMethodSignature + "." + methodName)) {
247259
illegallyReadPath = (attributes == null || attributes.length == 0) ? null : checkIfVariableCriteriaIsViolated(attributes, allowedPaths);
248260
}
249261
if (illegallyReadPath != null) {
@@ -264,7 +276,8 @@ public static String localize(String key, Object... args) {
264276
} else {
265277
throw new IllegalStateException("Method does not return a String");
266278
}
267-
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
279+
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
280+
IllegalAccessException e) {
268281
// Fallback: Return the key if localization fails
269282
return key;
270283
}

src/main/java/de/tum/cit/ase/ares/api/aop/java/instrumentation/pointcut/JavaInstrumentationPointcutDefinitions.java

+52-43
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import net.bytebuddy.matcher.ElementMatcher;
77
import net.bytebuddy.matcher.ElementMatchers;
88

9+
import java.util.AbstractMap;
910
import java.util.List;
1011
import java.util.Map;
1112
import java.util.Set;
@@ -122,29 +123,27 @@ static ElementMatcher<MethodDescription> getMethodsMatcher(
122123
* This map contains the methods which can read files. The map keys represent class names,
123124
* and the values are lists of method names that are considered to be file read operations.
124125
*/
125-
public static final Map<String, List<String>> methodsWhichCanReadFiles = Map.of(
126-
// TODO Markus: Currently the methods which can read are not properly instrumented
127-
"java.io.FileInputStream",
128-
List.of("<init>", "read", "open"),
129-
"java.io.RandomAccessFile",
130-
List.of("<init>", "read", "readFully", "readLine", "readBoolean", "readByte", "readChar", "readDouble",
131-
"readFloat", "readInt", "readLong", "readShort", "readUnsignedByte", "readUnsignedShort"),
132-
"java.io.UnixFileSystem",
133-
List.of("getBooleanAttributes0", "getSpace", "canonicalize0"),
134-
"java.io.WinNTFileSystem",
135-
List.of("getBooleanAttributes", "canonicalize", "getLastModifiedTime", "getSpace"),
136-
"java.io.Win32FileSystem",
137-
List.of("getBooleanAttributes", "canonicalize", "getLastModifiedTime", "getSpace"),
138-
"java.nio.file.Files",
139-
List.of("readAttributes", "readAllBytes", "readAllLines", "readString", "read", "newInputStream", "lines"),
140-
"java.io.FileReader",
141-
List.of("<init>", "read", "readLine"),
142-
"java.io.BufferedReader",
143-
List.of("lines"),
144-
"java.nio.channels.FileChannel",
145-
List.of("open"),
146-
"java.nio.file.spi.FileSystemProvider",
147-
List.of("newFileChannel")
126+
public static final Map<String, List<String>> methodsWhichCanReadFiles = Map.ofEntries(
127+
new AbstractMap.SimpleEntry<>("java.io.FileInputStream",
128+
List.of("<init>", "read", "open")),
129+
new AbstractMap.SimpleEntry<>("java.io.RandomAccessFile",
130+
List.of("<init>")),
131+
new AbstractMap.SimpleEntry<>("java.io.UnixFileSystem",
132+
List.of("getBooleanAttributes0", "getSpace", "canonicalize0")),
133+
new AbstractMap.SimpleEntry<>("java.io.WinNTFileSystem",
134+
List.of("getBooleanAttributes", "canonicalize", "getLastModifiedTime", "getSpace")),
135+
new AbstractMap.SimpleEntry<>("java.io.Win32FileSystem",
136+
List.of("getBooleanAttributes", "canonicalize", "getLastModifiedTime", "getSpace")),
137+
new AbstractMap.SimpleEntry<>("java.nio.file.Files",
138+
List.of("readAttributes", "readAllBytes", "readAllLines", "readString", "read", "newInputStream", "lines")),
139+
new AbstractMap.SimpleEntry<>("java.io.FileReader",
140+
List.of("<init>", "read", "readLine")),
141+
new AbstractMap.SimpleEntry<>("java.io.BufferedReader",
142+
List.of("lines")),
143+
new AbstractMap.SimpleEntry<>("sun.nio.ch.FileChannelImpl",
144+
List.of("open", "read", "readFully", "readDirect", "readIntoNativeBuffer")),
145+
new AbstractMap.SimpleEntry<>("java.nio.file.spi.FileSystemProvider",
146+
List.of("newFileChannel"))
148147
);
149148
//</editor-fold>
150149

@@ -153,24 +152,26 @@ static ElementMatcher<MethodDescription> getMethodsMatcher(
153152
* This map contains the methods which can overwrite files. The map keys represent class names,
154153
* and the values are lists of method names that are considered to be file overwrite operations.
155154
*/
156-
public static final Map<String, List<String>> methodsWhichCanOverwriteFiles = Map.of(
157-
"java.io.FileOutputStream",
158-
List.of("<init>"),
159-
"java.io.RandomAccessFile",
160-
List.of("write", "writeBoolean", "writeByte", "writeBytes", "writeChar", "writeChars", "writeDouble", "writeFloat", "writeInt", "writeLong", "writeShort"),
161-
"java.io.UnixFileSystem",
162-
List.of("setLastModifiedTime", "createFileExclusively", "delete0", "createDirectory"),
163-
"java.io.WinNTFileSystem",
164-
List.of("createFileExclusively", "delete", "setLastModifiedTime", "createDirectory"),
165-
"java.io.Win32FileSystem",
166-
List.of("createFileExclusively", "delete", "setLastModifiedTime", "createDirectory"),
167-
"java.util.prefs.FileSystemPreferences",
168-
List.of("lockFile0", "unlockFile0"),
169-
"java.nio.file.Files",
170-
List.of("write", "writeString", "newOutputStream", "writeBytes", "writeAllBytes", "writeLines"),
171-
"java.io.File",
172-
List.of("setWritable")
173-
155+
public static final Map<String, List<String>> methodsWhichCanOverwriteFiles = Map.ofEntries(
156+
new AbstractMap.SimpleEntry<>("java.io.FileOutputStream",
157+
List.of("<init>")),
158+
new AbstractMap.SimpleEntry<>("java.io.RandomAccessFile",
159+
List.of("write", "writeBoolean", "writeByte", "writeBytes", "writeChar", "writeChars",
160+
"writeDouble", "writeFloat", "writeInt", "writeLong", "writeShort")),
161+
new AbstractMap.SimpleEntry<>("java.io.UnixFileSystem",
162+
List.of("setLastModifiedTime", "createFileExclusively", "delete0", "createDirectory")),
163+
new AbstractMap.SimpleEntry<>("java.io.WinNTFileSystem",
164+
List.of("createFileExclusively", "delete", "setLastModifiedTime", "createDirectory")),
165+
new AbstractMap.SimpleEntry<>("java.io.Win32FileSystem",
166+
List.of("createFileExclusively", "delete", "setLastModifiedTime", "createDirectory")),
167+
new AbstractMap.SimpleEntry<>("java.util.prefs.FileSystemPreferences",
168+
List.of("lockFile0", "unlockFile0")),
169+
new AbstractMap.SimpleEntry<>("java.nio.file.Files",
170+
List.of("write", "writeString", "newOutputStream", "writeBytes", "writeAllBytes", "writeLines")),
171+
new AbstractMap.SimpleEntry<>("java.io.File",
172+
List.of("setWritable")),
173+
new AbstractMap.SimpleEntry<>("sun.nio.ch.FileChannelImpl",
174+
List.of("write", "writeFully", "writeDirect", "writeFromNativeBuffer"))
174175
);
175176
//</editor-fold>
176177

@@ -200,11 +201,19 @@ static ElementMatcher<MethodDescription> getMethodsMatcher(
200201
*/
201202
public static final Map<String, List<String>> methodsWhichCanDeleteFiles = Map.of(
202203
"java.io.File",
203-
List.of("delete", "deleteOnExit"),
204+
List.of("deleteOnExit"),
204205
"java.nio.file.Files",
205206
List.of("delete", "deleteIfExists"),
206207
"sun.nio.fs.UnixFileSystemProvider",
207-
List.of("implDelete")
208+
List.of("implDelete"),
209+
"sun.nio.fs.WindowsFileSystemProvider",
210+
List.of("implDelete"),
211+
"java.io.UnixFileSystem",
212+
List.of("delete"),
213+
"java.io.WinNTFileSystem",
214+
List.of("delete"),
215+
"java.io.Win32FileSystem",
216+
List.of("delete")
208217
);
209218
//</editor-fold>
210219

src/main/java/de/tum/cit/ase/ares/api/architecture/java/CallGraphBuilderUtils.java src/main/java/de/tum/cit/ase/ares/api/architecture/java/CustomCallgraphBuilder.java

+32-23
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import com.tngtech.archunit.core.importer.ClassFileImporter;
1919
import de.tum.cit.ase.ares.api.architecture.java.wala.ReachabilityChecker;
2020
import de.tum.cit.ase.ares.api.util.FileTools;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
2123

2224
import java.io.IOException;
2325
import java.util.*;
@@ -29,51 +31,46 @@
2931
/**
3032
* Utility class to build a call graph from a class path.
3133
*/
32-
public class CallGraphBuilderUtils {
34+
public class CustomCallgraphBuilder {
3335

34-
private CallGraphBuilderUtils() {
35-
throw new SecurityException(localize("security.general.utility.initialization", CallGraphBuilderUtils.class.getName()));
36-
}
36+
private static final Logger log = LoggerFactory.getLogger(CustomCallgraphBuilder.class);
3737

3838
/**
3939
* Class file importer to import the class files.
4040
* This is used to import the class files from the URL.
4141
*/
42-
private static final ClassFileImporter classFileImporter;
42+
private final ClassFileImporter classFileImporter;
4343

44-
private static final ClassHierarchy classHierarchy;
44+
private final ClassHierarchy classHierarchy;
4545

46-
private static final AnalysisScope scope;
46+
private final AnalysisScope scope;
4747

48-
static {
49-
try {
50-
// Create a class file importer
51-
classFileImporter = new ClassFileImporter();
48+
private CallGraph callGraph = null;
5249

53-
// Create an analysis scope
50+
public CustomCallgraphBuilder() {
51+
classFileImporter = new ClassFileImporter();
52+
try {
5453
scope = Java9AnalysisScopeReader.instance.makeJavaBinaryAnalysisScope(
5554
System.getProperty("java.class.path"),
56-
// File translates the path name for Windows and Unix
5755
FileTools.getResourceAsFile("de/tum/cit/ase/ares/api/templates/architecture/java/exclusions.txt")
5856
);
59-
60-
// Build the class hierarchy
6157
classHierarchy = ClassHierarchyFactory.make(scope);
6258
} catch (ClassHierarchyException | IOException e) {
63-
throw new SecurityException(localize("security.architecture.class.hierarchy.error")); // $NON-NLS-1$
59+
throw new SecurityException(localize("security.architecture.class.hierarchy.error"));
6460
}
6561
}
6662

6763

6864
/**
6965
* Try to resolve the class by the given type name.
70-
*
66+
* <p>
7167
* Ignore jrt URLs as they cause infinite loops and are not needed for the analysis for ArchUnit
7268
*
7369
* @param typeName The type name of the class to resolve.
7470
* @return The resolved class if it exists.
7571
*/
76-
public static Optional<JavaClass> tryResolve(String typeName) {
72+
@SuppressWarnings("unused")
73+
public Optional<JavaClass> tryResolve(String typeName) {
7774
List<String> ignoredTypeNames = List.of(
7875
// Advice definition uses Reflection and therefor should not be resolved
7976
"de.tum.cit.ase.ares.api.aop.java.aspectj.adviceandpointcut.JavaAspectJFileSystemAdviceDefinitions"
@@ -83,7 +80,7 @@ public static Optional<JavaClass> tryResolve(String typeName) {
8380
return Optional.empty();
8481
}
8582
// TODO: Check if FileTools supports this approach
86-
return Optional.ofNullable(CallGraphBuilderUtils.class.getResource("/" + typeName.replace(".", "/") + ".class"))
83+
return Optional.ofNullable(CustomCallgraphBuilder.class.getResource("/" + typeName.replace(".", "/") + ".class"))
8784
.map(location -> classFileImporter
8885
.withImportOption(loc -> !loc.contains("jrt"))
8986
.importUrl(location))
@@ -102,7 +99,8 @@ public static Optional<JavaClass> tryResolve(String typeName) {
10299
* @param typeName The type name of the class to get the immediate subclasses.
103100
* @return The immediate subclasses of the given type name.
104101
*/
105-
public static Set<JavaClass> getImmediateSubclasses(String typeName) {
102+
@SuppressWarnings("unused")
103+
public Set<JavaClass> getImmediateSubclasses(String typeName) {
106104
TypeReference reference = TypeReference.find(ClassLoaderReference.Application, convertTypeName(typeName));
107105
if (reference == null) {
108106
return Collections.emptySet();
@@ -116,7 +114,7 @@ public static Set<JavaClass> getImmediateSubclasses(String typeName) {
116114
.stream()
117115
.map(IClass::getName)
118116
.map(Object::toString)
119-
.map(CallGraphBuilderUtils::tryResolve)
117+
.map(this::tryResolve)
120118
.filter(Optional::isPresent)
121119
.map(Optional::get).collect(Collectors.toSet());
122120
}
@@ -137,8 +135,11 @@ public static String convertTypeName(String typeName) {
137135
/**
138136
* Build a call graph from a class path and the passed in predicate
139137
*/
140-
public static CallGraph buildCallGraph(String classPathToAnalyze) {
138+
public CallGraph buildCallGraph(String classPathToAnalyze) {
141139
try {
140+
if (callGraph != null) {
141+
return callGraph;
142+
}
142143
// Create a list to store entry points
143144
List<DefaultEntrypoint> customEntryPoints = ReachabilityChecker.getEntryPointsFromStudentSubmission(classPathToAnalyze, classHierarchy);
144145

@@ -149,9 +150,17 @@ public static CallGraph buildCallGraph(String classPathToAnalyze) {
149150
CallGraphBuilder<InstanceKey> builder = Util.makeZeroOneCFABuilder(Language.JAVA, options, new AnalysisCacheImpl(), classHierarchy);
150151

151152
// Generate the call graph
152-
return builder.makeCallGraph(options, null);
153+
callGraph = builder.makeCallGraph(options, null);
154+
return callGraph;
153155
} catch (CallGraphBuilderCancelException e) {
154156
throw new SecurityException(localize("security.architecture.build.call.graph.error")); //$NON-NLS-1$
155157
}
156158
}
159+
160+
/**
161+
* Get the call graph
162+
*/
163+
public CallGraph getCallGraph() {
164+
return callGraph;
165+
}
157166
}

0 commit comments

Comments
 (0)