diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..fc8bb55b --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,44 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build-and-test: + + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: '21' + distribution: 'adopt' + + - name: Install Maven + run: | + sudo rm -rf /usr/bin/mvn + sudo wget -q -O - "https://apache.osuosl.org/maven/maven-3/3.9.8/binaries/apache-maven-3.9.8-bin.tar.gz" | sudo tar xzf - -C /usr/share + sudo ln -s /usr/share/apache-maven-3.9.8/bin/mvn /usr/bin/mvn + + - name: Build and Test with Maven + run: mvn -B package --file pom.xml + + - name: Run Tests on Student Submission + run: | + git config --global url.https://github.com/.insteadOf git://github.com/ + git clone https://github.com/sarpsahinalp/test-student-submission.git + mkdir test-student-submission/libs + cp target/*.jar test-student-submission/libs + cd test-student-submission + ./gradlew build + ./gradlew test diff --git a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/CustomClassResolver.java b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/CustomClassResolver.java index f0ff7dc9..a7a8ea6a 100644 --- a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/CustomClassResolver.java +++ b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/CustomClassResolver.java @@ -1,27 +1,28 @@ package de.tum.cit.ase.ares.api.architecturetest.java.postcompile; -import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; -import com.tngtech.archunit.core.importer.resolvers.ClassResolverFromClasspath; -import java.net.URL; import java.util.Optional; /** * Custom class resolver to resolve classes that are outside classpath to be able to analyze them transitively. */ public class CustomClassResolver { - - private CustomClassResolver() { - throw new IllegalStateException("Utility class"); - } - /** * Class file importer to import the class files. * This is used to import the class files from the URL. */ - private static final ClassFileImporter classFileImporter = new ClassFileImporter(); + private final JavaClasses allClasses; + + public CustomClassResolver() { + // We need to import all classes to be able to resolve them later. + // TODO: We definitely need to improve this. We should not import all classes as it is not memory efficient. + // https://www.javadoc.io/doc/com.tngtech.archunit/archunit/0.10.2/com/tngtech/archunit/core/importer/ClassFileImporter.html + allClasses = new ClassFileImporter() + .importClasspath(); + } /** * Try to resolve the class by the given type name. @@ -29,9 +30,12 @@ private CustomClassResolver() { * @param typeName The type name of the class to resolve. * @return The resolved class if it exists. */ - public static Optional tryResolve(String typeName) { - ArchConfiguration.get().setClassResolver(ClassResolverFromClasspath.class); - URL url = CustomClassResolver.class.getResource("/" + typeName.replace(".", "/") + ".class"); - return url != null ? Optional.of(classFileImporter.importUrl(url).get(typeName)) : Optional.empty(); + public Optional tryResolve(String typeName) { + try { + // Try to resolve the class by the given type name. + return Optional.ofNullable(allClasses.get(typeName)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } } } diff --git a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/JavaArchitectureTestCaseCollection.java b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/JavaArchitectureTestCaseCollection.java index 49f3955f..202d4dfd 100644 --- a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/JavaArchitectureTestCaseCollection.java +++ b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/JavaArchitectureTestCaseCollection.java @@ -42,14 +42,12 @@ private JavaArchitectureTestCaseCollection() { "java.nio.file", "java.util.prefs", "sun.print", - "sun.security", "java.util.jar", "java.util.zip", "sun.awt.X11", "javax.imageio", "javax.sound.midi", - "javax.swing.filechooser", - "java.awt.desktop"); + "javax.swing.filechooser"); /** * Load pre file contents diff --git a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java index 3fbe5cbf..48f39b53 100644 --- a/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java +++ b/src/main/java/de/tum/cit/ase/ares/api/architecturetest/java/postcompile/TransitivelyAccessesMethodsCondition.java @@ -12,10 +12,7 @@ import de.tum.cit.ase.ares.api.architecturetest.java.JavaSupportedArchitectureTestCase; import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import static com.tngtech.archunit.lang.ConditionEvent.createMessage; import static com.tngtech.archunit.thirdparty.com.google.common.base.Preconditions.checkNotNull; @@ -30,15 +27,37 @@ */ public class TransitivelyAccessesMethodsCondition extends ArchCondition { + /** + * Set of classes that does not lead to a violation if they are accessed + */ + private static final Set bannedClasses = Set.of( + "java.lang.Object", + "java.lang.String", + "java.security.AccessControl", + "java.util", + "sun.util" + ); + + /** + * Predicate to match the accessed methods + */ private final DescribedPredicate> conditionPredicate; + + /** + * Transitive access path to find the path to the accessed method + */ private final TransitiveAccessPath transitiveAccessPath = new TransitiveAccessPath(); + /** + * Custom class resolver to resolve classes that are outside classpath to be able to analyze them transitively + */ + private final CustomClassResolver customClassResolver; + /** * @param conditionPredicate Predicate to match the accessed methods */ public TransitivelyAccessesMethodsCondition(DescribedPredicate> conditionPredicate, JavaSupportedArchitectureTestCase javaSupportedArchitectureTestCase) { super("transitively depend on classes that " + conditionPredicate.getDescription()); - switch (javaSupportedArchitectureTestCase) { case FILESYSTEM_INTERACTION -> { try { @@ -55,6 +74,7 @@ public TransitivelyAccessesMethodsCondition(DescribedPredicate throw new IllegalStateException("JavaSupportedArchitecture cannot be null"); } this.conditionPredicate = checkNotNull(conditionPredicate); + this.customClassResolver = new CustomClassResolver(); } /** @@ -143,18 +163,47 @@ private boolean addAccessesToPathFrom( * @return all accesses to the same target as the supplied item that are not in the analyzed classes */ private Set> getDirectAccessTargetsOutsideOfAnalyzedClasses(JavaAccess item) { - Optional resolvedTarget = CustomClassResolver.tryResolve(item.getTargetOwner().getFullName()); - return resolvedTarget.map(javaClass -> javaClass.getAccessesFromSelf() + // If the target owner is in the banned classes, return an empty set + if (bannedClasses.stream().anyMatch(p -> item.getTargetOwner().getFullName().startsWith(p)) || item.getTargetOwner().isAssignableTo(Exception.class) || item.getTargetOwner().isAssignableTo(Error.class)) { + return Collections.emptySet(); + } + + // Get all subclasses of the target owner including the target owner + JavaClass resolvedTarget = resolveTargetOwner(item.getTargetOwner()); + + // Match the accesses to the target + Set 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 + */ + if (subclasses.size() > 20) { + return Collections.emptySet(); + } + + return subclasses.stream() + .map(javaClass -> getAccessesFromClass(javaClass, item.getTarget().getName())) + .flatMap(Set::stream) + .collect(toSet()); + } + + private Set> getAccessesFromClass(JavaClass javaClass, String methodName) { + return javaClass.getAccessesFromSelf() .stream() .filter(a -> a .getOrigin() - .getFullName() - .equals(item - .getTarget() - .getFullName() - ) - ) - .collect(toSet())).orElseGet(Set::of); + .getName() + .equals(methodName)) + .filter(a -> bannedClasses.stream().noneMatch(p -> a.getTargetOwner().getFullName().startsWith(p))) + .collect(toSet()); + } + + private JavaClass resolveTargetOwner(JavaClass targetOwner) { + Optional resolvedTarget = customClassResolver.tryResolve(targetOwner.getFullName()); + return resolvedTarget.orElse(targetOwner); } } } diff --git a/src/main/java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java b/src/main/java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java index 173c6490..ae6bff1a 100644 --- a/src/main/java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java +++ b/src/main/java/de/tum/cit/ase/ares/api/ast/model/RecursionCheck.java @@ -108,7 +108,7 @@ public static String formatVertexInfoWithinCycle(String vertexName, MethodCallGr } return localized("ast.method.get_formatted_file_string_prefix", pathOfSourceRoot.toString() - + "/" + Objects.requireNonNull(extractClassName(vertexName))) + + File.separator + Objects.requireNonNull(extractClassName(vertexName))) + System.lineSeparator() + localized("ast.method.get_formatted_unwanted_node_string_prefix", "Unwanted Recursion") diff --git a/src/main/resources/archunit.properties b/src/main/resources/archunit.properties new file mode 100644 index 00000000..cd4e78b0 --- /dev/null +++ b/src/main/resources/archunit.properties @@ -0,0 +1,3 @@ +# Set to false to ignore missing dependencies in the classpath, as they are resolved manually by the de.tum.cit.ase.ares.api.architecturetest.java.postcompile.CustomClassResolver +resolveMissingDependenciesFromClassPath=false +archRule.failOnEmptyShould=false \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 0f2c37ac..8250fafe 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -12,6 +12,7 @@ + diff --git a/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java b/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java index fde13659..26b87255 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/AstAssertionTest.java @@ -690,8 +690,7 @@ void test_testExcludesPassedMethods_Success() { void test_testExcludesPassedMethod_Fail() { String testExcludesPassedMethod_Fail = "testExcludesPassedMethod_Fail"; tests.assertThatEvents().haveExactly(1, testFailedWith(testExcludesPassedMethod_Fail, AssertionError.class, "Unwanted statement found:" + System.lineSeparator() + " - In " - + Path.of("src", "test", "java", "de", "tum", "cit", "ase", "ares", "integration", "testuser", "subject", "structural", "astTestFiles", "recursions" , "excludeMethods", - "ClassWithNoExcludeMethods.java") + + Path.of("src", "test", "java", "de", "tum", "cit", "ase", "ares", "integration", "testuser", "subject", "structural", "astTestFiles", "recursions" , "excludeMethods", "ClassWithNoExcludeMethods.java") + ":" + System.lineSeparator() + " - Unwanted Recursion was found:" + System.lineSeparator() + " - Between line 5 (column 5) and line 7 (column 5) in Method de.tum.cit.ase.ares.integration.testuser.subject.structural.astTestFiles.recursions.excludeMethods.ClassWithNoExcludeMethods.something(de.tum.cit.ase.ares.integration.testuser.subject.structural.astTestFiles.recursions.excludeMethods.RandomParameterThatShouldBeResolved)" + System.lineSeparator())); } diff --git a/src/test/java/de/tum/cit/ase/ares/integration/PathAccessTest.java b/src/test/java/de/tum/cit/ase/ares/integration/PathAccessTest.java index 005dd40b..d39b95c5 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/PathAccessTest.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/PathAccessTest.java @@ -99,4 +99,9 @@ void test_accessPathTest() { void test_weAccessPath() { tests.assertThatEvents().haveExactly(1, finishedSuccessfully(weAccessPath)); } + + @TestTest + void test_accessFileSystem() { + tests.assertThatEvents().haveExactly(1, testFailedWith("accessFileSystem", SecurityException.class)); + } } diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java index c6bad35a..938785e9 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/PathAccessUser.java @@ -102,8 +102,11 @@ void accessPathTest() throws IOException { } @PublicTest - @Policy(value = "src/test/resources/de/tum/cit/ase/ares/integration/testuser/securitypolicies/NoAllowedPathPolicy.yaml", withinPath = "test-classes/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess") void weAccessPath() throws IOException { Files.readString(Path.of("pom.xml")); } + + @PublicTest + @Policy(value = "src/test/resources/de/tum/cit/ase/ares/integration/testuser/securitypolicies/NoAllowedPathPolicy.yaml", withinPath = "test-classes/de/tum/cit/ase/ares/integration/testuser/subject/student") + void accessFileSystem() throws IOException {} } diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess/PathAccessPenguin.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess/PathAccessPenguin.java index 168c830b..d4bf573e 100644 --- a/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess/PathAccessPenguin.java +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/pathaccess/PathAccessPenguin.java @@ -1,7 +1,5 @@ package de.tum.cit.ase.ares.integration.testuser.subject.pathaccess; -import ch.qos.logback.classic.log4j.XMLLayout; - import java.io.*; import java.nio.file.*; diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/student/FileSystemAccessPenguin.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/student/FileSystemAccessPenguin.java new file mode 100644 index 00000000..fb506f30 --- /dev/null +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/student/FileSystemAccessPenguin.java @@ -0,0 +1,416 @@ +package de.tum.cit.ase.ares.integration.testuser.subject.student; + +import de.tum.cit.ase.ares.integration.testuser.subject.thirdpartypackage.ThirdPartyPackagePenguin; + +import javax.sound.midi.InvalidMidiDataException; +import javax.sound.midi.MidiSystem; +import javax.swing.*; +import java.awt.*; +import java.io.*; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.*; +import java.nio.file.attribute.FileTime; +import java.nio.file.spi.FileSystemProvider; +import java.util.logging.FileHandler; + +/** + * This class is used to access the file system using various classes from the {@link java.nio.file} package. + * It is used to test if Ares can detect and handle file system access correctly. + */ +public class FileSystemAccessPenguin { + + /** + * Access the file system using the {@link FileChannel} class. + */ + void accessFileSystemViaFileChannel() { + try { + FileChannel fileChannel = FileChannel.open(null, null); + fileChannel.read((ByteBuffer) null); + fileChannel.write((ByteBuffer) null); + fileChannel.force(true); + fileChannel.position(0); + fileChannel.size(); + fileChannel.tryLock(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileInputStream} class. + */ + void accessFileSystemViaFileInputStream() { + try { + new FileInputStream("file.txt").read(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileOutputStream} class. + */ + void accessFileSystemViaFileOutputStream() { + // This method is intentionally left blank + try { + new FileOutputStream("file.txt").write(0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileReader} class. + */ + void accessFileSystemViaFileReader() { + // This method is intentionally left blank + try { + FileReader reader = new FileReader("file.txt"); + reader.read(); + reader.read(new char[0]); + reader.read(new char[0], 0, 0); + reader.ready(); + reader.skip(0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileWriter} class. + */ + void accessFileSystemViaFileWriter() { + // This method is intentionally left blank + try { + FileWriter writer = new FileWriter("file.txt"); + writer.write(0); + writer.flush(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link RandomAccessFile} class. + */ + void accessFileSystemViaRandomAccessFile() { + // This method is intentionally left blank + try { + RandomAccessFile file = new RandomAccessFile("file.txt", "rw"); + file.read(); + file.read(new byte[0]); + file.readBoolean(); + file.readByte(); + file.readChar(); + file.readDouble(); + file.readFloat(); + file.readInt(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link BufferedReader} class. + */ + void accessFileSystemViaFile() { + // This method is intentionally left blank + try { + File file = new File("file.txt"); + file.createNewFile(); + file.delete(); + file.exists(); + file.isDirectory(); + file.isFile(); + file.isHidden(); + file.lastModified(); + file.length(); + file.list(); + file.listFiles(); + file.mkdir(); + file.mkdirs(); + file.renameTo(new File("file.txt")); + file.setExecutable(true); + file.setReadable(true); + file.setWritable(true); + file.toPath(); + file.toURI(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileStore} class. + */ + void accessFileSystemViaFileStore() { + // This method is intentionally left blank + try { + FileStore store = Files.getFileStore(null); + store.getTotalSpace(); + store.getUsableSpace(); + store.getUnallocatedSpace(); + store.isReadOnly(); + store.type(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileTime} class. + */ + void accessFileSystemViaFileTime() { + // This method is intentionally left blank + try { + Files.setLastModifiedTime(null, FileTime.fromMillis(0)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link Files} class. + */ + void accessFileSystemViaFiles() { + // This method is intentionally left blank + try { + Files.copy(null, null); + Files.createDirectory(null); + Files.createFile(null); + Files.createLink(null, null); + Files.createSymbolicLink(null, null); + Files.createTempDirectory(null); + Files.createTempFile(null, null); + Files.createTempFile(null, null, null); + Files.createDirectories(null); + Files.createLink(null, null); + Files.createSymbolicLink(null, null); + Files.createTempDirectory(null); + Files.createTempFile(null, null); + Files.createTempFile(null, null, null); + Files.delete(null); + Files.deleteIfExists(null); + Files.exists(null); + Files.isReadable(null); + Files.isWritable(null); + Files.isExecutable(null); + Files.isHidden(null); + Files.getLastModifiedTime(null); + Files.getAttribute(null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + Files.getAttribute(null, null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link FileSystemProvider} class. + */ + void accessFileSystemViaFileSystemProvider() { + try { + FileSystemProvider provider = FileSystemProvider.installedProviders().get(0); + provider.delete(Path.of("test.txt")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.FileSystem} class. + */ + void accessFileSystemViaFileSystem() { + // This method is intentionally left blank + try { + FileSystem fileSystem = FileSystems.getDefault(); + fileSystem.close(); + fileSystem.isOpen(); + fileSystem.isReadOnly(); + fileSystem.provider(); + fileSystem.getRootDirectories(); + fileSystem.getFileStores(); + fileSystem.getPathMatcher(null); + fileSystem.getUserPrincipalLookupService(); + fileSystem.newWatchService(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.FileVisitor} class. + */ + void accessFileSystemViaFileVisitor() { + // This method is intentionally left blank + try { + SimpleFileVisitor visitor = new SimpleFileVisitor<>() { + }; + visitor.postVisitDirectory(null, null); + visitor.preVisitDirectory(null, null); + visitor.visitFile(null, null); + visitor.visitFileFailed(null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.Path} class. + */ + void accessFileSystemViaPath() { + // This method is intentionally left blank + try { + Path path = Paths.get("file.txt"); + path.getFileName(); + path.getRoot(); + path.getParent(); + path.getNameCount(); + path.getName(0); + path.subpath(0, 0); + path.startsWith((Path) null); + path.endsWith((Path) null); + path.normalize(); + path.resolve((Path) null); + path.relativize(null); + path.toAbsolutePath(); + path.toRealPath(); + path.toUri(); + path.register(null, null); + path.register(null, null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.Paths} class. + */ + void accessFileSystemViaPaths() { + // This method is intentionally left blank + try { + Paths.get("file.txt"); + Paths.get("file.txt", "file.txt"); + Paths.get("file.txt", "file.txt", "file.txt"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchService} class. + */ + void accessFileSystemViaWatchService() { + // This method is intentionally left blank + try { + WatchService service = FileSystems.getDefault().newWatchService(); + service.close(); + service.poll(); + service.poll(0, null); + service.take(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.Watchable} class. + */ + void accessFileSystemViaWatchable() { + // This method is intentionally left blank + try { + Watchable watchable = Paths.get("file.txt"); + watchable.register(null, null); + watchable.register(null, null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchEvent} class. + */ + void accessFileSystemViaDesktop() { + // This method is intentionally left blank + try { + Desktop.getDesktop().browse(null); + Desktop.getDesktop().edit(null); + Desktop.getDesktop().mail(null); + Desktop.getDesktop().open(null); + Desktop.getDesktop().print(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchEvent} class. + */ + void accessFileSystemViaFileHandler() { + // This method is intentionally left blank + try { + new FileHandler("file.txt").close(); + new FileHandler("file.txt").flush(); + new FileHandler("file.txt").publish(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchEvent} class. + */ + @SuppressWarnings("deprecated") + void accessFileSystemViaSoundBankReader() { + try { + // Get the default SoundbankReader + MidiSystem.getSoundbank(new URL("soundbank.sf2")); + } catch (InvalidMidiDataException | IOException e) { + e.printStackTrace(); + } + } + + /** + * Access the file system using the {@link java.nio.file.WatchEvent} class. + */ + void accessFileSystemViaJFileChooser() { + // Create a JFrame to hold the file chooser + // Create a JButton to trigger the file chooser + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle("Select a file to open"); + + // Show the file open dialog + // Get the selected file + File selectedFile = fileChooser.getSelectedFile(); + System.out.println("Selected file: " + selectedFile.getAbsolutePath()); + + // Read and print the file contents + try (BufferedReader reader = new BufferedReader(new FileReader(selectedFile))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /** + * Access the file system using a third party library which accesses the file system. + */ + void checkAccessRecursive() throws IOException { + ThirdPartyPackagePenguin.accessFileSystem(); + } + +} diff --git a/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/thirdpartypackage/ThirdPartyPackagePenguin.java b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/thirdpartypackage/ThirdPartyPackagePenguin.java new file mode 100644 index 00000000..5fac8f5f --- /dev/null +++ b/src/test/java/de/tum/cit/ase/ares/integration/testuser/subject/thirdpartypackage/ThirdPartyPackagePenguin.java @@ -0,0 +1,18 @@ +package de.tum.cit.ase.ares.integration.testuser.subject.thirdpartypackage; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * This class is used to emulate a third-party package that is not part of the test user's codebase. + */ +public class ThirdPartyPackagePenguin { + + /** + * This method is used to emulate a third-party package that is not part of the test user's codebase accessing the file system. + */ + public static void accessFileSystem() throws IOException { + Files.readString(Path.of("fileUsingFilesClass.txt")); + } +}