diff --git a/.github/workflows/analysis-of-endpoint-connections.yml b/.github/workflows/analysis-of-endpoint-connections.yml index 9ee3dcac09bd..2d73f13af207 100644 --- a/.github/workflows/analysis-of-endpoint-connections.yml +++ b/.github/workflows/analysis-of-endpoint-connections.yml @@ -10,7 +10,7 @@ on: - 'src/main/webapp/**' jobs: - show-modified-files: + analysis-of-endpoint-connections: timeout-minutes: 10 runs-on: ubuntu-latest steps: @@ -23,7 +23,12 @@ jobs: run: | git diff --name-only origin/${{ github.event.pull_request.base.ref }} HEAD > modified_files.txt - - name: Display modified files + - name: Set up JDK 21 + uses: actions/setup-java@v2 + with: + java-version: '21' + distribution: 'adopt' + + - name: Run analysis-of-endpoint-connections run: | - echo "Modified files in this pull request:" - cat modified_files.txt + ./gradlew :supporting_scripts:analysis-of-endpoint-connections:run --args="$(cat modified_files.txt)" diff --git a/build.gradle b/build.gradle index 49a260cb4622..cd47240a4c5e 100644 --- a/build.gradle +++ b/build.gradle @@ -221,6 +221,9 @@ repositories { ext["jackson.version"] = fasterxml_version ext["junit-jupiter.version"] = junit_version +ext { qDoxVersionReusable = "com.thoughtworks.qdox:qdox:2.1.0" } +ext { springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web:${spring_boot_version}" } + dependencies { // Note: jenkins-client is not well maintained and includes dependencies to libraries with critical security issues (e.g. CVE-2020-10683 for dom4j@1.6.1) @@ -291,7 +294,7 @@ dependencies { } } - implementation "com.thoughtworks.qdox:qdox:2.1.0" + implementation qDoxVersionReusable implementation "io.sentry:sentry-logback:${sentry_version}" implementation "io.sentry:sentry-spring-boot-starter-jakarta:${sentry_version}" @@ -361,7 +364,7 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-aop:${spring_boot_version}" implementation "org.springframework.boot:spring-boot-starter-data-jpa:${spring_boot_version}" implementation "org.springframework.boot:spring-boot-starter-security:${spring_boot_version}" - implementation("org.springframework.boot:spring-boot-starter-web:${spring_boot_version}") { + implementation(springBootStarterWeb) { exclude module: "spring-boot-starter-undertow" } implementation "org.springframework.boot:spring-boot-starter-tomcat:${spring_boot_version}" diff --git a/settings.gradle b/settings.gradle index a077d360727f..dc8897e1b9b1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,3 +11,5 @@ pluginManagement { } rootProject.name = 'Artemis' + +include 'supporting_scripts:analysis-of-endpoint-connections' diff --git a/supporting_scripts/analysis-of-endpoint-connections/build.gradle b/supporting_scripts/analysis-of-endpoint-connections/build.gradle new file mode 100644 index 000000000000..ef2fa37c2f5a --- /dev/null +++ b/supporting_scripts/analysis-of-endpoint-connections/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'java' + id 'application' +} + +group 'de.tum.in.www1.artemis' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +evaluationDependsOn(':') + +dependencies { + implementation rootProject.ext.qDoxVersionReusable + implementation rootProject.ext.springBootStarterWeb +} + +test { + useJUnitPlatform() +} + +application { + mainClassName = 'de.tum.cit.endpointanalysis.AnalysisOfEndpointConnections' +} + +run { + if (project.hasProperty('appArgs')) { + args = project.appArgs.split(' ') + } +} diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/AnalysisOfEndpointConnections.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/AnalysisOfEndpointConnections.java new file mode 100644 index 000000000000..b47b91d680d8 --- /dev/null +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/AnalysisOfEndpointConnections.java @@ -0,0 +1,83 @@ +package de.tum.cit.endpointanalysis; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.thoughtworks.qdox.JavaProjectBuilder; +import com.thoughtworks.qdox.model.JavaAnnotation; +import com.thoughtworks.qdox.model.JavaClass; +import com.thoughtworks.qdox.model.JavaMethod; + +public class AnalysisOfEndpointConnections { + + /** + * This is the entry point of the analysis of server sided endpoints. + * + * @param args List of files that should be analyzed regarding endpoints. + */ + public static void main(String[] args) { + if (args.length == 0) { + System.out.println("No files to analyze."); + return; + } + String[] filePaths = args[0].split("\n"); + String[] serverFiles = Arrays.stream(filePaths).map(filePath -> Paths.get("..", "..", filePath).toString()) + .filter(filePath -> Files.exists(Paths.get(filePath)) && filePath.endsWith(".java")).toArray(String[]::new); + analyzeServerEndpoints(serverFiles); + } + + private static void analyzeServerEndpoints(String[] filePaths) { + final Set httpMethodClasses = Set.of(GetMapping.class.getName(), PostMapping.class.getName(), PutMapping.class.getName(), DeleteMapping.class.getName(), + PatchMapping.class.getName(), RequestMapping.class.getName()); + + JavaProjectBuilder builder = new JavaProjectBuilder(); + for (String filePath : filePaths) { + builder.addSourceTree(new File(filePath)); + } + + Collection classes = builder.getClasses(); + for (JavaClass javaClass : classes) { + Optional requestMappingOptional = javaClass.getAnnotations().stream() + .filter(annotation -> annotation.getType().getFullyQualifiedName().equals(RequestMapping.class.getName())).findFirst(); + + boolean hasEndpoint = javaClass.getMethods().stream().flatMap(method -> method.getAnnotations().stream()) + .anyMatch(annotation -> httpMethodClasses.contains(annotation.getType().getFullyQualifiedName())); + + if (hasEndpoint) { + System.out.println("=================================================="); + System.out.println("Class: " + javaClass.getFullyQualifiedName()); + requestMappingOptional.ifPresent(annotation -> System.out.println("Class Request Mapping: " + annotation.getProperty("value"))); + System.out.println("=================================================="); + } + + for (JavaMethod method : javaClass.getMethods()) { + for (JavaAnnotation annotation : method.getAnnotations()) { + if (httpMethodClasses.contains(annotation.getType().getFullyQualifiedName())) { + System.out.println("Endpoint: " + method.getName()); + System.out.println( + annotation.getType().getFullyQualifiedName().equals(RequestMapping.class.getName()) ? "RequestMapping·method: " + annotation.getProperty("method") + : "HTTP method annotation: " + annotation.getType().getName()); + System.out.println("Path: " + annotation.getProperty("value")); + System.out.println("Line: " + method.getLineNumber()); + List annotations = method.getAnnotations().stream().filter(a -> !a.equals(annotation)).map(a -> a.getType().getName()).toList(); + System.out.println("Other annotations: " + annotations); + System.out.println("---------------------------------------------------"); + } + } + } + } + } +}